Bug 449474, Fx3.0.2 updates should force modules/distribution.js, and use the version...
[mozilla-1.9.git] / xpcom / base / nsTraceRefcntImpl.cpp
blob9bcd850bbe4d230bd82ddedb3b3fc3cf2734e86b
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 nsresult DumpTotal(PRUint32 nClasses, FILE* out) {
311 mClassSize /= mAllStats.mCreates;
312 return Dump(-1, out, nsTraceRefcntImpl::ALL_STATS);
315 static PRBool HaveLeaks(nsTraceRefcntStats* stats) {
316 return ((stats->mAddRefs != stats->mReleases) ||
317 (stats->mCreates != stats->mDestroys));
320 static nsresult PrintDumpHeader(FILE* out, const char* msg) {
321 fprintf(out, "\n== BloatView: %s\n\n", msg);
322 fprintf(out,
323 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n");
324 fprintf(out,
325 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
326 return NS_OK;
329 nsresult Dump(PRIntn i, FILE* out, nsTraceRefcntImpl::StatisticsType type) {
330 nsTraceRefcntStats* stats = (type == nsTraceRefcntImpl::NEW_STATS) ? &mNewStats : &mAllStats;
331 if (gLogLeaksOnly && !HaveLeaks(stats)) {
332 return NS_OK;
335 double meanRefs, stddevRefs;
336 NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases,
337 stats->mRefsOutstandingTotal,
338 stats->mRefsOutstandingSquared,
339 &meanRefs, &stddevRefs);
341 double meanObjs, stddevObjs;
342 NS_MeanAndStdDev(stats->mCreates + stats->mDestroys,
343 stats->mObjsOutstandingTotal,
344 stats->mObjsOutstandingSquared,
345 &meanObjs, &stddevObjs);
347 if ((stats->mAddRefs - stats->mReleases) != 0 ||
348 stats->mAddRefs != 0 ||
349 meanRefs != 0 ||
350 stddevRefs != 0 ||
351 (stats->mCreates - stats->mDestroys) != 0 ||
352 stats->mCreates != 0 ||
353 meanObjs != 0 ||
354 stddevObjs != 0) {
355 fprintf(out, "%4d %-40.40s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
356 i+1, mClassName,
357 (PRInt32)mClassSize,
358 (nsCRT::strcmp(mClassName, "TOTAL"))
359 ?(PRInt32)((stats->mCreates - stats->mDestroys) * mClassSize)
360 :mTotalLeaked,
361 stats->mCreates,
362 (stats->mCreates - stats->mDestroys),
363 meanObjs,
364 stddevObjs,
365 stats->mAddRefs,
366 (stats->mAddRefs - stats->mReleases),
367 meanRefs,
368 stddevRefs);
370 return NS_OK;
373 protected:
374 char* mClassName;
375 double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat
376 PRInt32 mTotalLeaked; // used only for TOTAL entry
377 nsTraceRefcntStats mNewStats;
378 nsTraceRefcntStats mAllStats;
381 static void PR_CALLBACK
382 BloatViewFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
384 if (flag == HT_FREE_ENTRY) {
385 BloatEntry* entry = reinterpret_cast<BloatEntry*>(he->value);
386 delete entry;
387 PR_Free(he);
391 const static PLHashAllocOps bloatViewHashAllocOps = {
392 DefaultAllocTable, DefaultFreeTable,
393 DefaultAllocEntry, BloatViewFreeEntry
396 static void
397 RecreateBloatView()
399 gBloatView = PL_NewHashTable(256,
400 PL_HashString,
401 PL_CompareStrings,
402 PL_CompareValues,
403 &bloatViewHashAllocOps, NULL);
406 static BloatEntry*
407 GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize)
409 if (!gBloatView) {
410 RecreateBloatView();
412 BloatEntry* entry = NULL;
413 if (gBloatView) {
414 entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
415 if (entry == NULL && aInstanceSize > 0) {
417 entry = new BloatEntry(aTypeName, aInstanceSize);
418 PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
419 if (e == NULL) {
420 delete entry;
421 entry = NULL;
423 } else {
424 NS_ASSERTION(aInstanceSize == 0 ||
425 entry->GetClassSize() == aInstanceSize,
426 "bad size recorded");
429 return entry;
432 static PRIntn PR_CALLBACK DumpSerialNumbers(PLHashEntry* aHashEntry, PRIntn aIndex, void* aClosure)
434 serialNumberRecord* record = reinterpret_cast<serialNumberRecord *>(aHashEntry->value);
435 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
436 fprintf((FILE*) aClosure, "%d @%p (%d references; %d from COMPtrs)\n",
437 record->serialNumber,
438 NS_INT32_TO_PTR(aHashEntry->key),
439 record->refCount,
440 record->COMPtrCount);
441 #else
442 fprintf((FILE*) aClosure, "%d @%p (%d references)\n",
443 record->serialNumber,
444 NS_INT32_TO_PTR(aHashEntry->key),
445 record->refCount);
446 #endif
447 return HT_ENUMERATE_NEXT;
451 #endif /* NS_IMPL_REFCNT_LOGGING */
453 NS_COM nsresult
454 nsTraceRefcntImpl::DumpStatistics(StatisticsType type, FILE* out)
456 nsresult rv = NS_OK;
457 #ifdef NS_IMPL_REFCNT_LOGGING
458 if (gBloatLog == nsnull || gBloatView == nsnull) {
459 return NS_ERROR_FAILURE;
461 if (out == nsnull) {
462 out = gBloatLog;
465 LOCK_TRACELOG();
467 PRBool wasLogging = gLogging;
468 gLogging = PR_FALSE; // turn off logging for this method
470 const char* msg;
471 if (type == NEW_STATS) {
472 if (gLogLeaksOnly)
473 msg = "NEW (incremental) LEAK STATISTICS";
474 else
475 msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
477 else {
478 if (gLogLeaksOnly)
479 msg = "ALL (cumulative) LEAK STATISTICS";
480 else
481 msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
483 rv = BloatEntry::PrintDumpHeader(out, msg);
484 if (NS_FAILED(rv)) goto done;
487 BloatEntry total("TOTAL", 0);
488 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
489 total.DumpTotal(gBloatView->nentries, out);
491 nsVoidArray entries;
492 PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
494 fprintf(stdout, "nsTraceRefcntImpl::DumpStatistics: %d entries\n",
495 entries.Count());
497 // Sort the entries alphabetically by classname.
498 PRInt32 i, j;
499 for (i = entries.Count() - 1; i >= 1; --i) {
500 for (j = i - 1; j >= 0; --j) {
501 BloatEntry* left = static_cast<BloatEntry*>(entries[i]);
502 BloatEntry* right = static_cast<BloatEntry*>(entries[j]);
504 if (PL_strcmp(left->GetClassName(), right->GetClassName()) < 0) {
505 entries.ReplaceElementAt(right, i);
506 entries.ReplaceElementAt(left, j);
511 // Enumerate from back-to-front, so things come out in alpha order
512 for (i = 0; i < entries.Count(); ++i) {
513 BloatEntry* entry = static_cast<BloatEntry*>(entries[i]);
514 entry->Dump(i, out, type);
518 if (gSerialNumbers) {
519 fprintf(out, "\n\nSerial Numbers of Leaked Objects:\n");
520 PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out);
523 done:
524 gLogging = wasLogging;
525 UNLOCK_TRACELOG();
526 #endif
527 return rv;
530 NS_COM void
531 nsTraceRefcntImpl::ResetStatistics()
533 #ifdef NS_IMPL_REFCNT_LOGGING
534 LOCK_TRACELOG();
535 if (gBloatView) {
536 PL_HashTableDestroy(gBloatView);
537 gBloatView = nsnull;
539 UNLOCK_TRACELOG();
540 #endif
543 #ifdef NS_IMPL_REFCNT_LOGGING
544 static PRBool LogThisType(const char* aTypeName)
546 void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
547 return nsnull != he;
550 static PRInt32 GetSerialNumber(void* aPtr, PRBool aCreate)
552 #ifdef GC_LEAK_DETECTOR
553 // need to disguise this pointer, so the table won't keep the object alive.
554 aPtr = (void*) ~PLHashNumber(aPtr);
555 #endif
556 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
557 if (hep && *hep) {
558 return PRInt32((reinterpret_cast<serialNumberRecord*>((*hep)->value))->serialNumber);
560 else if (aCreate) {
561 serialNumberRecord *record = PR_NEW(serialNumberRecord);
562 record->serialNumber = ++gNextSerialNumber;
563 record->refCount = 0;
564 record->COMPtrCount = 0;
565 PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, reinterpret_cast<void*>(record));
566 return gNextSerialNumber;
568 else {
569 return 0;
573 static PRInt32* GetRefCount(void* aPtr)
575 #ifdef GC_LEAK_DETECTOR
576 // need to disguise this pointer, so the table won't keep the object alive.
577 aPtr = (void*) ~PLHashNumber(aPtr);
578 #endif
579 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
580 if (hep && *hep) {
581 return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->refCount);
582 } else {
583 return nsnull;
587 static PRInt32* GetCOMPtrCount(void* aPtr)
589 #ifdef GC_LEAK_DETECTOR
590 // need to disguise this pointer, so the table won't keep the object alive.
591 aPtr = (void*) ~PLHashNumber(aPtr);
592 #endif
593 PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
594 if (hep && *hep) {
595 return &((reinterpret_cast<serialNumberRecord*>((*hep)->value))->COMPtrCount);
596 } else {
597 return nsnull;
601 static void RecycleSerialNumberPtr(void* aPtr)
603 #ifdef GC_LEAK_DETECTOR
604 // need to disguise this pointer, so the table won't keep the object alive.
605 aPtr = (void*) ~PLHashNumber(aPtr);
606 #endif
607 PL_HashTableRemove(gSerialNumbers, aPtr);
610 static PRBool LogThisObj(PRInt32 aSerialNumber)
612 return nsnull != PL_HashTableLookup(gObjectsToLog, (const void*)(aSerialNumber));
615 static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
617 const char* value = getenv(envVar);
618 if (value) {
619 if (nsCRT::strcmp(value, "1") == 0) {
620 *result = stdout;
621 fprintf(stdout, "### %s defined -- logging %s to stdout\n",
622 envVar, msg);
623 return PR_TRUE;
625 else if (nsCRT::strcmp(value, "2") == 0) {
626 *result = stderr;
627 fprintf(stdout, "### %s defined -- logging %s to stderr\n",
628 envVar, msg);
629 return PR_TRUE;
631 else {
632 FILE *stream = ::fopen(value, "w");
633 if (stream != NULL) {
634 *result = stream;
635 fprintf(stdout, "### %s defined -- logging %s to %s\n",
636 envVar, msg, value);
637 return PR_TRUE;
639 else {
640 fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
641 envVar, msg, value);
642 return PR_FALSE;
646 return PR_FALSE;
650 static PLHashNumber PR_CALLBACK HashNumber(const void* aKey)
652 return PLHashNumber(NS_PTR_TO_INT32(aKey));
655 static void InitTraceLog(void)
657 if (gInitialized) return;
658 gInitialized = PR_TRUE;
660 PRBool defined;
661 defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
662 if (!defined)
663 gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
664 if (defined || gLogLeaksOnly) {
665 RecreateBloatView();
666 if (!gBloatView) {
667 NS_WARNING("out of memory");
668 gBloatLog = nsnull;
669 gLogLeaksOnly = PR_FALSE;
673 (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
675 (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
677 defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
678 if (defined) {
679 gLogToLeaky = PR_TRUE;
680 PRFuncPtr p = nsnull, q = nsnull;
681 #ifdef HAVE_LIBDL
683 PRLibrary *lib = nsnull;
684 p = PR_FindFunctionSymbolAndLibrary("__log_addref", &lib);
685 if (lib) {
686 PR_UnloadLibrary(lib);
687 lib = nsnull;
689 q = PR_FindFunctionSymbolAndLibrary("__log_release", &lib);
690 if (lib) {
691 PR_UnloadLibrary(lib);
694 #endif
695 if (p && q) {
696 leakyLogAddRef = (void (*)(void*,int,int)) p;
697 leakyLogRelease = (void (*)(void*,int,int)) q;
699 else {
700 gLogToLeaky = PR_FALSE;
701 fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
702 fflush(stdout);
706 const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
708 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
709 if (classes) {
710 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
711 } else {
712 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
713 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
716 #else
717 const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
718 if (comptr_log) {
719 fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
721 #endif
723 if (classes) {
724 // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
725 // as a list of class names to track
726 gTypesToLog = PL_NewHashTable(256,
727 PL_HashString,
728 PL_CompareStrings,
729 PL_CompareValues,
730 &typesToLogHashAllocOps, NULL);
731 if (!gTypesToLog) {
732 NS_WARNING("out of memory");
733 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
735 else {
736 fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
737 const char* cp = classes;
738 for (;;) {
739 char* cm = (char*) strchr(cp, ',');
740 if (cm) {
741 *cm = '\0';
743 PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
744 fprintf(stdout, "%s ", cp);
745 if (!cm) break;
746 *cm = ',';
747 cp = cm + 1;
749 fprintf(stdout, "\n");
752 gSerialNumbers = PL_NewHashTable(256,
753 HashNumber,
754 PL_CompareValues,
755 PL_CompareValues,
756 &serialNumberHashAllocOps, NULL);
761 const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
762 if (objects) {
763 gObjectsToLog = PL_NewHashTable(256,
764 HashNumber,
765 PL_CompareValues,
766 PL_CompareValues,
767 NULL, NULL);
769 if (!gObjectsToLog) {
770 NS_WARNING("out of memory");
771 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
773 else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
774 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
776 else {
777 fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
778 const char* cp = objects;
779 for (;;) {
780 char* cm = (char*) strchr(cp, ',');
781 if (cm) {
782 *cm = '\0';
784 PRInt32 top = 0;
785 PRInt32 bottom = 0;
786 while (*cp) {
787 if (*cp == '-') {
788 bottom = top;
789 top = 0;
790 ++cp;
792 top *= 10;
793 top += *cp - '0';
794 ++cp;
796 if (!bottom) {
797 bottom = top;
799 for(PRInt32 serialno = bottom; serialno <= top; serialno++) {
800 PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
801 fprintf(stdout, "%d ", serialno);
803 if (!cm) break;
804 *cm = ',';
805 cp = cm + 1;
807 fprintf(stdout, "\n");
812 if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
813 gLogging = PR_TRUE;
816 gTraceLock = PR_NewLock();
819 #endif
821 extern "C" {
823 PR_STATIC_CALLBACK(void) PrintStackFrame(void *aPC, void *aClosure)
825 FILE *stream = (FILE*)aClosure;
826 nsCodeAddressDetails details;
827 char buf[1024];
829 NS_DescribeCodeAddress(aPC, &details);
830 NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf));
831 fprintf(stream, buf);
836 NS_COM void
837 nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
839 NS_StackWalk(PrintStackFrame, 2, aStream);
842 //----------------------------------------------------------------------
844 // This thing is exported by libstdc++
845 // Yes, this is a gcc only hack
846 #if defined(MOZ_DEMANGLE_SYMBOLS)
847 #include <cxxabi.h>
848 #include <stdlib.h> // for free()
849 #endif // MOZ_DEMANGLE_SYMBOLS
851 NS_COM void
852 nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol,
853 char * aBuffer,
854 int aBufLen)
856 NS_ASSERTION(nsnull != aSymbol,"null symbol");
857 NS_ASSERTION(nsnull != aBuffer,"null buffer");
858 NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
860 aBuffer[0] = '\0';
862 #if defined(MOZ_DEMANGLE_SYMBOLS)
863 /* See demangle.h in the gcc source for the voodoo */
864 char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
866 if (demangled)
868 strncpy(aBuffer,demangled,aBufLen);
869 free(demangled);
871 #endif // MOZ_DEMANGLE_SYMBOLS
875 //----------------------------------------------------------------------
877 EXPORT_XPCOM_API(void)
878 NS_LogInit()
880 #ifdef NS_IMPL_REFCNT_LOGGING
881 if (++gInitCount)
882 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE);
883 #endif
886 EXPORT_XPCOM_API(void)
887 NS_LogTerm()
889 NS_ASSERTION(gInitCount > 0,
890 "NS_LogTerm without matching NS_LogInit");
892 if (--gInitCount == 0) {
893 if (gInitialized) {
894 nsTraceRefcntImpl::DumpStatistics();
895 nsTraceRefcntImpl::ResetStatistics();
897 nsTraceRefcntImpl::Shutdown();
898 #ifdef NS_IMPL_REFCNT_LOGGING
899 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE);
900 gActivityTLS = BAD_TLS_INDEX;
901 #endif
905 EXPORT_XPCOM_API(void)
906 NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
907 const char* aClazz, PRUint32 classSize)
909 #ifdef NS_IMPL_REFCNT_LOGGING
910 ASSERT_ACTIVITY_IS_LEGAL;
911 if (!gInitialized)
912 InitTraceLog();
913 if (gLogging) {
914 LOCK_TRACELOG();
916 if (gBloatLog) {
917 BloatEntry* entry = GetBloatEntry(aClazz, classSize);
918 if (entry) {
919 entry->AddRef(aRefcnt);
923 // Here's the case where neither NS_NEWXPCOM nor MOZ_COUNT_CTOR were used,
924 // yet we still want to see creation information:
926 PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
927 PRInt32 serialno = 0;
928 if (gSerialNumbers && loggingThisType) {
929 serialno = GetSerialNumber(aPtr, aRefcnt == 1);
930 PRInt32* count = GetRefCount(aPtr);
931 if(count)
932 (*count)++;
936 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
937 if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
938 fprintf(gAllocLog, "\n<%s> 0x%08X %d Create\n",
939 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
940 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
943 if (gRefcntsLog && loggingThisType && loggingThisObject) {
944 if (gLogToLeaky) {
945 (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
947 else {
948 // Can't use PR_LOG(), b/c it truncates the line
949 fprintf(gRefcntsLog,
950 "\n<%s> 0x%08X %d AddRef %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
951 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog);
952 fflush(gRefcntsLog);
955 UNLOCK_TRACELOG();
957 #endif
960 EXPORT_XPCOM_API(void)
961 NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClazz)
963 #ifdef NS_IMPL_REFCNT_LOGGING
964 ASSERT_ACTIVITY_IS_LEGAL;
965 if (!gInitialized)
966 InitTraceLog();
967 if (gLogging) {
968 LOCK_TRACELOG();
970 if (gBloatLog) {
971 BloatEntry* entry = GetBloatEntry(aClazz, 0);
972 if (entry) {
973 entry->Release(aRefcnt);
977 PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
978 PRInt32 serialno = 0;
979 if (gSerialNumbers && loggingThisType) {
980 serialno = GetSerialNumber(aPtr, PR_FALSE);
981 PRInt32* count = GetRefCount(aPtr);
982 if(count)
983 (*count)--;
987 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
988 if (gRefcntsLog && loggingThisType && loggingThisObject) {
989 if (gLogToLeaky) {
990 (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
992 else {
993 // Can't use PR_LOG(), b/c it truncates the line
994 fprintf(gRefcntsLog,
995 "\n<%s> 0x%08X %d Release %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
996 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog);
997 fflush(gRefcntsLog);
1001 // Here's the case where neither NS_DELETEXPCOM nor MOZ_COUNT_DTOR were used,
1002 // yet we still want to see deletion information:
1004 if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
1005 fprintf(gAllocLog,
1006 "\n<%s> 0x%08X %d Destroy\n",
1007 aClazz, NS_PTR_TO_INT32(aPtr), serialno);
1008 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1011 if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
1012 RecycleSerialNumberPtr(aPtr);
1015 UNLOCK_TRACELOG();
1017 #endif
1020 EXPORT_XPCOM_API(void)
1021 NS_LogCtor(void* aPtr, const char* aType, PRUint32 aInstanceSize)
1023 #ifdef NS_IMPL_REFCNT_LOGGING
1024 ASSERT_ACTIVITY_IS_LEGAL;
1025 if (!gInitialized)
1026 InitTraceLog();
1028 if (gLogging) {
1029 LOCK_TRACELOG();
1031 if (gBloatLog) {
1032 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1033 if (entry) {
1034 entry->Ctor();
1038 PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
1039 PRInt32 serialno = 0;
1040 if (gSerialNumbers && loggingThisType) {
1041 serialno = GetSerialNumber(aPtr, PR_TRUE);
1044 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1045 if (gAllocLog && loggingThisType && loggingThisObject) {
1046 fprintf(gAllocLog, "\n<%s> 0x%08X %d Ctor (%d)\n",
1047 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1048 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1051 UNLOCK_TRACELOG();
1053 #endif
1057 EXPORT_XPCOM_API(void)
1058 NS_LogDtor(void* aPtr, const char* aType, PRUint32 aInstanceSize)
1060 #ifdef NS_IMPL_REFCNT_LOGGING
1061 ASSERT_ACTIVITY_IS_LEGAL;
1062 if (!gInitialized)
1063 InitTraceLog();
1065 if (gLogging) {
1066 LOCK_TRACELOG();
1068 if (gBloatLog) {
1069 BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1070 if (entry) {
1071 entry->Dtor();
1075 PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
1076 PRInt32 serialno = 0;
1077 if (gSerialNumbers && loggingThisType) {
1078 serialno = GetSerialNumber(aPtr, PR_FALSE);
1079 RecycleSerialNumberPtr(aPtr);
1082 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1084 // (If we're on a losing architecture, don't do this because we'll be
1085 // using LogDeleteXPCOM instead to get file and line numbers.)
1086 if (gAllocLog && loggingThisType && loggingThisObject) {
1087 fprintf(gAllocLog, "\n<%s> 0x%08X %d Dtor (%d)\n",
1088 aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1089 nsTraceRefcntImpl::WalkTheStack(gAllocLog);
1092 UNLOCK_TRACELOG();
1094 #endif
1098 EXPORT_XPCOM_API(void)
1099 NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
1101 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1102 // Get the most-derived object.
1103 void *object = dynamic_cast<void *>(aObject);
1105 // This is a very indirect way of finding out what the class is
1106 // of the object being logged. If we're logging a specific type,
1107 // then
1108 if (!gTypesToLog || !gSerialNumbers) {
1109 return;
1111 PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
1112 if (serialno == 0) {
1113 return;
1116 if (!gInitialized)
1117 InitTraceLog();
1118 if (gLogging) {
1119 LOCK_TRACELOG();
1121 PRInt32* count = GetCOMPtrCount(object);
1122 if(count)
1123 (*count)++;
1125 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1127 if (gCOMPtrLog && loggingThisObject) {
1128 fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrAddRef %d 0x%08X\n",
1129 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1130 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog);
1133 UNLOCK_TRACELOG();
1135 #endif
1139 EXPORT_XPCOM_API(void)
1140 NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
1142 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1143 // Get the most-derived object.
1144 void *object = dynamic_cast<void *>(aObject);
1146 // This is a very indirect way of finding out what the class is
1147 // of the object being logged. If we're logging a specific type,
1148 // then
1149 if (!gTypesToLog || !gSerialNumbers) {
1150 return;
1152 PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
1153 if (serialno == 0) {
1154 return;
1157 if (!gInitialized)
1158 InitTraceLog();
1159 if (gLogging) {
1160 LOCK_TRACELOG();
1162 PRInt32* count = GetCOMPtrCount(object);
1163 if(count)
1164 (*count)--;
1166 PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1168 if (gCOMPtrLog && loggingThisObject) {
1169 fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrRelease %d 0x%08X\n",
1170 NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1171 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog);
1174 UNLOCK_TRACELOG();
1176 #endif
1179 NS_COM void
1180 nsTraceRefcntImpl::Startup()
1184 NS_COM void
1185 nsTraceRefcntImpl::Shutdown()
1187 #ifdef NS_IMPL_REFCNT_LOGGING
1189 if (gBloatView) {
1190 PL_HashTableDestroy(gBloatView);
1191 gBloatView = nsnull;
1193 if (gTypesToLog) {
1194 PL_HashTableDestroy(gTypesToLog);
1195 gTypesToLog = nsnull;
1197 if (gObjectsToLog) {
1198 PL_HashTableDestroy(gObjectsToLog);
1199 gObjectsToLog = nsnull;
1201 if (gSerialNumbers) {
1202 PL_HashTableDestroy(gSerialNumbers);
1203 gSerialNumbers = nsnull;
1205 #endif
1208 NS_COM void
1209 nsTraceRefcntImpl::SetActivityIsLegal(PRBool aLegal)
1211 #ifdef NS_IMPL_REFCNT_LOGGING
1212 if (gActivityTLS == BAD_TLS_INDEX)
1213 PR_NewThreadPrivateIndex(&gActivityTLS, nsnull);
1215 PR_SetThreadPrivate(gActivityTLS, NS_INT32_TO_PTR(!aLegal));
1216 #endif
1219 NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl, nsITraceRefcnt)
1221 NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::AddRef(void)
1223 return 2;
1226 NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::Release(void)
1228 return 1;
1231 NS_IMETHODIMP
1232 nsTraceRefcntImpl::LogAddRef(void *aPtr, nsrefcnt aNewRefcnt,
1233 const char *aTypeName, PRUint32 aSize)
1235 NS_LogAddRef(aPtr, aNewRefcnt, aTypeName, aSize);
1236 return NS_OK;
1239 NS_IMETHODIMP
1240 nsTraceRefcntImpl::LogRelease(void *aPtr, nsrefcnt aNewRefcnt,
1241 const char *aTypeName)
1243 NS_LogRelease(aPtr, aNewRefcnt, aTypeName);
1244 return NS_OK;
1247 NS_IMETHODIMP
1248 nsTraceRefcntImpl::LogCtor(void *aPtr, const char *aTypeName, PRUint32 aSize)
1250 NS_LogCtor(aPtr, aTypeName, aSize);
1251 return NS_OK;
1254 NS_IMETHODIMP
1255 nsTraceRefcntImpl::LogDtor(void *aPtr, const char *aTypeName, PRUint32 aSize)
1257 NS_LogDtor(aPtr, aTypeName, aSize);
1258 return NS_OK;
1261 NS_IMETHODIMP
1262 nsTraceRefcntImpl::LogAddCOMPtr(void *aCOMPtr, nsISupports* aObject)
1264 NS_LogCOMPtrAddRef(aCOMPtr, aObject);
1265 return NS_OK;
1268 NS_IMETHODIMP
1269 nsTraceRefcntImpl::LogReleaseCOMPtr(void *aCOMPtr, nsISupports* aObject)
1271 NS_LogCOMPtrRelease(aCOMPtr, aObject);
1272 return NS_OK;
1275 static const nsTraceRefcntImpl kTraceRefcntImpl;
1277 NS_METHOD
1278 nsTraceRefcntImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
1280 return const_cast<nsTraceRefcntImpl*>(&kTraceRefcntImpl)->
1281 QueryInterface(aIID, aInstancePtr);