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 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
))
330 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \
331 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
333 this->DumpTotal(out
);
338 void Dump(PRIntn i
, FILE* out
, nsTraceRefcntImpl::StatisticsType type
) {
339 nsTraceRefcntStats
* stats
= (type
== nsTraceRefcntImpl::NEW_STATS
) ? &mNewStats
: &mAllStats
;
340 if (gLogLeaksOnly
&& !HaveLeaks(stats
)) {
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 ||
360 (stats
->mCreates
- stats
->mDestroys
) != 0 ||
361 stats
->mCreates
!= 0 ||
364 fprintf(out
, "%4d %-40.40s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
367 (nsCRT::strcmp(mClassName
, "TOTAL"))
368 ?(PRInt32
)((stats
->mCreates
- stats
->mDestroys
) * mClassSize
)
371 (stats
->mCreates
- stats
->mDestroys
),
375 (stats
->mAddRefs
- stats
->mReleases
),
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
);
399 const static PLHashAllocOps bloatViewHashAllocOps
= {
400 DefaultAllocTable
, DefaultFreeTable
,
401 DefaultAllocEntry
, BloatViewFreeEntry
407 gBloatView
= PL_NewHashTable(256,
411 &bloatViewHashAllocOps
, NULL
);
415 GetBloatEntry(const char* aTypeName
, PRUint32 aInstanceSize
)
420 BloatEntry
* entry
= NULL
;
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
);
432 NS_ASSERTION(aInstanceSize
== 0 ||
433 entry
->GetClassSize() == aInstanceSize
,
434 "bad size recorded");
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
),
448 record
->COMPtrCount
);
450 fprintf((FILE*) aClosure
, "%d @%p (%d references)\n",
451 record
->serialNumber
,
452 NS_INT32_TO_PTR(aHashEntry
->key
),
455 return HT_ENUMERATE_NEXT
;
459 #endif /* NS_IMPL_REFCNT_LOGGING */
462 nsTraceRefcntImpl::DumpStatistics(StatisticsType type
, FILE* out
)
464 #ifdef NS_IMPL_REFCNT_LOGGING
465 if (gBloatLog
== nsnull
|| gBloatView
== nsnull
) {
466 return NS_ERROR_FAILURE
;
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
);
480 if (type
== NEW_STATS
) {
482 msg
= "NEW (incremental) LEAK STATISTICS";
484 msg
= "NEW (incremental) LEAK AND BLOAT STATISTICS";
488 msg
= "ALL (cumulative) LEAK STATISTICS";
490 msg
= "ALL (cumulative) LEAK AND BLOAT STATISTICS";
492 const PRBool leaked
= total
.PrintDumpHeader(out
, msg
, type
);
495 PL_HashTableEnumerateEntries(gBloatView
, BloatEntry::DumpEntry
, &entries
);
496 const PRInt32 count
= entries
.Count();
498 if (!gLogLeaksOnly
|| leaked
) {
499 // Sort the entries alphabetically by classname.
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
);
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
;
537 nsTraceRefcntImpl::ResetStatistics()
539 #ifdef NS_IMPL_REFCNT_LOGGING
542 PL_HashTableDestroy(gBloatView
);
549 #ifdef NS_IMPL_REFCNT_LOGGING
550 static PRBool
LogThisType(const char* aTypeName
)
552 void* he
= PL_HashTableLookup(gTypesToLog
, aTypeName
);
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
);
562 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
564 return PRInt32((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->serialNumber
);
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
;
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
);
585 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
587 return &((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->refCount
);
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
);
599 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
601 return &((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->COMPtrCount
);
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
);
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
);
625 if (nsCRT::strcmp(value
, "1") == 0) {
627 fprintf(stdout
, "### %s defined -- logging %s to stdout\n",
631 else if (nsCRT::strcmp(value
, "2") == 0) {
633 fprintf(stdout
, "### %s defined -- logging %s to stderr\n",
638 FILE *stream
= ::fopen(value
, "w");
639 if (stream
!= NULL
) {
641 fprintf(stdout
, "### %s defined -- logging %s to %s\n",
646 fprintf(stdout
, "### %s defined -- unable to log %s to %s\n",
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
;
667 defined
= InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog
);
669 gLogLeaksOnly
= InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog
);
670 if (defined
|| gLogLeaksOnly
) {
673 NS_WARNING("out of memory");
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
);
685 gLogToLeaky
= PR_TRUE
;
686 PRFuncPtr p
= nsnull
, q
= nsnull
;
689 PRLibrary
*lib
= nsnull
;
690 p
= PR_FindFunctionSymbolAndLibrary("__log_addref", &lib
);
692 PR_UnloadLibrary(lib
);
695 q
= PR_FindFunctionSymbolAndLibrary("__log_release", &lib
);
697 PR_UnloadLibrary(lib
);
702 leakyLogAddRef
= (void (*)(void*,int,int)) p
;
703 leakyLogRelease
= (void (*)(void*,int,int)) q
;
706 gLogToLeaky
= PR_FALSE
;
707 fprintf(stdout
, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
712 const char* classes
= getenv("XPCOM_MEM_LOG_CLASSES");
714 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
716 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog
);
718 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
719 fprintf(stdout
, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
723 const char* comptr_log
= getenv("XPCOM_MEM_COMPTR_LOG");
725 fprintf(stdout
, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
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,
736 &typesToLogHashAllocOps
, NULL
);
738 NS_WARNING("out of memory");
739 fprintf(stdout
, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
742 fprintf(stdout
, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
743 const char* cp
= classes
;
745 char* cm
= (char*) strchr(cp
, ',');
749 PL_HashTableAdd(gTypesToLog
, nsCRT::strdup(cp
), (void*)1);
750 fprintf(stdout
, "%s ", cp
);
755 fprintf(stdout
, "\n");
758 gSerialNumbers
= PL_NewHashTable(256,
762 &serialNumberHashAllocOps
, NULL
);
767 const char* objects
= getenv("XPCOM_MEM_LOG_OBJECTS");
769 gObjectsToLog
= PL_NewHashTable(256,
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");
783 fprintf(stdout
, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
784 const char* cp
= objects
;
786 char* cm
= (char*) strchr(cp
, ',');
805 for(PRInt32 serialno
= bottom
; serialno
<= top
; serialno
++) {
806 PL_HashTableAdd(gObjectsToLog
, (const void*)serialno
, (void*)1);
807 fprintf(stdout
, "%d ", serialno
);
813 fprintf(stdout
, "\n");
818 if (gBloatLog
|| gRefcntsLog
|| gAllocLog
|| gLeakyLog
|| gCOMPtrLog
) {
822 gTraceLock
= PR_NewLock();
829 PR_STATIC_CALLBACK(void) PrintStackFrame(void *aPC
, void *aClosure
)
831 FILE *stream
= (FILE*)aClosure
;
832 nsCodeAddressDetails details
;
835 NS_DescribeCodeAddress(aPC
, &details
);
836 NS_FormatCodeAddressDetails(aPC
, &details
, buf
, sizeof(buf
));
837 fprintf(stream
, buf
);
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)
854 #include <stdlib.h> // for free()
855 #endif // MOZ_DEMANGLE_SYMBOLS
858 nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol
,
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");
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);
874 strncpy(aBuffer
,demangled
,aBufLen
);
877 #endif // MOZ_DEMANGLE_SYMBOLS
881 //----------------------------------------------------------------------
883 EXPORT_XPCOM_API(void)
886 #ifdef NS_IMPL_REFCNT_LOGGING
888 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE
);
892 EXPORT_XPCOM_API(void)
895 NS_ASSERTION(gInitCount
> 0,
896 "NS_LogTerm without matching NS_LogInit");
898 if (--gInitCount
== 0) {
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
;
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
;
923 BloatEntry
* entry
= GetBloatEntry(aClazz
, classSize
);
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
);
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
) {
954 (*leakyLogAddRef
)(aPtr
, aRefcnt
- 1, aRefcnt
);
957 // Can't use PR_LOG(), b/c it truncates the line
959 "\n<%s> 0x%08X %d AddRef %d\n", aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
, aRefcnt
);
960 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog
);
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
;
980 BloatEntry
* entry
= GetBloatEntry(aClazz
, 0);
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
);
999 PRBool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1000 if (gRefcntsLog
&& loggingThisType
&& loggingThisObject
) {
1002 (*leakyLogRelease
)(aPtr
, aRefcnt
+ 1, aRefcnt
);
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
) {
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
);
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
;
1044 BloatEntry
* entry
= GetBloatEntry(aType
, aInstanceSize
);
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
);
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
;
1081 BloatEntry
* entry
= GetBloatEntry(aType
, aInstanceSize
);
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
);
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,
1120 if (!gTypesToLog
|| !gSerialNumbers
) {
1123 PRInt32 serialno
= GetSerialNumber(object
, PR_FALSE
);
1124 if (serialno
== 0) {
1133 PRInt32
* count
= GetCOMPtrCount(object
);
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
);
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,
1161 if (!gTypesToLog
|| !gSerialNumbers
) {
1164 PRInt32 serialno
= GetSerialNumber(object
, PR_FALSE
);
1165 if (serialno
== 0) {
1174 PRInt32
* count
= GetCOMPtrCount(object
);
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
);
1192 nsTraceRefcntImpl::Startup()
1197 nsTraceRefcntImpl::Shutdown()
1199 #ifdef NS_IMPL_REFCNT_LOGGING
1202 PL_HashTableDestroy(gBloatView
);
1203 gBloatView
= nsnull
;
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
;
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
));
1231 NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl
, nsITraceRefcnt
)
1233 NS_IMETHODIMP_(nsrefcnt
) nsTraceRefcntImpl::AddRef(void)
1238 NS_IMETHODIMP_(nsrefcnt
) nsTraceRefcntImpl::Release(void)
1244 nsTraceRefcntImpl::LogAddRef(void *aPtr
, nsrefcnt aNewRefcnt
,
1245 const char *aTypeName
, PRUint32 aSize
)
1247 NS_LogAddRef(aPtr
, aNewRefcnt
, aTypeName
, aSize
);
1252 nsTraceRefcntImpl::LogRelease(void *aPtr
, nsrefcnt aNewRefcnt
,
1253 const char *aTypeName
)
1255 NS_LogRelease(aPtr
, aNewRefcnt
, aTypeName
);
1260 nsTraceRefcntImpl::LogCtor(void *aPtr
, const char *aTypeName
, PRUint32 aSize
)
1262 NS_LogCtor(aPtr
, aTypeName
, aSize
);
1267 nsTraceRefcntImpl::LogDtor(void *aPtr
, const char *aTypeName
, PRUint32 aSize
)
1269 NS_LogDtor(aPtr
, aTypeName
, aSize
);
1274 nsTraceRefcntImpl::LogAddCOMPtr(void *aCOMPtr
, nsISupports
* aObject
)
1276 NS_LogCOMPtrAddRef(aCOMPtr
, aObject
);
1281 nsTraceRefcntImpl::LogReleaseCOMPtr(void *aCOMPtr
, nsISupports
* aObject
)
1283 NS_LogCOMPtrRelease(aCOMPtr
, aObject
);
1287 static const nsTraceRefcntImpl kTraceRefcntImpl
;
1290 nsTraceRefcntImpl::Create(nsISupports
* outer
, const nsIID
& aIID
, void* *aInstancePtr
)
1292 return const_cast<nsTraceRefcntImpl
*>(&kTraceRefcntImpl
)->
1293 QueryInterface(aIID
, aInstancePtr
);