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
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.
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"
41 #include "nsISupports.h"
42 #include "nsVoidArray.h"
51 #include "nsStackWalk.h"
57 ////////////////////////////////////////////////////////////////////////////////
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)
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;
75 *stdDevResult
= stdDev
;
78 ////////////////////////////////////////////////////////////////////////////////
80 #define NS_IMPL_REFCNT_LOGGING
82 #ifdef NS_IMPL_REFCNT_LOGGING
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
;
129 struct nsTraceRefcntStats
{
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
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
)
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
));
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
)));
187 static const PLHashAllocOps serialNumberHashAllocOps
= {
188 DefaultAllocTable
, DefaultFreeTable
,
189 DefaultAllocEntry
, SerialNumberFreeEntry
192 static const PLHashAllocOps typesToLogHashAllocOps
= {
193 DefaultAllocTable
, DefaultFreeTable
,
194 DefaultAllocEntry
, TypesToLogFreeEntry
197 ////////////////////////////////////////////////////////////////////////////////
201 BloatEntry(const char* className
, PRUint32 classSize
)
202 : mClassSize(classSize
) {
203 mClassName
= PL_strdup(className
);
210 PL_strfree(mClassName
);
213 PRUint32
GetClassSize() { return (PRUint32
)mClassSize
; }
214 const char* GetClassName() { return mClassName
; }
216 static void Clear(nsTraceRefcntStats
* stats
) {
218 stats
->mReleases
= 0;
220 stats
->mDestroys
= 0;
221 stats
->mRefsOutstandingTotal
= 0;
222 stats
->mRefsOutstandingSquared
= 0;
223 stats
->mObjsOutstandingTotal
= 0;
224 stats
->mObjsOutstandingSquared
= 0;
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
;
239 void AddRef(nsrefcnt refcnt
) {
240 mNewStats
.mAddRefs
++;
247 void Release(nsrefcnt refcnt
) {
248 mNewStats
.mReleases
++;
256 mNewStats
.mCreates
++;
261 mNewStats
.mDestroys
++;
266 PRInt32 cnt
= (mNewStats
.mAddRefs
- mNewStats
.mReleases
);
267 mNewStats
.mRefsOutstandingTotal
+= cnt
;
268 mNewStats
.mRefsOutstandingSquared
+= cnt
* cnt
;
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
;
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
);
323 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n");
325 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
329 nsresult
Dump(PRIntn i
, FILE* out
, nsTraceRefcntImpl::StatisticsType type
) {
330 nsTraceRefcntStats
* stats
= (type
== nsTraceRefcntImpl::NEW_STATS
) ? &mNewStats
: &mAllStats
;
331 if (gLogLeaksOnly
&& !HaveLeaks(stats
)) {
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 ||
351 (stats
->mCreates
- stats
->mDestroys
) != 0 ||
352 stats
->mCreates
!= 0 ||
355 fprintf(out
, "%4d %-40.40s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
358 (nsCRT::strcmp(mClassName
, "TOTAL"))
359 ?(PRInt32
)((stats
->mCreates
- stats
->mDestroys
) * mClassSize
)
362 (stats
->mCreates
- stats
->mDestroys
),
366 (stats
->mAddRefs
- stats
->mReleases
),
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
);
391 const static PLHashAllocOps bloatViewHashAllocOps
= {
392 DefaultAllocTable
, DefaultFreeTable
,
393 DefaultAllocEntry
, BloatViewFreeEntry
399 gBloatView
= PL_NewHashTable(256,
403 &bloatViewHashAllocOps
, NULL
);
407 GetBloatEntry(const char* aTypeName
, PRUint32 aInstanceSize
)
412 BloatEntry
* entry
= NULL
;
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
);
424 NS_ASSERTION(aInstanceSize
== 0 ||
425 entry
->GetClassSize() == aInstanceSize
,
426 "bad size recorded");
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
),
440 record
->COMPtrCount
);
442 fprintf((FILE*) aClosure
, "%d @%p (%d references)\n",
443 record
->serialNumber
,
444 NS_INT32_TO_PTR(aHashEntry
->key
),
447 return HT_ENUMERATE_NEXT
;
451 #endif /* NS_IMPL_REFCNT_LOGGING */
454 nsTraceRefcntImpl::DumpStatistics(StatisticsType type
, FILE* out
)
457 #ifdef NS_IMPL_REFCNT_LOGGING
458 if (gBloatLog
== nsnull
|| gBloatView
== nsnull
) {
459 return NS_ERROR_FAILURE
;
467 PRBool wasLogging
= gLogging
;
468 gLogging
= PR_FALSE
; // turn off logging for this method
471 if (type
== NEW_STATS
) {
473 msg
= "NEW (incremental) LEAK STATISTICS";
475 msg
= "NEW (incremental) LEAK AND BLOAT STATISTICS";
479 msg
= "ALL (cumulative) LEAK STATISTICS";
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
);
492 PL_HashTableEnumerateEntries(gBloatView
, BloatEntry::DumpEntry
, &entries
);
494 fprintf(stdout
, "nsTraceRefcntImpl::DumpStatistics: %d entries\n",
497 // Sort the entries alphabetically by classname.
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
);
524 gLogging
= wasLogging
;
531 nsTraceRefcntImpl::ResetStatistics()
533 #ifdef NS_IMPL_REFCNT_LOGGING
536 PL_HashTableDestroy(gBloatView
);
543 #ifdef NS_IMPL_REFCNT_LOGGING
544 static PRBool
LogThisType(const char* aTypeName
)
546 void* he
= PL_HashTableLookup(gTypesToLog
, aTypeName
);
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
);
556 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
558 return PRInt32((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->serialNumber
);
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
;
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
);
579 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
581 return &((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->refCount
);
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
);
593 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
595 return &((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->COMPtrCount
);
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
);
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
);
619 if (nsCRT::strcmp(value
, "1") == 0) {
621 fprintf(stdout
, "### %s defined -- logging %s to stdout\n",
625 else if (nsCRT::strcmp(value
, "2") == 0) {
627 fprintf(stdout
, "### %s defined -- logging %s to stderr\n",
632 FILE *stream
= ::fopen(value
, "w");
633 if (stream
!= NULL
) {
635 fprintf(stdout
, "### %s defined -- logging %s to %s\n",
640 fprintf(stdout
, "### %s defined -- unable to log %s to %s\n",
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
;
661 defined
= InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog
);
663 gLogLeaksOnly
= InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog
);
664 if (defined
|| gLogLeaksOnly
) {
667 NS_WARNING("out of memory");
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
);
679 gLogToLeaky
= PR_TRUE
;
680 PRFuncPtr p
= nsnull
, q
= nsnull
;
683 PRLibrary
*lib
= nsnull
;
684 p
= PR_FindFunctionSymbolAndLibrary("__log_addref", &lib
);
686 PR_UnloadLibrary(lib
);
689 q
= PR_FindFunctionSymbolAndLibrary("__log_release", &lib
);
691 PR_UnloadLibrary(lib
);
696 leakyLogAddRef
= (void (*)(void*,int,int)) p
;
697 leakyLogRelease
= (void (*)(void*,int,int)) q
;
700 gLogToLeaky
= PR_FALSE
;
701 fprintf(stdout
, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
706 const char* classes
= getenv("XPCOM_MEM_LOG_CLASSES");
708 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
710 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog
);
712 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
713 fprintf(stdout
, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
717 const char* comptr_log
= getenv("XPCOM_MEM_COMPTR_LOG");
719 fprintf(stdout
, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
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,
730 &typesToLogHashAllocOps
, NULL
);
732 NS_WARNING("out of memory");
733 fprintf(stdout
, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
736 fprintf(stdout
, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
737 const char* cp
= classes
;
739 char* cm
= (char*) strchr(cp
, ',');
743 PL_HashTableAdd(gTypesToLog
, nsCRT::strdup(cp
), (void*)1);
744 fprintf(stdout
, "%s ", cp
);
749 fprintf(stdout
, "\n");
752 gSerialNumbers
= PL_NewHashTable(256,
756 &serialNumberHashAllocOps
, NULL
);
761 const char* objects
= getenv("XPCOM_MEM_LOG_OBJECTS");
763 gObjectsToLog
= PL_NewHashTable(256,
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");
777 fprintf(stdout
, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
778 const char* cp
= objects
;
780 char* cm
= (char*) strchr(cp
, ',');
799 for(PRInt32 serialno
= bottom
; serialno
<= top
; serialno
++) {
800 PL_HashTableAdd(gObjectsToLog
, (const void*)serialno
, (void*)1);
801 fprintf(stdout
, "%d ", serialno
);
807 fprintf(stdout
, "\n");
812 if (gBloatLog
|| gRefcntsLog
|| gAllocLog
|| gLeakyLog
|| gCOMPtrLog
) {
816 gTraceLock
= PR_NewLock();
823 PR_STATIC_CALLBACK(void) PrintStackFrame(void *aPC
, void *aClosure
)
825 FILE *stream
= (FILE*)aClosure
;
826 nsCodeAddressDetails details
;
829 NS_DescribeCodeAddress(aPC
, &details
);
830 NS_FormatCodeAddressDetails(aPC
, &details
, buf
, sizeof(buf
));
831 fprintf(stream
, buf
);
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)
848 #include <stdlib.h> // for free()
849 #endif // MOZ_DEMANGLE_SYMBOLS
852 nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol
,
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");
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);
868 strncpy(aBuffer
,demangled
,aBufLen
);
871 #endif // MOZ_DEMANGLE_SYMBOLS
875 //----------------------------------------------------------------------
877 EXPORT_XPCOM_API(void)
880 #ifdef NS_IMPL_REFCNT_LOGGING
882 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE
);
886 EXPORT_XPCOM_API(void)
889 NS_ASSERTION(gInitCount
> 0,
890 "NS_LogTerm without matching NS_LogInit");
892 if (--gInitCount
== 0) {
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
;
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
;
917 BloatEntry
* entry
= GetBloatEntry(aClazz
, classSize
);
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
);
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
) {
945 (*leakyLogAddRef
)(aPtr
, aRefcnt
- 1, aRefcnt
);
948 // Can't use PR_LOG(), b/c it truncates the line
950 "\n<%s> 0x%08X %d AddRef %d\n", aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
, aRefcnt
);
951 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog
);
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
;
971 BloatEntry
* entry
= GetBloatEntry(aClazz
, 0);
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
);
987 PRBool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
988 if (gRefcntsLog
&& loggingThisType
&& loggingThisObject
) {
990 (*leakyLogRelease
)(aPtr
, aRefcnt
+ 1, aRefcnt
);
993 // Can't use PR_LOG(), b/c it truncates the line
995 "\n<%s> 0x%08X %d Release %d\n", aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
, aRefcnt
);
996 nsTraceRefcntImpl::WalkTheStack(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
) {
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
);
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
;
1032 BloatEntry
* entry
= GetBloatEntry(aType
, aInstanceSize
);
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
);
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
;
1069 BloatEntry
* entry
= GetBloatEntry(aType
, aInstanceSize
);
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
);
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,
1108 if (!gTypesToLog
|| !gSerialNumbers
) {
1111 PRInt32 serialno
= GetSerialNumber(object
, PR_FALSE
);
1112 if (serialno
== 0) {
1121 PRInt32
* count
= GetCOMPtrCount(object
);
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
);
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,
1149 if (!gTypesToLog
|| !gSerialNumbers
) {
1152 PRInt32 serialno
= GetSerialNumber(object
, PR_FALSE
);
1153 if (serialno
== 0) {
1162 PRInt32
* count
= GetCOMPtrCount(object
);
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
);
1180 nsTraceRefcntImpl::Startup()
1185 nsTraceRefcntImpl::Shutdown()
1187 #ifdef NS_IMPL_REFCNT_LOGGING
1190 PL_HashTableDestroy(gBloatView
);
1191 gBloatView
= nsnull
;
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
;
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
));
1219 NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl
, nsITraceRefcnt
)
1221 NS_IMETHODIMP_(nsrefcnt
) nsTraceRefcntImpl::AddRef(void)
1226 NS_IMETHODIMP_(nsrefcnt
) nsTraceRefcntImpl::Release(void)
1232 nsTraceRefcntImpl::LogAddRef(void *aPtr
, nsrefcnt aNewRefcnt
,
1233 const char *aTypeName
, PRUint32 aSize
)
1235 NS_LogAddRef(aPtr
, aNewRefcnt
, aTypeName
, aSize
);
1240 nsTraceRefcntImpl::LogRelease(void *aPtr
, nsrefcnt aNewRefcnt
,
1241 const char *aTypeName
)
1243 NS_LogRelease(aPtr
, aNewRefcnt
, aTypeName
);
1248 nsTraceRefcntImpl::LogCtor(void *aPtr
, const char *aTypeName
, PRUint32 aSize
)
1250 NS_LogCtor(aPtr
, aTypeName
, aSize
);
1255 nsTraceRefcntImpl::LogDtor(void *aPtr
, const char *aTypeName
, PRUint32 aSize
)
1257 NS_LogDtor(aPtr
, aTypeName
, aSize
);
1262 nsTraceRefcntImpl::LogAddCOMPtr(void *aCOMPtr
, nsISupports
* aObject
)
1264 NS_LogCOMPtrAddRef(aCOMPtr
, aObject
);
1269 nsTraceRefcntImpl::LogReleaseCOMPtr(void *aCOMPtr
, nsISupports
* aObject
)
1271 NS_LogCOMPtrRelease(aCOMPtr
, aObject
);
1275 static const nsTraceRefcntImpl kTraceRefcntImpl
;
1278 nsTraceRefcntImpl::Create(nsISupports
* outer
, const nsIID
& aIID
, void* *aInstancePtr
)
1280 return const_cast<nsTraceRefcntImpl
*>(&kTraceRefcntImpl
)->
1281 QueryInterface(aIID
, aInstancePtr
);