Bug 452317 - FeedConverter.js: QueryInterface should throw NS_ERROR_NO_INTERFACE...
[wine-gecko.git] / xpcom / base / nsTraceRefcntImpl.cpp
blob7e5fb68076dfbbc666cf342579137dd0e95179f9
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * L. David Baron <dbaron@dbaron.org>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsTraceRefcntImpl.h"
40 #include "nscore.h"
41 #include "nsISupports.h"
42 #include "nsVoidArray.h"
43 #include "prprf.h"
44 #include "prlog.h"
45 #include "plstr.h"
46 #include "prlink.h"
47 #include <stdlib.h>
48 #include "nsCOMPtr.h"
49 #include "nsCRT.h"
50 #include <math.h>
51 #include "nsStackWalk.h"
53 #ifdef HAVE_LIBDL
54 #include <dlfcn.h>
55 #endif
57 ////////////////////////////////////////////////////////////////////////////////
59 NS_COM void
60 NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
61 double *meanResult, double *stdDevResult)
63 double mean = 0.0, var = 0.0, stdDev = 0.0;
64 if (n > 0.0 && sumOfValues >= 0) {
65 mean = sumOfValues / n;
66 double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
67 if (temp < 0.0 || n <= 1)
68 var = 0.0;
69 else
70 var = temp / (n * (n - 1));
71 // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
72 stdDev = var != 0.0 ? sqrt(var) : 0.0;
74 *meanResult = mean;
75 *stdDevResult = stdDev;
78 ////////////////////////////////////////////////////////////////////////////////
80 #define NS_IMPL_REFCNT_LOGGING
82 #ifdef NS_IMPL_REFCNT_LOGGING
83 #include "plhash.h"
84 #include "prmem.h"
86 #include "prlock.h"
88 static PRLock* gTraceLock;
90 #define LOCK_TRACELOG() PR_Lock(gTraceLock)
91 #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
93 static PLHashTable* gBloatView;
94 static PLHashTable* gTypesToLog;
95 static PLHashTable* gObjectsToLog;
96 static PLHashTable* gSerialNumbers;
97 static PRInt32 gNextSerialNumber;
99 static PRBool gLogging;
100 static PRBool gLogToLeaky;
101 static PRBool gLogLeaksOnly;
103 static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
104 static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
106 #define BAD_TLS_INDEX ((PRUintn) -1)
108 // if gActivityTLS == BAD_TLS_INDEX, then we're
109 // unitialized... otherwise this points to a NSPR TLS thread index
110 // indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
111 // activity is ok, otherwise not!
112 static PRUintn gActivityTLS = BAD_TLS_INDEX;
114 static PRBool gInitialized;
115 static nsrefcnt gInitCount;
117 static FILE *gBloatLog = nsnull;
118 static FILE *gRefcntsLog = nsnull;
119 static FILE *gAllocLog = nsnull;
120 static FILE *gLeakyLog = nsnull;
121 static FILE *gCOMPtrLog = nsnull;
123 struct serialNumberRecord {
124 PRInt32 serialNumber;
125 PRInt32 refCount;
126 PRInt32 COMPtrCount;
129 struct nsTraceRefcntStats {
130 nsrefcnt mAddRefs;
131 nsrefcnt mReleases;
132 nsrefcnt mCreates;
133 nsrefcnt mDestroys;
134 double mRefsOutstandingTotal;
135 double mRefsOutstandingSquared;
136 double mObjsOutstandingTotal;
137 double mObjsOutstandingSquared;
140 // I hope to turn this on for everybody once we hit it a little less.
141 #define ASSERT_ACTIVITY_IS_LEGAL \
142 NS_WARN_IF_FALSE(gActivityTLS != BAD_TLS_INDEX && \
143 NS_PTR_TO_INT32(PR_GetThreadPrivate(gActivityTLS)) == 0, \
144 "XPCOM objects created/destroyed from static ctor/dtor");
146 // These functions are copied from nsprpub/lib/ds/plhash.c, with changes
147 // to the functions not called Default* to free the serialNumberRecord or
148 // the BloatEntry.
150 static void * PR_CALLBACK
151 DefaultAllocTable(void *pool, PRSize size)
153 return PR_MALLOC(size);
156 static void PR_CALLBACK
157 DefaultFreeTable(void *pool, void *item)
159 PR_Free(item);
162 static PLHashEntry * PR_CALLBACK
163 DefaultAllocEntry(void *pool, const void *key)
165 return PR_NEW(PLHashEntry);
168 static void PR_CALLBACK
169 SerialNumberFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
171 if (flag == HT_FREE_ENTRY) {
172 PR_Free(reinterpret_cast<serialNumberRecord*>(he->value));
173 PR_Free(he);
177 static void PR_CALLBACK
178 TypesToLogFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
180 if (flag == HT_FREE_ENTRY) {
181 nsCRT::free(const_cast<char*>
182 (reinterpret_cast<const char*>(he->key)));
183 PR_Free(he);
187 static const PLHashAllocOps serialNumberHashAllocOps = {
188 DefaultAllocTable, DefaultFreeTable,
189 DefaultAllocEntry, SerialNumberFreeEntry
192 static const PLHashAllocOps typesToLogHashAllocOps = {
193 DefaultAllocTable, DefaultFreeTable,
194 DefaultAllocEntry, TypesToLogFreeEntry
197 ////////////////////////////////////////////////////////////////////////////////
199 class BloatEntry {
200 public:
201 BloatEntry(const char* className, PRUint32 classSize)
202 : mClassSize(classSize) {
203 mClassName = PL_strdup(className);
204 Clear(&mNewStats);
205 Clear(&mAllStats);
206 mTotalLeaked = 0;
209 ~BloatEntry() {
210 PL_strfree(mClassName);
213 PRUint32 GetClassSize() { return (PRUint32)mClassSize; }
214 const char* GetClassName() { return mClassName; }
216 static void Clear(nsTraceRefcntStats* stats) {
217 stats->mAddRefs = 0;
218 stats->mReleases = 0;
219 stats->mCreates = 0;
220 stats->mDestroys = 0;
221 stats->mRefsOutstandingTotal = 0;
222 stats->mRefsOutstandingSquared = 0;
223 stats->mObjsOutstandingTotal = 0;
224 stats->mObjsOutstandingSquared = 0;
227 void Accumulate() {
228 mAllStats.mAddRefs += mNewStats.mAddRefs;
229 mAllStats.mReleases += mNewStats.mReleases;
230 mAllStats.mCreates += mNewStats.mCreates;
231 mAllStats.mDestroys += mNewStats.mDestroys;
232 mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal;
233 mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared;
234 mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal;
235 mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared;
236 Clear(&mNewStats);
239 void AddRef(nsrefcnt refcnt) {
240 mNewStats.mAddRefs++;
241 if (refcnt == 1) {
242 Ctor();
244 AccountRefs();
247 void Release(nsrefcnt refcnt) {
248 mNewStats.mReleases++;
249 if (refcnt == 0) {
250 Dtor();
252 AccountRefs();
255 void Ctor() {
256 mNewStats.mCreates++;
257 AccountObjs();
260 void Dtor() {
261 mNewStats.mDestroys++;
262 AccountObjs();
265 void AccountRefs() {
266 PRInt32 cnt = (mNewStats.mAddRefs - mNewStats.mReleases);
267 mNewStats.mRefsOutstandingTotal += cnt;
268 mNewStats.mRefsOutstandingSquared += cnt * cnt;
271 void AccountObjs() {
272 PRInt32 cnt = (mNewStats.mCreates - mNewStats.mDestroys);
273 mNewStats.mObjsOutstandingTotal += cnt;
274 mNewStats.mObjsOutstandingSquared += cnt * cnt;
277 static PRIntn PR_CALLBACK DumpEntry(PLHashEntry *he, PRIntn i, void *arg) {
278 BloatEntry* entry = (BloatEntry*)he->value;
279 if (entry) {
280 entry->Accumulate();
281 static_cast<nsVoidArray*>(arg)->AppendElement(entry);
283 return HT_ENUMERATE_NEXT;
286 static PRIntn PR_CALLBACK TotalEntries(PLHashEntry *he, PRIntn i, void *arg) {
287 BloatEntry* entry = (BloatEntry*)he->value;
288 if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
289 entry->Total((BloatEntry*)arg);
291 return HT_ENUMERATE_NEXT;
294 void Total(BloatEntry* total) {
295 total->mAllStats.mAddRefs += mNewStats.mAddRefs + mAllStats.mAddRefs;
296 total->mAllStats.mReleases += mNewStats.mReleases + mAllStats.mReleases;
297 total->mAllStats.mCreates += mNewStats.mCreates + mAllStats.mCreates;
298 total->mAllStats.mDestroys += mNewStats.mDestroys + mAllStats.mDestroys;
299 total->mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal + mAllStats.mRefsOutstandingTotal;
300 total->mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared + mAllStats.mRefsOutstandingSquared;
301 total->mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal + mAllStats.mObjsOutstandingTotal;
302 total->mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared + mAllStats.mObjsOutstandingSquared;
303 PRInt32 count = (mNewStats.mCreates + mAllStats.mCreates);
304 total->mClassSize += mClassSize * count; // adjust for average in DumpTotal
305 total->mTotalLeaked += (PRInt32)(mClassSize *
306 ((mNewStats.mCreates + mAllStats.mCreates)
307 -(mNewStats.mDestroys + mAllStats.mDestroys)));
310 void DumpTotal(FILE* out) {
311 mClassSize /= mAllStats.mCreates;
312 Dump(-1, out, nsTraceRefcntImpl::ALL_STATS);
315 static PRBool HaveLeaks(nsTraceRefcntStats* stats) {
316 return ((stats->mAddRefs != stats->mReleases) ||
317 (stats->mCreates != stats->mDestroys));
320 PRBool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcntImpl::StatisticsType type) {
321 fprintf(out, "\n== BloatView: %s\n", msg);
323 nsTraceRefcntStats& stats =
324 (type == nsTraceRefcntImpl::NEW_STATS) ? mNewStats : mAllStats;
325 if (gLogLeaksOnly && !HaveLeaks(&stats))
326 return PR_FALSE;
328 fprintf(out,
329 "\n" \
330 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \
331 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
333 this->DumpTotal(out);
335 return PR_TRUE;
338 void Dump(PRIntn i, FILE* out, nsTraceRefcntImpl::StatisticsType type) {
339 nsTraceRefcntStats* stats = (type == nsTraceRefcntImpl::NEW_STATS) ? &mNewStats : &mAllStats;
340 if (gLogLeaksOnly && !HaveLeaks(stats)) {
341 return;
344 double meanRefs, stddevRefs;
345 NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases,
346 stats->mRefsOutstandingTotal,
347 stats->mRefsOutstandingSquared,
348 &meanRefs, &stddevRefs);
350 double meanObjs, stddevObjs;
351 NS_MeanAndStdDev(stats->mCreates + stats->mDestroys,
352 stats->mObjsOutstandingTotal,
353 stats->mObjsOutstandingSquared,
354 &meanObjs, &stddevObjs);
356 if ((stats->mAddRefs - stats->mReleases) != 0 ||
357 stats->mAddRefs != 0 ||
358 meanRefs != 0 ||
359 stddevRefs != 0 ||
360 (stats->mCreates - stats->mDestroys) != 0 ||
361 stats->mCreates != 0 ||
362 meanObjs != 0 ||
363 stddevObjs != 0) {
364 fprintf(out, "%4d %-40.40s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
365 i+1, mClassName,
366 (PRInt32)mClassSize,
367 (nsCRT::strcmp(mClassName, "TOTAL"))
368 ?(PRInt32)((stats->mCreates - stats->mDestroys) * mClassSize)
369 :mTotalLeaked,
370 stats->mCreates,
371 (stats->mCreates - stats->mDestroys),
372 meanObjs,
373 stddevObjs,
374 stats->mAddRefs,
375 (stats->mAddRefs - stats->mReleases),
376 meanRefs,
377 stddevRefs);
381 protected:
382 char* mClassName;
383 double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat
384 PRInt32 mTotalLeaked; // used only for TOTAL entry
385 nsTraceRefcntStats mNewStats;
386 nsTraceRefcntStats mAllStats;
389 static void PR_CALLBACK
390 BloatViewFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
392 if (flag == HT_FREE_ENTRY) {
393 BloatEntry* entry = reinterpret_cast<BloatEntry*>(he->value);
394 delete entry;
395 PR_Free(he);
399 const static PLHashAllocOps bloatViewHashAllocOps = {
400 DefaultAllocTable, DefaultFreeTable,
401 DefaultAllocEntry, BloatViewFreeEntry
404 static void
405 RecreateBloatView()
407 gBloatView = PL_NewHashTable(256,
408 PL_HashString,
409 PL_CompareStrings,
410 PL_CompareValues,
411 &bloatViewHashAllocOps, NULL);
414 static BloatEntry*
415 GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize)
417 if (!gBloatView) {
418 RecreateBloatView();
420 BloatEntry* entry = NULL;
421 if (gBloatView) {
422 entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
423 if (entry == NULL && aInstanceSize > 0) {
425 entry = new BloatEntry(aTypeName, aInstanceSize);
426 PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
427 if (e == NULL) {
428 delete entry;
429 entry = NULL;
431 } else {
432 NS_ASSERTION(aInstanceSize == 0 ||
433 entry->GetClassSize() == aInstanceSize,
434 "bad size recorded");
437 return entry;
440 static PRIntn PR_CALLBACK DumpSerialNumbers(PLHashEntry* aHashEntry, PRIntn aIndex, void* aClosure)
442 serialNumberRecord* record = reinterpret_cast<serialNumberRecord *>(aHashEntry->value);
443 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
444 fprintf((FILE*) aClosure, "%d @%p (%d references; %d from COMPtrs)\n",
445 record->serialNumber,
446 NS_INT32_TO_PTR(aHashEntry->key),
447 record->refCount,
448 record->COMPtrCount);
449 #else
450 fprintf((FILE*) aClosure, "%d @%p (%d references)\n",
451 record->serialNumber,
452 NS_INT32_TO_PTR(aHashEntry->key),
453 record->refCount);
454 #endif
455 return HT_ENUMERATE_NEXT;
459 #endif /* NS_IMPL_REFCNT_LOGGING */
461 NS_COM nsresult
462 nsTraceRefcntImpl::DumpStatistics(StatisticsType type, FILE* out)
464 #ifdef NS_IMPL_REFCNT_LOGGING
465 if (gBloatLog == nsnull || gBloatView == nsnull) {
466 return NS_ERROR_FAILURE;
468 if (out == nsnull) {
469 out = gBloatLog;
472 LOCK_TRACELOG();
474 PRBool wasLogging = gLogging;
475 gLogging = PR_FALSE; // turn off logging for this method
477 BloatEntry total("TOTAL", 0);
478 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
479 const char* msg;
480 if (type == NEW_STATS) {
481 if (gLogLeaksOnly)
482 msg = "NEW (incremental) LEAK STATISTICS";
483 else
484 msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
486 else {
487 if (gLogLeaksOnly)
488 msg = "ALL (cumulative) LEAK STATISTICS";
489 else
490 msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
492 const PRBool leaked = total.PrintDumpHeader(out, msg, type);
494 nsVoidArray entries;
495 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
496 const PRInt32 count = entries.Count();
498 if (!gLogLeaksOnly || leaked) {
499 // Sort the entries alphabetically by classname.
500 PRInt32 i, j;
501 for (i = count - 1; i >= 1; --i) {
502 for (j = i - 1; j >= 0; --j) {
503 BloatEntry* left = static_cast<BloatEntry*>(entries[i]);
504 BloatEntry* right = static_cast<BloatEntry*>(entries[j]);
506 if (PL_strcmp(left->GetClassName(), right->GetClassName()) < 0) {
507 entries.ReplaceElementAt(right, i);
508 entries.ReplaceElementAt(left, j);
513 // Enumerate from back-to-front, so things come out in alpha order
514 for (i = 0; i < count; ++i) {
515 BloatEntry* entry = static_cast<BloatEntry*>(entries[i]);
516 entry->Dump(i, out, type);
519 fprintf(out, "\n");
522 fprintf(out, "nsTraceRefcntImpl::DumpStatistics: %d entries\n", count);
524 if (gSerialNumbers) {
525 fprintf(out, "\nSerial Numbers of Leaked Objects:\n");
526 PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out);
529 gLogging = wasLogging;
530 UNLOCK_TRACELOG();
531 #endif
533 return NS_OK;
536 NS_COM void
537 nsTraceRefcntImpl::ResetStatistics()
539 #ifdef NS_IMPL_REFCNT_LOGGING
540 LOCK_TRACELOG();
541 if (gBloatView) {
542 PL_HashTableDestroy(gBloatView);
543 gBloatView = nsnull;
545 UNLOCK_TRACELOG();
546 #endif
549 #ifdef NS_IMPL_REFCNT_LOGGING
550 static PRBool LogThisType(const char* aTypeName)
552 void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
553 return nsnull != he;
556 static PRInt32 GetSerialNumber(void* aPtr, PRBool aCreate)
558 #ifdef GC_LEAK_DETECTOR
559 // need to disguise this pointer, so the table won't keep the object alive.
560 aPtr = (void*) ~PLHashNumber(aPtr);
561 #endif
562 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
563 if (hep && *hep) {
564 return PRInt32((reinterpret_cast<serialNumberRecord*>((*hep)->value))->serialNumber);
566 else if (aCreate) {
567 serialNumberRecord *record = PR_NEW(serialNumberRecord);
568 record->serialNumber = ++gNextSerialNumber;
569 record->refCount = 0;
570 record->COMPtrCount = 0;
571 PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, reinterpret_cast<void*>(record));
572 return gNextSerialNumber;
574 else {
575 return 0;
579 static PRInt32* GetRefCount(void* aPtr)
581 #ifdef GC_LEAK_DETECTOR
582 // need to disguise this pointer, so the table won't keep the object alive.
583 aPtr = (void*) ~PLHashNumber(aPtr);
584 #endif
585 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
586 if (hep && *hep) {
587 return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->refCount);
588 } else {
589 return nsnull;
593 static PRInt32* GetCOMPtrCount(void* aPtr)
595 #ifdef GC_LEAK_DETECTOR
596 // need to disguise this pointer, so the table won't keep the object alive.
597 aPtr = (void*) ~PLHashNumber(aPtr);
598 #endif
599 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
600 if (hep && *hep) {
601 return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->COMPtrCount);
602 } else {
603 return nsnull;
607 static void RecycleSerialNumberPtr(void* aPtr)
609 #ifdef GC_LEAK_DETECTOR
610 // need to disguise this pointer, so the table won't keep the object alive.
611 aPtr = (void*) ~PLHashNumber(aPtr);
612 #endif
613 PL_HashTableRemove(gSerialNumbers, aPtr);
616 static PRBool LogThisObj(PRInt32 aSerialNumber)
618 return nsnull != PL_HashTableLookup(gObjectsToLog, (const void*)(aSerialNumber));
621 static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
623 const char* value = getenv(envVar);
624 if (value) {
625 if (nsCRT::strcmp(value, "1") == 0) {
626 *result = stdout;
627 fprintf(stdout, "### %s defined -- logging %s to stdout\n",
628 envVar, msg);
629 return PR_TRUE;
631 else if (nsCRT::strcmp(value, "2") == 0) {
632 *result = stderr;
633 fprintf(stdout, "### %s defined -- logging %s to stderr\n",
634 envVar, msg);
635 return PR_TRUE;
637 else {
638 FILE *stream = ::fopen(value, "w");
639 if (stream != NULL) {
640 *result = stream;
641 fprintf(stdout, "### %s defined -- logging %s to %s\n",
642 envVar, msg, value);
643 return PR_TRUE;
645 else {
646 fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
647 envVar, msg, value);
648 return PR_FALSE;
652 return PR_FALSE;
656 static PLHashNumber PR_CALLBACK HashNumber(const void* aKey)
658 return PLHashNumber(NS_PTR_TO_INT32(aKey));
661 static void InitTraceLog(void)
663 if (gInitialized) return;
664 gInitialized = PR_TRUE;
666 PRBool defined;
667 defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
668 if (!defined)
669 gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
670 if (defined || gLogLeaksOnly) {
671 RecreateBloatView();
672 if (!gBloatView) {
673 NS_WARNING("out of memory");
674 gBloatLog = nsnull;
675 gLogLeaksOnly = PR_FALSE;
679 (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
681 (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
683 defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
684 if (defined) {
685 gLogToLeaky = PR_TRUE;
686 PRFuncPtr p = nsnull, q = nsnull;
687 #ifdef HAVE_LIBDL
689 PRLibrary *lib = nsnull;
690 p = PR_FindFunctionSymbolAndLibrary("__log_addref", &lib);
691 if (lib) {
692 PR_UnloadLibrary(lib);
693 lib = nsnull;
695 q = PR_FindFunctionSymbolAndLibrary("__log_release", &lib);
696 if (lib) {
697 PR_UnloadLibrary(lib);
700 #endif
701 if (p && q) {
702 leakyLogAddRef = (void (*)(void*,int,int)) p;
703 leakyLogRelease = (void (*)(void*,int,int)) q;
705 else {
706 gLogToLeaky = PR_FALSE;
707 fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
708 fflush(stdout);
712 const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
714 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
715 if (classes) {
716 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
717 } else {
718 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
719 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
722 #else
723 const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
724 if (comptr_log) {
725 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
727 #endif
729 if (classes) {
730 // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
731 // as a list of class names to track
732 gTypesToLog = PL_NewHashTable(256,
733 PL_HashString,
734 PL_CompareStrings,
735 PL_CompareValues,
736 &typesToLogHashAllocOps, NULL);
737 if (!gTypesToLog) {
738 NS_WARNING("out of memory");
739 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
741 else {
742 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
743 const char* cp = classes;
744 for (;;) {
745 char* cm = (char*) strchr(cp, ',');
746 if (cm) {
747 *cm = '\0';
749 PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
750 fprintf(stdout, "%s ", cp);
751 if (!cm) break;
752 *cm = ',';
753 cp = cm + 1;
755 fprintf(stdout, "\n");
758 gSerialNumbers = PL_NewHashTable(256,
759 HashNumber,
760 PL_CompareValues,
761 PL_CompareValues,
762 &serialNumberHashAllocOps, NULL);
767 const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
768 if (objects) {
769 gObjectsToLog = PL_NewHashTable(256,
770 HashNumber,
771 PL_CompareValues,
772 PL_CompareValues,
773 NULL, NULL);
775 if (!gObjectsToLog) {
776 NS_WARNING("out of memory");
777 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
779 else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
780 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
782 else {
783 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
784 const char* cp = objects;
785 for (;;) {
786 char* cm = (char*) strchr(cp, ',');
787 if (cm) {
788 *cm = '\0';
790 PRInt32 top = 0;
791 PRInt32 bottom = 0;
792 while (*cp) {
793 if (*cp == '-') {
794 bottom = top;
795 top = 0;
796 ++cp;
798 top *= 10;
799 top += *cp - '0';
800 ++cp;
802 if (!bottom) {
803 bottom = top;
805 for(PRInt32 serialno = bottom; serialno <= top; serialno++) {
806 PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
807 fprintf(stdout, "%d ", serialno);
809 if (!cm) break;
810 *cm = ',';
811 cp = cm + 1;
813 fprintf(stdout, "\n");
818 if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
819 gLogging = PR_TRUE;
822 gTraceLock = PR_NewLock();
825 #endif
827 extern "C" {
829 PR_STATIC_CALLBACK(void) PrintStackFrame(void *aPC, void *aClosure)
831 FILE *stream = (FILE*)aClosure;
832 nsCodeAddressDetails details;
833 char buf[1024];
835 NS_DescribeCodeAddress(aPC, &details);
836 NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
837 fprintf(stream, buf);
842 NS_COM void
843 nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
845 NS_StackWalk(PrintStackFrame, 2, aStream);
848 //----------------------------------------------------------------------
850 // This thing is exported by libstdc++
851 // Yes, this is a gcc only hack
852 #if defined(MOZ_DEMANGLE_SYMBOLS)
853 #include <cxxabi.h>
854 #include <stdlib.h> // for free()
855 #endif // MOZ_DEMANGLE_SYMBOLS
857 NS_COM void
858 nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol,
859 char * aBuffer,
860 int aBufLen)
862 NS_ASSERTION(nsnull != aSymbol,"null symbol");
863 NS_ASSERTION(nsnull != aBuffer,"null buffer");
864 NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
866 aBuffer[0] = '\0';
868 #if defined(MOZ_DEMANGLE_SYMBOLS)
869 /* See demangle.h in the gcc source for the voodoo */
870 char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
872 if (demangled)
874 strncpy(aBuffer,demangled,aBufLen);
875 free(demangled);
877 #endif // MOZ_DEMANGLE_SYMBOLS
881 //----------------------------------------------------------------------
883 EXPORT_XPCOM_API(void)
884 NS_LogInit()
886 #ifdef NS_IMPL_REFCNT_LOGGING
887 if (++gInitCount)
888 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
889 #endif
892 EXPORT_XPCOM_API(void)
893 NS_LogTerm()
895 NS_ASSERTION(gInitCount > 0,
896 "NS_LogTerm without matching NS_LogInit");
898 if (--gInitCount == 0) {
899 if (gInitialized) {
900 nsTraceRefcntImpl::DumpStatistics();
901 nsTraceRefcntImpl::ResetStatistics();
903 nsTraceRefcntImpl::Shutdown();
904 #ifdef NS_IMPL_REFCNT_LOGGING
905 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
906 gActivityTLS = BAD_TLS_INDEX;
907 #endif
911 EXPORT_XPCOM_API(void)
912 NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
913 const char* aClazz, PRUint32 classSize)
915 #ifdef NS_IMPL_REFCNT_LOGGING
916 ASSERT_ACTIVITY_IS_LEGAL;
917 if (!gInitialized)
918 InitTraceLog();
919 if (gLogging) {
920 LOCK_TRACELOG();
922 if (gBloatLog) {
923 BloatEntry* entry = GetBloatEntry(aClazz, classSize);
924 if (entry) {
925 entry->AddRef(aRefcnt);
929 // Here's the case where neither NS_NEWXPCOM nor MOZ_COUNT_CTOR were used,
930 // yet we still want to see creation information:
932 PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
933 PRInt32 serialno = 0;
934 if (gSerialNumbers && loggingThisType) {
935 serialno = GetSerialNumber(aPtr, aRefcnt == 1);
936 NS_ASSERTION(serialno != 0,
937 "Serial number requested for unrecognized pointer! "
938 "Are you memmoving a refcounted object?");
939 PRInt32* count = GetRefCount(aPtr);
940 if(count)
941 (*count)++;
945 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
946 if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
947 fprintf(gAllocLog, "\n<%s> 0x%08X %d Create\n",
948 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
949 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
952 if (gRefcntsLog && loggingThisType && loggingThisObject) {
953 if (gLogToLeaky) {
954 (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
956 else {
957 // Can't use PR_LOG(), b/c it truncates the line
958 fprintf(gRefcntsLog,
959 "\n<%s> 0x%08X %d AddRef %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
960 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog);
961 fflush(gRefcntsLog);
964 UNLOCK_TRACELOG();
966 #endif
969 EXPORT_XPCOM_API(void)
970 NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClazz)
972 #ifdef NS_IMPL_REFCNT_LOGGING
973 ASSERT_ACTIVITY_IS_LEGAL;
974 if (!gInitialized)
975 InitTraceLog();
976 if (gLogging) {
977 LOCK_TRACELOG();
979 if (gBloatLog) {
980 BloatEntry* entry = GetBloatEntry(aClazz, 0);
981 if (entry) {
982 entry->Release(aRefcnt);
986 PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
987 PRInt32 serialno = 0;
988 if (gSerialNumbers && loggingThisType) {
989 serialno = GetSerialNumber(aPtr, PR_FALSE);
990 NS_ASSERTION(serialno != 0,
991 "Serial number requested for unrecognized pointer! "
992 "Are you memmoving a refcounted object?");
993 PRInt32* count = GetRefCount(aPtr);
994 if(count)
995 (*count)--;
999 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1000 if (gRefcntsLog && loggingThisType && loggingThisObject) {
1001 if (gLogToLeaky) {
1002 (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
1004 else {
1005 // Can't use PR_LOG(), b/c it truncates the line
1006 fprintf(gRefcntsLog,
1007 "\n<%s> 0x%08X %d Release %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
1008 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog);
1009 fflush(gRefcntsLog);
1013 // Here's the case where neither NS_DELETEXPCOM nor MOZ_COUNT_DTOR were used,
1014 // yet we still want to see deletion information:
1016 if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
1017 fprintf(gAllocLog,
1018 "\n<%s> 0x%08X %d Destroy\n",
1019 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
1020 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1023 if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
1024 RecycleSerialNumberPtr(aPtr);
1027 UNLOCK_TRACELOG();
1029 #endif
1032 EXPORT_XPCOM_API(void)
1033 NS_LogCtor(void* aPtr, const char* aType, PRUint32 aInstanceSize)
1035 #ifdef NS_IMPL_REFCNT_LOGGING
1036 ASSERT_ACTIVITY_IS_LEGAL;
1037 if (!gInitialized)
1038 InitTraceLog();
1040 if (gLogging) {
1041 LOCK_TRACELOG();
1043 if (gBloatLog) {
1044 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1045 if (entry) {
1046 entry->Ctor();
1050 PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
1051 PRInt32 serialno = 0;
1052 if (gSerialNumbers && loggingThisType) {
1053 serialno = GetSerialNumber(aPtr, PR_TRUE);
1056 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1057 if (gAllocLog && loggingThisType && loggingThisObject) {
1058 fprintf(gAllocLog, "\n<%s> 0x%08X %d Ctor (%d)\n",
1059 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1060 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1063 UNLOCK_TRACELOG();
1065 #endif
1069 EXPORT_XPCOM_API(void)
1070 NS_LogDtor(void* aPtr, const char* aType, PRUint32 aInstanceSize)
1072 #ifdef NS_IMPL_REFCNT_LOGGING
1073 ASSERT_ACTIVITY_IS_LEGAL;
1074 if (!gInitialized)
1075 InitTraceLog();
1077 if (gLogging) {
1078 LOCK_TRACELOG();
1080 if (gBloatLog) {
1081 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1082 if (entry) {
1083 entry->Dtor();
1087 PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
1088 PRInt32 serialno = 0;
1089 if (gSerialNumbers && loggingThisType) {
1090 serialno = GetSerialNumber(aPtr, PR_FALSE);
1091 RecycleSerialNumberPtr(aPtr);
1094 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1096 // (If we're on a losing architecture, don't do this because we'll be
1097 // using LogDeleteXPCOM instead to get file and line numbers.)
1098 if (gAllocLog && loggingThisType && loggingThisObject) {
1099 fprintf(gAllocLog, "\n<%s> 0x%08X %d Dtor (%d)\n",
1100 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1101 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1104 UNLOCK_TRACELOG();
1106 #endif
1110 EXPORT_XPCOM_API(void)
1111 NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
1113 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1114 // Get the most-derived object.
1115 void *object = dynamic_cast<void *>(aObject);
1117 // This is a very indirect way of finding out what the class is
1118 // of the object being logged. If we're logging a specific type,
1119 // then
1120 if (!gTypesToLog || !gSerialNumbers) {
1121 return;
1123 PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
1124 if (serialno == 0) {
1125 return;
1128 if (!gInitialized)
1129 InitTraceLog();
1130 if (gLogging) {
1131 LOCK_TRACELOG();
1133 PRInt32* count = GetCOMPtrCount(object);
1134 if(count)
1135 (*count)++;
1137 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1139 if (gCOMPtrLog && loggingThisObject) {
1140 fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrAddRef %d 0x%08X\n",
1141 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1142 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog);
1145 UNLOCK_TRACELOG();
1147 #endif
1151 EXPORT_XPCOM_API(void)
1152 NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
1154 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1155 // Get the most-derived object.
1156 void *object = dynamic_cast<void *>(aObject);
1158 // This is a very indirect way of finding out what the class is
1159 // of the object being logged. If we're logging a specific type,
1160 // then
1161 if (!gTypesToLog || !gSerialNumbers) {
1162 return;
1164 PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
1165 if (serialno == 0) {
1166 return;
1169 if (!gInitialized)
1170 InitTraceLog();
1171 if (gLogging) {
1172 LOCK_TRACELOG();
1174 PRInt32* count = GetCOMPtrCount(object);
1175 if(count)
1176 (*count)--;
1178 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1180 if (gCOMPtrLog && loggingThisObject) {
1181 fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrRelease %d 0x%08X\n",
1182 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1183 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog);
1186 UNLOCK_TRACELOG();
1188 #endif
1191 NS_COM void
1192 nsTraceRefcntImpl::Startup()
1196 NS_COM void
1197 nsTraceRefcntImpl::Shutdown()
1199 #ifdef NS_IMPL_REFCNT_LOGGING
1201 if (gBloatView) {
1202 PL_HashTableDestroy(gBloatView);
1203 gBloatView = nsnull;
1205 if (gTypesToLog) {
1206 PL_HashTableDestroy(gTypesToLog);
1207 gTypesToLog = nsnull;
1209 if (gObjectsToLog) {
1210 PL_HashTableDestroy(gObjectsToLog);
1211 gObjectsToLog = nsnull;
1213 if (gSerialNumbers) {
1214 PL_HashTableDestroy(gSerialNumbers);
1215 gSerialNumbers = nsnull;
1217 #endif
1220 NS_COM void
1221 nsTraceRefcntImpl::SetActivityIsLegal(PRBool aLegal)
1223 #ifdef NS_IMPL_REFCNT_LOGGING
1224 if (gActivityTLS == BAD_TLS_INDEX)
1225 PR_NewThreadPrivateIndex(&gActivityTLS, nsnull);
1227 PR_SetThreadPrivate(gActivityTLS, NS_INT32_TO_PTR(!aLegal));
1228 #endif
1231 NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl, nsITraceRefcnt)
1233 NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::AddRef(void)
1235 return 2;
1238 NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::Release(void)
1240 return 1;
1243 NS_IMETHODIMP
1244 nsTraceRefcntImpl::LogAddRef(void *aPtr, nsrefcnt aNewRefcnt,
1245 const char *aTypeName, PRUint32 aSize)
1247 NS_LogAddRef(aPtr, aNewRefcnt, aTypeName, aSize);
1248 return NS_OK;
1251 NS_IMETHODIMP
1252 nsTraceRefcntImpl::LogRelease(void *aPtr, nsrefcnt aNewRefcnt,
1253 const char *aTypeName)
1255 NS_LogRelease(aPtr, aNewRefcnt, aTypeName);
1256 return NS_OK;
1259 NS_IMETHODIMP
1260 nsTraceRefcntImpl::LogCtor(void *aPtr, const char *aTypeName, PRUint32 aSize)
1262 NS_LogCtor(aPtr, aTypeName, aSize);
1263 return NS_OK;
1266 NS_IMETHODIMP
1267 nsTraceRefcntImpl::LogDtor(void *aPtr, const char *aTypeName, PRUint32 aSize)
1269 NS_LogDtor(aPtr, aTypeName, aSize);
1270 return NS_OK;
1273 NS_IMETHODIMP
1274 nsTraceRefcntImpl::LogAddCOMPtr(void *aCOMPtr, nsISupports* aObject)
1276 NS_LogCOMPtrAddRef(aCOMPtr, aObject);
1277 return NS_OK;
1280 NS_IMETHODIMP
1281 nsTraceRefcntImpl::LogReleaseCOMPtr(void *aCOMPtr, nsISupports* aObject)
1283 NS_LogCOMPtrRelease(aCOMPtr, aObject);
1284 return NS_OK;
1287 static const nsTraceRefcntImpl kTraceRefcntImpl;
1289 NS_METHOD
1290 nsTraceRefcntImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
1292 return const_cast<nsTraceRefcntImpl*>(&kTraceRefcntImpl)->
1293 QueryInterface(aIID, aInstancePtr);