1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsTraceRefcntImpl.h"
7 #include "mozilla/IntegerPrintfMacros.h"
8 #include "nsXPCOMPrivate.h"
10 #include "nsISupports.h"
17 #include "nsStackWalkPrivate.h"
18 #include "nsStackWalk.h"
21 #include "nsXULAppAPI.h"
24 #define getpid _getpid
29 #ifdef NS_TRACE_MALLOC
30 #include "nsTraceMalloc.h"
33 #include "mozilla/BlockingResourceBase.h"
34 #include "mozilla/PoisonIOInterposer.h"
40 ////////////////////////////////////////////////////////////////////////////////
43 NS_MeanAndStdDev(double n
, double sumOfValues
, double sumOfSquaredValues
,
44 double *meanResult
, double *stdDevResult
)
46 double mean
= 0.0, var
= 0.0, stdDev
= 0.0;
47 if (n
> 0.0 && sumOfValues
>= 0) {
48 mean
= sumOfValues
/ n
;
49 double temp
= (n
* sumOfSquaredValues
) - (sumOfValues
* sumOfValues
);
50 if (temp
< 0.0 || n
<= 1)
53 var
= temp
/ (n
* (n
- 1));
54 // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
55 stdDev
= var
!= 0.0 ? sqrt(var
) : 0.0;
58 *stdDevResult
= stdDev
;
61 ////////////////////////////////////////////////////////////////////////////////
63 #if !defined(XP_WIN) || (!defined(MOZ_OPTIMIZE) || defined(MOZ_PROFILING) || defined(DEBUG))
64 #define STACKWALKING_AVAILABLE
67 #define NS_IMPL_REFCNT_LOGGING
69 #ifdef NS_IMPL_REFCNT_LOGGING
75 // TraceRefcnt has to use bare PRLock instead of mozilla::Mutex
76 // because TraceRefcnt can be used very early in startup.
77 static PRLock
* gTraceLock
;
79 #define LOCK_TRACELOG() PR_Lock(gTraceLock)
80 #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
82 static PLHashTable
* gBloatView
;
83 static PLHashTable
* gTypesToLog
;
84 static PLHashTable
* gObjectsToLog
;
85 static PLHashTable
* gSerialNumbers
;
86 static intptr_t gNextSerialNumber
;
89 static bool gLogToLeaky
;
90 static bool gLogLeaksOnly
;
92 static void (*leakyLogAddRef
)(void* p
, int oldrc
, int newrc
);
93 static void (*leakyLogRelease
)(void* p
, int oldrc
, int newrc
);
95 #define BAD_TLS_INDEX ((unsigned) -1)
97 // if gActivityTLS == BAD_TLS_INDEX, then we're
98 // unitialized... otherwise this points to a NSPR TLS thread index
99 // indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
100 // activity is ok, otherwise not!
101 static unsigned gActivityTLS
= BAD_TLS_INDEX
;
103 static bool gInitialized
;
104 static nsrefcnt gInitCount
;
106 static FILE *gBloatLog
= nullptr;
107 static FILE *gRefcntsLog
= nullptr;
108 static FILE *gAllocLog
= nullptr;
109 static FILE *gLeakyLog
= nullptr;
110 static FILE *gCOMPtrLog
= nullptr;
112 struct serialNumberRecord
{
113 intptr_t serialNumber
;
118 struct nsTraceRefcntStats
{
123 double mRefsOutstandingTotal
;
124 double mRefsOutstandingSquared
;
125 double mObjsOutstandingTotal
;
126 double mObjsOutstandingSquared
;
129 // I hope to turn this on for everybody once we hit it a little less.
131 static const char kStaticCtorDtorWarning
[] =
132 "XPCOM objects created/destroyed from static ctor/dtor";
135 AssertActivityIsLegal()
137 if (gActivityTLS
== BAD_TLS_INDEX
||
138 NS_PTR_TO_INT32(PR_GetThreadPrivate(gActivityTLS
)) != 0) {
139 if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) {
140 NS_RUNTIMEABORT(kStaticCtorDtorWarning
);
142 NS_WARNING(kStaticCtorDtorWarning
);
146 # define ASSERT_ACTIVITY_IS_LEGAL \
148 AssertActivityIsLegal(); \
151 # define ASSERT_ACTIVITY_IS_LEGAL PR_BEGIN_MACRO PR_END_MACRO
154 // These functions are copied from nsprpub/lib/ds/plhash.c, with changes
155 // to the functions not called Default* to free the serialNumberRecord or
159 DefaultAllocTable(void *pool
, size_t size
)
161 return PR_MALLOC(size
);
165 DefaultFreeTable(void *pool
, void *item
)
171 DefaultAllocEntry(void *pool
, const void *key
)
173 return PR_NEW(PLHashEntry
);
177 SerialNumberFreeEntry(void *pool
, PLHashEntry
*he
, unsigned flag
)
179 if (flag
== HT_FREE_ENTRY
) {
180 PR_Free(reinterpret_cast<serialNumberRecord
*>(he
->value
));
186 TypesToLogFreeEntry(void *pool
, PLHashEntry
*he
, unsigned flag
)
188 if (flag
== HT_FREE_ENTRY
) {
189 free(const_cast<char*>(reinterpret_cast<const char*>(he
->key
)));
194 static const PLHashAllocOps serialNumberHashAllocOps
= {
195 DefaultAllocTable
, DefaultFreeTable
,
196 DefaultAllocEntry
, SerialNumberFreeEntry
199 static const PLHashAllocOps typesToLogHashAllocOps
= {
200 DefaultAllocTable
, DefaultFreeTable
,
201 DefaultAllocEntry
, TypesToLogFreeEntry
204 ////////////////////////////////////////////////////////////////////////////////
208 BloatEntry(const char* className
, uint32_t classSize
)
209 : mClassSize(classSize
) {
210 mClassName
= PL_strdup(className
);
217 PL_strfree(mClassName
);
220 uint32_t GetClassSize() { return (uint32_t)mClassSize
; }
221 const char* GetClassName() { return mClassName
; }
223 static void Clear(nsTraceRefcntStats
* stats
) {
225 stats
->mReleases
= 0;
227 stats
->mDestroys
= 0;
228 stats
->mRefsOutstandingTotal
= 0;
229 stats
->mRefsOutstandingSquared
= 0;
230 stats
->mObjsOutstandingTotal
= 0;
231 stats
->mObjsOutstandingSquared
= 0;
235 mAllStats
.mAddRefs
+= mNewStats
.mAddRefs
;
236 mAllStats
.mReleases
+= mNewStats
.mReleases
;
237 mAllStats
.mCreates
+= mNewStats
.mCreates
;
238 mAllStats
.mDestroys
+= mNewStats
.mDestroys
;
239 mAllStats
.mRefsOutstandingTotal
+= mNewStats
.mRefsOutstandingTotal
;
240 mAllStats
.mRefsOutstandingSquared
+= mNewStats
.mRefsOutstandingSquared
;
241 mAllStats
.mObjsOutstandingTotal
+= mNewStats
.mObjsOutstandingTotal
;
242 mAllStats
.mObjsOutstandingSquared
+= mNewStats
.mObjsOutstandingSquared
;
246 void AddRef(nsrefcnt refcnt
) {
247 mNewStats
.mAddRefs
++;
254 void Release(nsrefcnt refcnt
) {
255 mNewStats
.mReleases
++;
263 mNewStats
.mCreates
++;
268 mNewStats
.mDestroys
++;
273 uint64_t cnt
= (mNewStats
.mAddRefs
- mNewStats
.mReleases
);
274 mNewStats
.mRefsOutstandingTotal
+= cnt
;
275 mNewStats
.mRefsOutstandingSquared
+= cnt
* cnt
;
279 uint64_t cnt
= (mNewStats
.mCreates
- mNewStats
.mDestroys
);
280 mNewStats
.mObjsOutstandingTotal
+= cnt
;
281 mNewStats
.mObjsOutstandingSquared
+= cnt
* cnt
;
284 static int DumpEntry(PLHashEntry
*he
, int i
, void *arg
) {
285 BloatEntry
* entry
= (BloatEntry
*)he
->value
;
288 static_cast<nsTArray
<BloatEntry
*>*>(arg
)->AppendElement(entry
);
290 return HT_ENUMERATE_NEXT
;
293 static int TotalEntries(PLHashEntry
*he
, int i
, void *arg
) {
294 BloatEntry
* entry
= (BloatEntry
*)he
->value
;
295 if (entry
&& nsCRT::strcmp(entry
->mClassName
, "TOTAL") != 0) {
296 entry
->Total((BloatEntry
*)arg
);
298 return HT_ENUMERATE_NEXT
;
301 void Total(BloatEntry
* total
) {
302 total
->mAllStats
.mAddRefs
+= mNewStats
.mAddRefs
+ mAllStats
.mAddRefs
;
303 total
->mAllStats
.mReleases
+= mNewStats
.mReleases
+ mAllStats
.mReleases
;
304 total
->mAllStats
.mCreates
+= mNewStats
.mCreates
+ mAllStats
.mCreates
;
305 total
->mAllStats
.mDestroys
+= mNewStats
.mDestroys
+ mAllStats
.mDestroys
;
306 total
->mAllStats
.mRefsOutstandingTotal
+= mNewStats
.mRefsOutstandingTotal
+ mAllStats
.mRefsOutstandingTotal
;
307 total
->mAllStats
.mRefsOutstandingSquared
+= mNewStats
.mRefsOutstandingSquared
+ mAllStats
.mRefsOutstandingSquared
;
308 total
->mAllStats
.mObjsOutstandingTotal
+= mNewStats
.mObjsOutstandingTotal
+ mAllStats
.mObjsOutstandingTotal
;
309 total
->mAllStats
.mObjsOutstandingSquared
+= mNewStats
.mObjsOutstandingSquared
+ mAllStats
.mObjsOutstandingSquared
;
310 uint64_t count
= (mNewStats
.mCreates
+ mAllStats
.mCreates
);
311 total
->mClassSize
+= mClassSize
* count
; // adjust for average in DumpTotal
312 total
->mTotalLeaked
+= (uint64_t)(mClassSize
*
313 ((mNewStats
.mCreates
+ mAllStats
.mCreates
)
314 -(mNewStats
.mDestroys
+ mAllStats
.mDestroys
)));
317 void DumpTotal(FILE* out
) {
318 mClassSize
/= mAllStats
.mCreates
;
319 Dump(-1, out
, nsTraceRefcntImpl::ALL_STATS
);
322 static bool HaveLeaks(nsTraceRefcntStats
* stats
) {
323 return ((stats
->mAddRefs
!= stats
->mReleases
) ||
324 (stats
->mCreates
!= stats
->mDestroys
));
327 bool PrintDumpHeader(FILE* out
, const char* msg
, nsTraceRefcntImpl::StatisticsType type
) {
328 fprintf(out
, "\n== BloatView: %s, %s process %d\n", msg
,
329 XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
330 nsTraceRefcntStats
& stats
=
331 (type
== nsTraceRefcntImpl::NEW_STATS
) ? mNewStats
: mAllStats
;
332 if (gLogLeaksOnly
&& !HaveLeaks(&stats
))
337 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \
338 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
340 this->DumpTotal(out
);
345 void Dump(int i
, FILE* out
, nsTraceRefcntImpl::StatisticsType type
) {
346 nsTraceRefcntStats
* stats
= (type
== nsTraceRefcntImpl::NEW_STATS
) ? &mNewStats
: &mAllStats
;
347 if (gLogLeaksOnly
&& !HaveLeaks(stats
)) {
351 double meanRefs
, stddevRefs
;
352 NS_MeanAndStdDev(stats
->mAddRefs
+ stats
->mReleases
,
353 stats
->mRefsOutstandingTotal
,
354 stats
->mRefsOutstandingSquared
,
355 &meanRefs
, &stddevRefs
);
357 double meanObjs
, stddevObjs
;
358 NS_MeanAndStdDev(stats
->mCreates
+ stats
->mDestroys
,
359 stats
->mObjsOutstandingTotal
,
360 stats
->mObjsOutstandingSquared
,
361 &meanObjs
, &stddevObjs
);
363 if ((stats
->mAddRefs
- stats
->mReleases
) != 0 ||
364 stats
->mAddRefs
!= 0 ||
367 (stats
->mCreates
- stats
->mDestroys
) != 0 ||
368 stats
->mCreates
!= 0 ||
371 fprintf(out
, "%4d %-40.40s %8d %8" PRIu64
" %8" PRIu64
" %8" PRIu64
" (%8.2f +/- %8.2f) %8" PRIu64
" %8" PRIu64
" (%8.2f +/- %8.2f)\n",
374 (nsCRT::strcmp(mClassName
, "TOTAL"))
375 ?(uint64_t)((stats
->mCreates
- stats
->mDestroys
) * mClassSize
)
378 (stats
->mCreates
- stats
->mDestroys
),
382 (stats
->mAddRefs
- stats
->mReleases
),
390 double mClassSize
; // this is stored as a double because of the way we compute the avg class size for total bloat
391 uint64_t mTotalLeaked
; // used only for TOTAL entry
392 nsTraceRefcntStats mNewStats
;
393 nsTraceRefcntStats mAllStats
;
397 BloatViewFreeEntry(void *pool
, PLHashEntry
*he
, unsigned flag
)
399 if (flag
== HT_FREE_ENTRY
) {
400 BloatEntry
* entry
= reinterpret_cast<BloatEntry
*>(he
->value
);
406 const static PLHashAllocOps bloatViewHashAllocOps
= {
407 DefaultAllocTable
, DefaultFreeTable
,
408 DefaultAllocEntry
, BloatViewFreeEntry
414 gBloatView
= PL_NewHashTable(256,
418 &bloatViewHashAllocOps
, nullptr);
422 GetBloatEntry(const char* aTypeName
, uint32_t aInstanceSize
)
427 BloatEntry
* entry
= nullptr;
429 entry
= (BloatEntry
*)PL_HashTableLookup(gBloatView
, aTypeName
);
430 if (entry
== nullptr && aInstanceSize
> 0) {
432 entry
= new BloatEntry(aTypeName
, aInstanceSize
);
433 PLHashEntry
* e
= PL_HashTableAdd(gBloatView
, aTypeName
, entry
);
439 NS_ASSERTION(aInstanceSize
== 0 ||
440 entry
->GetClassSize() == aInstanceSize
,
441 "bad size recorded");
447 static int DumpSerialNumbers(PLHashEntry
* aHashEntry
, int aIndex
, void* aClosure
)
449 serialNumberRecord
* record
= reinterpret_cast<serialNumberRecord
*>(aHashEntry
->value
);
450 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
451 fprintf((FILE*) aClosure
, "%" PRIdPTR
452 " @%p (%d references; %d from COMPtrs)\n",
453 record
->serialNumber
,
454 NS_INT32_TO_PTR(aHashEntry
->key
),
456 record
->COMPtrCount
);
458 fprintf((FILE*) aClosure
, "%" PRIdPTR
459 " @%p (%d references)\n",
460 record
->serialNumber
,
461 NS_INT32_TO_PTR(aHashEntry
->key
),
464 return HT_ENUMERATE_NEXT
;
469 class nsDefaultComparator
<BloatEntry
*, BloatEntry
*> {
471 bool Equals(BloatEntry
* const& aA
, BloatEntry
* const& aB
) const {
472 return PL_strcmp(aA
->GetClassName(), aB
->GetClassName()) == 0;
474 bool LessThan(BloatEntry
* const& aA
, BloatEntry
* const& aB
) const {
475 return PL_strcmp(aA
->GetClassName(), aB
->GetClassName()) < 0;
479 #endif /* NS_IMPL_REFCNT_LOGGING */
482 nsTraceRefcntImpl::DumpStatistics(StatisticsType type
, FILE* out
)
484 #ifdef NS_IMPL_REFCNT_LOGGING
485 if (gBloatLog
== nullptr || gBloatView
== nullptr) {
486 return NS_ERROR_FAILURE
;
488 if (out
== nullptr) {
494 bool wasLogging
= gLogging
;
495 gLogging
= false; // turn off logging for this method
497 BloatEntry
total("TOTAL", 0);
498 PL_HashTableEnumerateEntries(gBloatView
, BloatEntry::TotalEntries
, &total
);
500 if (type
== NEW_STATS
) {
502 msg
= "NEW (incremental) LEAK STATISTICS";
504 msg
= "NEW (incremental) LEAK AND BLOAT STATISTICS";
508 msg
= "ALL (cumulative) LEAK STATISTICS";
510 msg
= "ALL (cumulative) LEAK AND BLOAT STATISTICS";
512 const bool leaked
= total
.PrintDumpHeader(out
, msg
, type
);
514 nsTArray
<BloatEntry
*> entries
;
515 PL_HashTableEnumerateEntries(gBloatView
, BloatEntry::DumpEntry
, &entries
);
516 const uint32_t count
= entries
.Length();
518 if (!gLogLeaksOnly
|| leaked
) {
519 // Sort the entries alphabetically by classname.
522 for (uint32_t i
= 0; i
< count
; ++i
) {
523 BloatEntry
* entry
= entries
[i
];
524 entry
->Dump(i
, out
, type
);
530 fprintf(out
, "nsTraceRefcntImpl::DumpStatistics: %d entries\n", count
);
532 if (gSerialNumbers
) {
533 fprintf(out
, "\nSerial Numbers of Leaked Objects:\n");
534 PL_HashTableEnumerateEntries(gSerialNumbers
, DumpSerialNumbers
, out
);
537 gLogging
= wasLogging
;
545 nsTraceRefcntImpl::ResetStatistics()
547 #ifdef NS_IMPL_REFCNT_LOGGING
550 PL_HashTableDestroy(gBloatView
);
551 gBloatView
= nullptr;
557 #ifdef NS_IMPL_REFCNT_LOGGING
558 static bool LogThisType(const char* aTypeName
)
560 void* he
= PL_HashTableLookup(gTypesToLog
, aTypeName
);
561 return nullptr != he
;
564 static intptr_t GetSerialNumber(void* aPtr
, bool aCreate
)
566 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
568 return reinterpret_cast<serialNumberRecord
*>((*hep
)->value
)->serialNumber
;
571 serialNumberRecord
*record
= PR_NEW(serialNumberRecord
);
572 record
->serialNumber
= ++gNextSerialNumber
;
573 record
->refCount
= 0;
574 record
->COMPtrCount
= 0;
575 PL_HashTableRawAdd(gSerialNumbers
, hep
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
, reinterpret_cast<void*>(record
));
576 return gNextSerialNumber
;
583 static int32_t* GetRefCount(void* aPtr
)
585 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
587 return &((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->refCount
);
593 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
594 static int32_t* GetCOMPtrCount(void* aPtr
)
596 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
598 return &((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->COMPtrCount
);
605 static void RecycleSerialNumberPtr(void* aPtr
)
607 PL_HashTableRemove(gSerialNumbers
, aPtr
);
610 static bool LogThisObj(intptr_t aSerialNumber
)
612 return nullptr != PL_HashTableLookup(gObjectsToLog
, (const void*)aSerialNumber
);
616 #define FOPEN_NO_INHERIT "N"
618 #define FOPEN_NO_INHERIT
621 static bool 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",
639 nsAutoCString
fname(value
);
640 if (XRE_GetProcessType() != GeckoProcessType_Default
) {
641 bool hasLogExtension
=
642 fname
.RFind(".log", true, -1, 4) == kNotFound
? false : true;
644 fname
.Cut(fname
.Length() - 4, 4);
645 fname
.AppendLiteral("_");
646 fname
.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
647 fname
.AppendLiteral("_pid");
648 fname
.AppendInt((uint32_t)getpid());
650 fname
.AppendLiteral(".log");
652 stream
= ::fopen(fname
.get(), "w" FOPEN_NO_INHERIT
);
653 if (stream
!= nullptr) {
654 MozillaRegisterDebugFD(fileno(stream
));
656 fprintf(stdout
, "### %s defined -- logging %s to %s\n",
657 envVar
, msg
, fname
.get());
660 fprintf(stdout
, "### %s defined -- unable to log %s to %s\n",
661 envVar
, msg
, fname
.get());
663 return stream
!= nullptr;
670 static PLHashNumber
HashNumber(const void* aKey
)
672 return PLHashNumber(NS_PTR_TO_INT32(aKey
));
675 static void InitTraceLog(void)
677 if (gInitialized
) return;
681 defined
= InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog
);
683 gLogLeaksOnly
= InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog
);
684 if (defined
|| gLogLeaksOnly
) {
687 NS_WARNING("out of memory");
689 gLogLeaksOnly
= false;
693 (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog
);
695 (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog
);
697 defined
= InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog
);
700 PRFuncPtr p
= nullptr, q
= nullptr;
703 PRLibrary
*lib
= nullptr;
704 p
= PR_FindFunctionSymbolAndLibrary("__log_addref", &lib
);
706 PR_UnloadLibrary(lib
);
709 q
= PR_FindFunctionSymbolAndLibrary("__log_release", &lib
);
711 PR_UnloadLibrary(lib
);
716 leakyLogAddRef
= (void (*)(void*,int,int)) p
;
717 leakyLogRelease
= (void (*)(void*,int,int)) q
;
721 fprintf(stdout
, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
726 const char* classes
= getenv("XPCOM_MEM_LOG_CLASSES");
728 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
730 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog
);
732 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
733 fprintf(stdout
, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
737 const char* comptr_log
= getenv("XPCOM_MEM_COMPTR_LOG");
739 fprintf(stdout
, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
744 // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
745 // as a list of class names to track
746 gTypesToLog
= PL_NewHashTable(256,
750 &typesToLogHashAllocOps
, nullptr);
752 NS_WARNING("out of memory");
753 fprintf(stdout
, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
756 fprintf(stdout
, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
757 const char* cp
= classes
;
759 char* cm
= (char*) strchr(cp
, ',');
763 PL_HashTableAdd(gTypesToLog
, strdup(cp
), (void*)1);
764 fprintf(stdout
, "%s ", cp
);
769 fprintf(stdout
, "\n");
772 gSerialNumbers
= PL_NewHashTable(256,
776 &serialNumberHashAllocOps
, nullptr);
781 const char* objects
= getenv("XPCOM_MEM_LOG_OBJECTS");
783 gObjectsToLog
= PL_NewHashTable(256,
789 if (!gObjectsToLog
) {
790 NS_WARNING("out of memory");
791 fprintf(stdout
, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
793 else if (! (gRefcntsLog
|| gAllocLog
|| gCOMPtrLog
)) {
794 fprintf(stdout
, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
797 fprintf(stdout
, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
798 const char* cp
= objects
;
800 char* cm
= (char*) strchr(cp
, ',');
819 for (intptr_t serialno
= bottom
; serialno
<= top
; serialno
++) {
820 PL_HashTableAdd(gObjectsToLog
, (const void*)serialno
, (void*)1);
821 fprintf(stdout
, "%" PRIdPTR
" ", serialno
);
827 fprintf(stdout
, "\n");
832 if (gBloatLog
|| gRefcntsLog
|| gAllocLog
|| gLeakyLog
|| gCOMPtrLog
) {
836 gTraceLock
= PR_NewLock();
843 #ifdef STACKWALKING_AVAILABLE
844 static void PrintStackFrame(void *aPC
, void *aSP
, void *aClosure
)
846 FILE *stream
= (FILE*)aClosure
;
847 nsCodeAddressDetails details
;
850 NS_DescribeCodeAddress(aPC
, &details
);
851 NS_FormatCodeAddressDetails(aPC
, &details
, buf
, sizeof(buf
));
859 nsTraceRefcntImpl::WalkTheStack(FILE* aStream
)
861 #ifdef STACKWALKING_AVAILABLE
862 NS_StackWalk(PrintStackFrame
, /* skipFrames */ 2, /* maxFrames */ 0, aStream
,
867 //----------------------------------------------------------------------
869 // This thing is exported by libstdc++
870 // Yes, this is a gcc only hack
871 #if defined(MOZ_DEMANGLE_SYMBOLS)
873 #include <stdlib.h> // for free()
874 #endif // MOZ_DEMANGLE_SYMBOLS
877 nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol
,
881 NS_ASSERTION(nullptr != aSymbol
,"null symbol");
882 NS_ASSERTION(nullptr != aBuffer
,"null buffer");
883 NS_ASSERTION(aBufLen
>= 32 ,"pulled 32 out of you know where");
887 #if defined(MOZ_DEMANGLE_SYMBOLS)
888 /* See demangle.h in the gcc source for the voodoo */
889 char * demangled
= abi::__cxa_demangle(aSymbol
,0,0,0);
893 strncpy(aBuffer
,demangled
,aBufLen
);
896 #endif // MOZ_DEMANGLE_SYMBOLS
900 //----------------------------------------------------------------------
902 EXPORT_XPCOM_API(void)
905 // FIXME: This is called multiple times, we should probably not allow that.
906 #ifdef STACKWALKING_AVAILABLE
907 StackWalkInitCriticalAddress();
909 #ifdef NS_IMPL_REFCNT_LOGGING
911 nsTraceRefcntImpl::SetActivityIsLegal(true);
914 #ifdef NS_TRACE_MALLOC
915 // XXX we don't have to worry about shutting down trace-malloc; it
916 // handles this itself, through an atexit() callback.
917 if (!NS_TraceMallocHasStarted())
918 NS_TraceMallocStartup(-1); // -1 == no logging
922 EXPORT_XPCOM_API(void)
932 NS_ASSERTION(gInitCount
> 0,
933 "NS_LogTerm without matching NS_LogInit");
935 if (--gInitCount
== 0) {
937 /* FIXME bug 491977: This is only going to operate on the
938 * BlockingResourceBase which is compiled into
939 * libxul/libxpcom_core.so. Anyone using external linkage will
940 * have their own copy of BlockingResourceBase statics which will
941 * not be freed by this method.
943 * It sounds like what we really want is to be able to register a
944 * callback function to call at XPCOM shutdown. Note that with
945 * this solution, however, we need to guarantee that
946 * BlockingResourceBase::Shutdown() runs after all other shutdown
949 BlockingResourceBase::Shutdown();
953 nsTraceRefcntImpl::DumpStatistics();
954 nsTraceRefcntImpl::ResetStatistics();
956 nsTraceRefcntImpl::Shutdown();
957 #ifdef NS_IMPL_REFCNT_LOGGING
958 nsTraceRefcntImpl::SetActivityIsLegal(false);
959 gActivityTLS
= BAD_TLS_INDEX
;
964 } // namespace mozilla
966 EXPORT_XPCOM_API(void)
967 NS_LogAddRef(void* aPtr
, nsrefcnt aRefcnt
,
968 const char* aClazz
, uint32_t classSize
)
970 #ifdef NS_IMPL_REFCNT_LOGGING
971 ASSERT_ACTIVITY_IS_LEGAL
;
978 BloatEntry
* entry
= GetBloatEntry(aClazz
, classSize
);
980 entry
->AddRef(aRefcnt
);
984 // Here's the case where MOZ_COUNT_CTOR was not used,
985 // yet we still want to see creation information:
987 bool loggingThisType
= (!gTypesToLog
|| LogThisType(aClazz
));
988 intptr_t serialno
= 0;
989 if (gSerialNumbers
&& loggingThisType
) {
990 serialno
= GetSerialNumber(aPtr
, aRefcnt
== 1);
991 NS_ASSERTION(serialno
!= 0,
992 "Serial number requested for unrecognized pointer! "
993 "Are you memmoving a refcounted object?");
994 int32_t* count
= GetRefCount(aPtr
);
1000 bool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1001 if (aRefcnt
== 1 && gAllocLog
&& loggingThisType
&& loggingThisObject
) {
1002 fprintf(gAllocLog
, "\n<%s> 0x%08X %" PRIdPTR
" Create\n",
1003 aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
);
1004 nsTraceRefcntImpl::WalkTheStack(gAllocLog
);
1007 if (gRefcntsLog
&& loggingThisType
&& loggingThisObject
) {
1009 (*leakyLogAddRef
)(aPtr
, aRefcnt
- 1, aRefcnt
);
1012 // Can't use PR_LOG(), b/c it truncates the line
1013 fprintf(gRefcntsLog
,
1014 "\n<%s> 0x%08X %" PRIdPTR
" AddRef %d\n", aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
, aRefcnt
);
1015 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog
);
1016 fflush(gRefcntsLog
);
1024 EXPORT_XPCOM_API(void)
1025 NS_LogRelease(void* aPtr
, nsrefcnt aRefcnt
, const char* aClazz
)
1027 #ifdef NS_IMPL_REFCNT_LOGGING
1028 ASSERT_ACTIVITY_IS_LEGAL
;
1035 BloatEntry
* entry
= GetBloatEntry(aClazz
, 0);
1037 entry
->Release(aRefcnt
);
1041 bool loggingThisType
= (!gTypesToLog
|| LogThisType(aClazz
));
1042 intptr_t serialno
= 0;
1043 if (gSerialNumbers
&& loggingThisType
) {
1044 serialno
= GetSerialNumber(aPtr
, false);
1045 NS_ASSERTION(serialno
!= 0,
1046 "Serial number requested for unrecognized pointer! "
1047 "Are you memmoving a refcounted object?");
1048 int32_t* count
= GetRefCount(aPtr
);
1054 bool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1055 if (gRefcntsLog
&& loggingThisType
&& loggingThisObject
) {
1057 (*leakyLogRelease
)(aPtr
, aRefcnt
+ 1, aRefcnt
);
1060 // Can't use PR_LOG(), b/c it truncates the line
1061 fprintf(gRefcntsLog
,
1062 "\n<%s> 0x%08X %" PRIdPTR
" Release %d\n", aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
, aRefcnt
);
1063 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog
);
1064 fflush(gRefcntsLog
);
1068 // Here's the case where MOZ_COUNT_DTOR was not used,
1069 // yet we still want to see deletion information:
1071 if (aRefcnt
== 0 && gAllocLog
&& loggingThisType
&& loggingThisObject
) {
1073 "\n<%s> 0x%08X %" PRIdPTR
" Destroy\n",
1074 aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
);
1075 nsTraceRefcntImpl::WalkTheStack(gAllocLog
);
1078 if (aRefcnt
== 0 && gSerialNumbers
&& loggingThisType
) {
1079 RecycleSerialNumberPtr(aPtr
);
1087 EXPORT_XPCOM_API(void)
1088 NS_LogCtor(void* aPtr
, const char* aType
, uint32_t aInstanceSize
)
1090 #ifdef NS_IMPL_REFCNT_LOGGING
1091 ASSERT_ACTIVITY_IS_LEGAL
;
1099 BloatEntry
* entry
= GetBloatEntry(aType
, aInstanceSize
);
1105 bool loggingThisType
= (!gTypesToLog
|| LogThisType(aType
));
1106 intptr_t serialno
= 0;
1107 if (gSerialNumbers
&& loggingThisType
) {
1108 serialno
= GetSerialNumber(aPtr
, true);
1111 bool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1112 if (gAllocLog
&& loggingThisType
&& loggingThisObject
) {
1113 fprintf(gAllocLog
, "\n<%s> 0x%08X %" PRIdPTR
" Ctor (%d)\n",
1114 aType
, NS_PTR_TO_INT32(aPtr
), serialno
, aInstanceSize
);
1115 nsTraceRefcntImpl::WalkTheStack(gAllocLog
);
1124 EXPORT_XPCOM_API(void)
1125 NS_LogDtor(void* aPtr
, const char* aType
, uint32_t aInstanceSize
)
1127 #ifdef NS_IMPL_REFCNT_LOGGING
1128 ASSERT_ACTIVITY_IS_LEGAL
;
1136 BloatEntry
* entry
= GetBloatEntry(aType
, aInstanceSize
);
1142 bool loggingThisType
= (!gTypesToLog
|| LogThisType(aType
));
1143 intptr_t serialno
= 0;
1144 if (gSerialNumbers
&& loggingThisType
) {
1145 serialno
= GetSerialNumber(aPtr
, false);
1146 RecycleSerialNumberPtr(aPtr
);
1149 bool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1151 // (If we're on a losing architecture, don't do this because we'll be
1152 // using LogDeleteXPCOM instead to get file and line numbers.)
1153 if (gAllocLog
&& loggingThisType
&& loggingThisObject
) {
1154 fprintf(gAllocLog
, "\n<%s> 0x%08X %" PRIdPTR
" Dtor (%d)\n",
1155 aType
, NS_PTR_TO_INT32(aPtr
), serialno
, aInstanceSize
);
1156 nsTraceRefcntImpl::WalkTheStack(gAllocLog
);
1165 EXPORT_XPCOM_API(void)
1166 NS_LogCOMPtrAddRef(void* aCOMPtr
, nsISupports
* aObject
)
1168 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1169 // Get the most-derived object.
1170 void *object
= dynamic_cast<void *>(aObject
);
1172 // This is a very indirect way of finding out what the class is
1173 // of the object being logged. If we're logging a specific type,
1175 if (!gTypesToLog
|| !gSerialNumbers
) {
1178 intptr_t serialno
= GetSerialNumber(object
, false);
1179 if (serialno
== 0) {
1188 int32_t* count
= GetCOMPtrCount(object
);
1192 bool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1194 if (gCOMPtrLog
&& loggingThisObject
) {
1195 fprintf(gCOMPtrLog
, "\n<?> 0x%08X %" PRIdPTR
" nsCOMPtrAddRef %d 0x%08X\n",
1196 NS_PTR_TO_INT32(object
), serialno
, count
?(*count
):-1, NS_PTR_TO_INT32(aCOMPtr
));
1197 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog
);
1206 EXPORT_XPCOM_API(void)
1207 NS_LogCOMPtrRelease(void* aCOMPtr
, nsISupports
* aObject
)
1209 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1210 // Get the most-derived object.
1211 void *object
= dynamic_cast<void *>(aObject
);
1213 // This is a very indirect way of finding out what the class is
1214 // of the object being logged. If we're logging a specific type,
1216 if (!gTypesToLog
|| !gSerialNumbers
) {
1219 intptr_t serialno
= GetSerialNumber(object
, false);
1220 if (serialno
== 0) {
1229 int32_t* count
= GetCOMPtrCount(object
);
1233 bool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1235 if (gCOMPtrLog
&& loggingThisObject
) {
1236 fprintf(gCOMPtrLog
, "\n<?> 0x%08X %" PRIdPTR
" nsCOMPtrRelease %d 0x%08X\n",
1237 NS_PTR_TO_INT32(object
), serialno
, count
?(*count
):-1, NS_PTR_TO_INT32(aCOMPtr
));
1238 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog
);
1247 nsTraceRefcntImpl::Startup()
1251 static void maybeUnregisterAndCloseFile(FILE *&f
) {
1255 MozillaUnRegisterDebugFILE(f
);
1261 nsTraceRefcntImpl::Shutdown()
1263 #ifdef NS_IMPL_REFCNT_LOGGING
1266 PL_HashTableDestroy(gBloatView
);
1267 gBloatView
= nullptr;
1270 PL_HashTableDestroy(gTypesToLog
);
1271 gTypesToLog
= nullptr;
1273 if (gObjectsToLog
) {
1274 PL_HashTableDestroy(gObjectsToLog
);
1275 gObjectsToLog
= nullptr;
1277 if (gSerialNumbers
) {
1278 PL_HashTableDestroy(gSerialNumbers
);
1279 gSerialNumbers
= nullptr;
1281 maybeUnregisterAndCloseFile(gBloatLog
);
1282 maybeUnregisterAndCloseFile(gRefcntsLog
);
1283 maybeUnregisterAndCloseFile(gAllocLog
);
1284 maybeUnregisterAndCloseFile(gLeakyLog
);
1285 maybeUnregisterAndCloseFile(gCOMPtrLog
);
1290 nsTraceRefcntImpl::SetActivityIsLegal(bool aLegal
)
1292 #ifdef NS_IMPL_REFCNT_LOGGING
1293 if (gActivityTLS
== BAD_TLS_INDEX
)
1294 PR_NewThreadPrivateIndex(&gActivityTLS
, nullptr);
1296 PR_SetThreadPrivate(gActivityTLS
, NS_INT32_TO_PTR(!aLegal
));
1300 NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl
, nsITraceRefcnt
)
1302 NS_IMETHODIMP_(nsrefcnt
) nsTraceRefcntImpl::AddRef(void)
1307 NS_IMETHODIMP_(nsrefcnt
) nsTraceRefcntImpl::Release(void)
1313 nsTraceRefcntImpl::LogAddRef(void *aPtr
, nsrefcnt aNewRefcnt
,
1314 const char *aTypeName
, uint32_t aSize
)
1316 NS_LogAddRef(aPtr
, aNewRefcnt
, aTypeName
, aSize
);
1321 nsTraceRefcntImpl::LogRelease(void *aPtr
, nsrefcnt aNewRefcnt
,
1322 const char *aTypeName
)
1324 NS_LogRelease(aPtr
, aNewRefcnt
, aTypeName
);
1329 nsTraceRefcntImpl::LogCtor(void *aPtr
, const char *aTypeName
, uint32_t aSize
)
1331 NS_LogCtor(aPtr
, aTypeName
, aSize
);
1336 nsTraceRefcntImpl::LogDtor(void *aPtr
, const char *aTypeName
, uint32_t aSize
)
1338 NS_LogDtor(aPtr
, aTypeName
, aSize
);
1343 nsTraceRefcntImpl::LogAddCOMPtr(void *aCOMPtr
, nsISupports
* aObject
)
1345 NS_LogCOMPtrAddRef(aCOMPtr
, aObject
);
1350 nsTraceRefcntImpl::LogReleaseCOMPtr(void *aCOMPtr
, nsISupports
* aObject
)
1352 NS_LogCOMPtrRelease(aCOMPtr
, aObject
);
1356 static const nsTraceRefcntImpl kTraceRefcntImpl
;
1359 nsTraceRefcntImpl::Create(nsISupports
* outer
, const nsIID
& aIID
, void* *aInstancePtr
)
1361 return const_cast<nsTraceRefcntImpl
*>(&kTraceRefcntImpl
)->
1362 QueryInterface(aIID
, aInstancePtr
);