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"
40 #include "nsXPCOMPrivate.h"
42 #include "nsISupports.h"
53 #include "nsStackWalk.h"
57 #include "nsXULAppAPI.h"
60 #define getpid _getpid
66 #ifdef NS_TRACE_MALLOC
67 #include "nsTraceMalloc.h"
70 #include "mozilla/BlockingResourceBase.h"
76 ////////////////////////////////////////////////////////////////////////////////
79 NS_MeanAndStdDev(double n
, double sumOfValues
, double sumOfSquaredValues
,
80 double *meanResult
, double *stdDevResult
)
82 double mean
= 0.0, var
= 0.0, stdDev
= 0.0;
83 if (n
> 0.0 && sumOfValues
>= 0) {
84 mean
= sumOfValues
/ n
;
85 double temp
= (n
* sumOfSquaredValues
) - (sumOfValues
* sumOfValues
);
86 if (temp
< 0.0 || n
<= 1)
89 var
= temp
/ (n
* (n
- 1));
90 // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
91 stdDev
= var
!= 0.0 ? sqrt(var
) : 0.0;
94 *stdDevResult
= stdDev
;
97 ////////////////////////////////////////////////////////////////////////////////
99 #define NS_IMPL_REFCNT_LOGGING
101 #ifdef NS_IMPL_REFCNT_LOGGING
107 static PRLock
* gTraceLock
;
109 #define LOCK_TRACELOG() PR_Lock(gTraceLock)
110 #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
112 static PLHashTable
* gBloatView
;
113 static PLHashTable
* gTypesToLog
;
114 static PLHashTable
* gObjectsToLog
;
115 static PLHashTable
* gSerialNumbers
;
116 static PRInt32 gNextSerialNumber
;
118 static PRBool gLogging
;
119 static PRBool gLogToLeaky
;
120 static PRBool gLogLeaksOnly
;
122 static void (*leakyLogAddRef
)(void* p
, int oldrc
, int newrc
);
123 static void (*leakyLogRelease
)(void* p
, int oldrc
, int newrc
);
125 #define BAD_TLS_INDEX ((PRUintn) -1)
127 // if gActivityTLS == BAD_TLS_INDEX, then we're
128 // unitialized... otherwise this points to a NSPR TLS thread index
129 // indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
130 // activity is ok, otherwise not!
131 static PRUintn gActivityTLS
= BAD_TLS_INDEX
;
133 static PRBool gInitialized
;
134 static nsrefcnt gInitCount
;
136 static FILE *gBloatLog
= nsnull
;
137 static FILE *gRefcntsLog
= nsnull
;
138 static FILE *gAllocLog
= nsnull
;
139 static FILE *gLeakyLog
= nsnull
;
140 static FILE *gCOMPtrLog
= nsnull
;
142 struct serialNumberRecord
{
143 PRInt32 serialNumber
;
148 struct nsTraceRefcntStats
{
153 double mRefsOutstandingTotal
;
154 double mRefsOutstandingSquared
;
155 double mObjsOutstandingTotal
;
156 double mObjsOutstandingSquared
;
159 // I hope to turn this on for everybody once we hit it a little less.
161 static const char kStaticCtorDtorWarning
[] =
162 "XPCOM objects created/destroyed from static ctor/dtor";
165 AssertActivityIsLegal()
167 if (gActivityTLS
== BAD_TLS_INDEX
||
168 NS_PTR_TO_INT32(PR_GetThreadPrivate(gActivityTLS
)) != 0) {
169 if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) {
170 NS_RUNTIMEABORT(kStaticCtorDtorWarning
);
172 NS_WARNING(kStaticCtorDtorWarning
);
176 # define ASSERT_ACTIVITY_IS_LEGAL \
178 AssertActivityIsLegal(); \
181 # define ASSERT_ACTIVITY_IS_LEGAL PR_BEGIN_MACRO PR_END_MACRO
184 // These functions are copied from nsprpub/lib/ds/plhash.c, with changes
185 // to the functions not called Default* to free the serialNumberRecord or
189 DefaultAllocTable(void *pool
, PRSize size
)
191 return PR_MALLOC(size
);
195 DefaultFreeTable(void *pool
, void *item
)
201 DefaultAllocEntry(void *pool
, const void *key
)
203 return PR_NEW(PLHashEntry
);
207 SerialNumberFreeEntry(void *pool
, PLHashEntry
*he
, PRUintn flag
)
209 if (flag
== HT_FREE_ENTRY
) {
210 PR_Free(reinterpret_cast<serialNumberRecord
*>(he
->value
));
216 TypesToLogFreeEntry(void *pool
, PLHashEntry
*he
, PRUintn flag
)
218 if (flag
== HT_FREE_ENTRY
) {
219 nsCRT::free(const_cast<char*>
220 (reinterpret_cast<const char*>(he
->key
)));
225 static const PLHashAllocOps serialNumberHashAllocOps
= {
226 DefaultAllocTable
, DefaultFreeTable
,
227 DefaultAllocEntry
, SerialNumberFreeEntry
230 static const PLHashAllocOps typesToLogHashAllocOps
= {
231 DefaultAllocTable
, DefaultFreeTable
,
232 DefaultAllocEntry
, TypesToLogFreeEntry
235 ////////////////////////////////////////////////////////////////////////////////
239 BloatEntry(const char* className
, PRUint32 classSize
)
240 : mClassSize(classSize
) {
241 mClassName
= PL_strdup(className
);
248 PL_strfree(mClassName
);
251 PRUint32
GetClassSize() { return (PRUint32
)mClassSize
; }
252 const char* GetClassName() { return mClassName
; }
254 static void Clear(nsTraceRefcntStats
* stats
) {
256 stats
->mReleases
= 0;
258 stats
->mDestroys
= 0;
259 stats
->mRefsOutstandingTotal
= 0;
260 stats
->mRefsOutstandingSquared
= 0;
261 stats
->mObjsOutstandingTotal
= 0;
262 stats
->mObjsOutstandingSquared
= 0;
266 mAllStats
.mAddRefs
+= mNewStats
.mAddRefs
;
267 mAllStats
.mReleases
+= mNewStats
.mReleases
;
268 mAllStats
.mCreates
+= mNewStats
.mCreates
;
269 mAllStats
.mDestroys
+= mNewStats
.mDestroys
;
270 mAllStats
.mRefsOutstandingTotal
+= mNewStats
.mRefsOutstandingTotal
;
271 mAllStats
.mRefsOutstandingSquared
+= mNewStats
.mRefsOutstandingSquared
;
272 mAllStats
.mObjsOutstandingTotal
+= mNewStats
.mObjsOutstandingTotal
;
273 mAllStats
.mObjsOutstandingSquared
+= mNewStats
.mObjsOutstandingSquared
;
277 void AddRef(nsrefcnt refcnt
) {
278 mNewStats
.mAddRefs
++;
285 void Release(nsrefcnt refcnt
) {
286 mNewStats
.mReleases
++;
294 mNewStats
.mCreates
++;
299 mNewStats
.mDestroys
++;
304 PRUint64 cnt
= (mNewStats
.mAddRefs
- mNewStats
.mReleases
);
305 mNewStats
.mRefsOutstandingTotal
+= cnt
;
306 mNewStats
.mRefsOutstandingSquared
+= cnt
* cnt
;
310 PRUint64 cnt
= (mNewStats
.mCreates
- mNewStats
.mDestroys
);
311 mNewStats
.mObjsOutstandingTotal
+= cnt
;
312 mNewStats
.mObjsOutstandingSquared
+= cnt
* cnt
;
315 static PRIntn
DumpEntry(PLHashEntry
*he
, PRIntn i
, void *arg
) {
316 BloatEntry
* entry
= (BloatEntry
*)he
->value
;
319 static_cast<nsTArray
<BloatEntry
*>*>(arg
)->AppendElement(entry
);
321 return HT_ENUMERATE_NEXT
;
324 static PRIntn
TotalEntries(PLHashEntry
*he
, PRIntn i
, void *arg
) {
325 BloatEntry
* entry
= (BloatEntry
*)he
->value
;
326 if (entry
&& nsCRT::strcmp(entry
->mClassName
, "TOTAL") != 0) {
327 entry
->Total((BloatEntry
*)arg
);
329 return HT_ENUMERATE_NEXT
;
332 void Total(BloatEntry
* total
) {
333 total
->mAllStats
.mAddRefs
+= mNewStats
.mAddRefs
+ mAllStats
.mAddRefs
;
334 total
->mAllStats
.mReleases
+= mNewStats
.mReleases
+ mAllStats
.mReleases
;
335 total
->mAllStats
.mCreates
+= mNewStats
.mCreates
+ mAllStats
.mCreates
;
336 total
->mAllStats
.mDestroys
+= mNewStats
.mDestroys
+ mAllStats
.mDestroys
;
337 total
->mAllStats
.mRefsOutstandingTotal
+= mNewStats
.mRefsOutstandingTotal
+ mAllStats
.mRefsOutstandingTotal
;
338 total
->mAllStats
.mRefsOutstandingSquared
+= mNewStats
.mRefsOutstandingSquared
+ mAllStats
.mRefsOutstandingSquared
;
339 total
->mAllStats
.mObjsOutstandingTotal
+= mNewStats
.mObjsOutstandingTotal
+ mAllStats
.mObjsOutstandingTotal
;
340 total
->mAllStats
.mObjsOutstandingSquared
+= mNewStats
.mObjsOutstandingSquared
+ mAllStats
.mObjsOutstandingSquared
;
341 PRUint64 count
= (mNewStats
.mCreates
+ mAllStats
.mCreates
);
342 total
->mClassSize
+= mClassSize
* count
; // adjust for average in DumpTotal
343 total
->mTotalLeaked
+= (PRUint64
)(mClassSize
*
344 ((mNewStats
.mCreates
+ mAllStats
.mCreates
)
345 -(mNewStats
.mDestroys
+ mAllStats
.mDestroys
)));
348 void DumpTotal(FILE* out
) {
349 mClassSize
/= mAllStats
.mCreates
;
350 Dump(-1, out
, nsTraceRefcntImpl::ALL_STATS
);
353 static PRBool
HaveLeaks(nsTraceRefcntStats
* stats
) {
354 return ((stats
->mAddRefs
!= stats
->mReleases
) ||
355 (stats
->mCreates
!= stats
->mDestroys
));
358 PRBool
PrintDumpHeader(FILE* out
, const char* msg
, nsTraceRefcntImpl::StatisticsType type
) {
360 fprintf(out
, "\n== BloatView: %s, %s process %d\n", msg
,
361 XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
363 fprintf(out
, "\n== BloatView: %s\n", msg
);
365 nsTraceRefcntStats
& stats
=
366 (type
== nsTraceRefcntImpl::NEW_STATS
) ? mNewStats
: mAllStats
;
367 if (gLogLeaksOnly
&& !HaveLeaks(&stats
))
372 " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \
373 " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
375 this->DumpTotal(out
);
380 void Dump(PRIntn i
, FILE* out
, nsTraceRefcntImpl::StatisticsType type
) {
381 nsTraceRefcntStats
* stats
= (type
== nsTraceRefcntImpl::NEW_STATS
) ? &mNewStats
: &mAllStats
;
382 if (gLogLeaksOnly
&& !HaveLeaks(stats
)) {
386 double meanRefs
, stddevRefs
;
387 NS_MeanAndStdDev(stats
->mAddRefs
+ stats
->mReleases
,
388 stats
->mRefsOutstandingTotal
,
389 stats
->mRefsOutstandingSquared
,
390 &meanRefs
, &stddevRefs
);
392 double meanObjs
, stddevObjs
;
393 NS_MeanAndStdDev(stats
->mCreates
+ stats
->mDestroys
,
394 stats
->mObjsOutstandingTotal
,
395 stats
->mObjsOutstandingSquared
,
396 &meanObjs
, &stddevObjs
);
398 if ((stats
->mAddRefs
- stats
->mReleases
) != 0 ||
399 stats
->mAddRefs
!= 0 ||
402 (stats
->mCreates
- stats
->mDestroys
) != 0 ||
403 stats
->mCreates
!= 0 ||
406 fprintf(out
, "%4d %-40.40s %8d %8llu %8llu %8llu (%8.2f +/- %8.2f) %8llu %8llu (%8.2f +/- %8.2f)\n",
409 (nsCRT::strcmp(mClassName
, "TOTAL"))
410 ?(PRUint64
)((stats
->mCreates
- stats
->mDestroys
) * mClassSize
)
413 (stats
->mCreates
- stats
->mDestroys
),
417 (stats
->mAddRefs
- stats
->mReleases
),
425 double mClassSize
; // this is stored as a double because of the way we compute the avg class size for total bloat
426 PRUint64 mTotalLeaked
; // used only for TOTAL entry
427 nsTraceRefcntStats mNewStats
;
428 nsTraceRefcntStats mAllStats
;
432 BloatViewFreeEntry(void *pool
, PLHashEntry
*he
, PRUintn flag
)
434 if (flag
== HT_FREE_ENTRY
) {
435 BloatEntry
* entry
= reinterpret_cast<BloatEntry
*>(he
->value
);
441 const static PLHashAllocOps bloatViewHashAllocOps
= {
442 DefaultAllocTable
, DefaultFreeTable
,
443 DefaultAllocEntry
, BloatViewFreeEntry
449 gBloatView
= PL_NewHashTable(256,
453 &bloatViewHashAllocOps
, NULL
);
457 GetBloatEntry(const char* aTypeName
, PRUint32 aInstanceSize
)
462 BloatEntry
* entry
= NULL
;
464 entry
= (BloatEntry
*)PL_HashTableLookup(gBloatView
, aTypeName
);
465 if (entry
== NULL
&& aInstanceSize
> 0) {
467 entry
= new BloatEntry(aTypeName
, aInstanceSize
);
468 PLHashEntry
* e
= PL_HashTableAdd(gBloatView
, aTypeName
, entry
);
474 NS_ASSERTION(aInstanceSize
== 0 ||
475 entry
->GetClassSize() == aInstanceSize
,
476 "bad size recorded");
482 static PRIntn
DumpSerialNumbers(PLHashEntry
* aHashEntry
, PRIntn aIndex
, void* aClosure
)
484 serialNumberRecord
* record
= reinterpret_cast<serialNumberRecord
*>(aHashEntry
->value
);
485 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
486 fprintf((FILE*) aClosure
, "%d @%p (%d references; %d from COMPtrs)\n",
487 record
->serialNumber
,
488 NS_INT32_TO_PTR(aHashEntry
->key
),
490 record
->COMPtrCount
);
492 fprintf((FILE*) aClosure
, "%d @%p (%d references)\n",
493 record
->serialNumber
,
494 NS_INT32_TO_PTR(aHashEntry
->key
),
497 return HT_ENUMERATE_NEXT
;
501 NS_SPECIALIZE_TEMPLATE
502 class nsDefaultComparator
<BloatEntry
*, BloatEntry
*> {
504 PRBool
Equals(BloatEntry
* const& aA
, BloatEntry
* const& aB
) const {
505 return PL_strcmp(aA
->GetClassName(), aB
->GetClassName()) == 0;
507 PRBool
LessThan(BloatEntry
* const& aA
, BloatEntry
* const& aB
) const {
508 return PL_strcmp(aA
->GetClassName(), aB
->GetClassName()) < 0;
512 #endif /* NS_IMPL_REFCNT_LOGGING */
515 nsTraceRefcntImpl::DumpStatistics(StatisticsType type
, FILE* out
)
517 #ifdef NS_IMPL_REFCNT_LOGGING
518 if (gBloatLog
== nsnull
|| gBloatView
== nsnull
) {
519 return NS_ERROR_FAILURE
;
527 PRBool wasLogging
= gLogging
;
528 gLogging
= PR_FALSE
; // turn off logging for this method
530 BloatEntry
total("TOTAL", 0);
531 PL_HashTableEnumerateEntries(gBloatView
, BloatEntry::TotalEntries
, &total
);
533 if (type
== NEW_STATS
) {
535 msg
= "NEW (incremental) LEAK STATISTICS";
537 msg
= "NEW (incremental) LEAK AND BLOAT STATISTICS";
541 msg
= "ALL (cumulative) LEAK STATISTICS";
543 msg
= "ALL (cumulative) LEAK AND BLOAT STATISTICS";
545 const PRBool leaked
= total
.PrintDumpHeader(out
, msg
, type
);
547 nsTArray
<BloatEntry
*> entries
;
548 PL_HashTableEnumerateEntries(gBloatView
, BloatEntry::DumpEntry
, &entries
);
549 const PRUint32 count
= entries
.Length();
551 if (!gLogLeaksOnly
|| leaked
) {
552 // Sort the entries alphabetically by classname.
555 for (PRUint32 i
= 0; i
< count
; ++i
) {
556 BloatEntry
* entry
= entries
[i
];
557 entry
->Dump(i
, out
, type
);
563 fprintf(out
, "nsTraceRefcntImpl::DumpStatistics: %d entries\n", count
);
565 if (gSerialNumbers
) {
566 fprintf(out
, "\nSerial Numbers of Leaked Objects:\n");
567 PL_HashTableEnumerateEntries(gSerialNumbers
, DumpSerialNumbers
, out
);
570 gLogging
= wasLogging
;
578 nsTraceRefcntImpl::ResetStatistics()
580 #ifdef NS_IMPL_REFCNT_LOGGING
583 PL_HashTableDestroy(gBloatView
);
590 #ifdef NS_IMPL_REFCNT_LOGGING
591 static PRBool
LogThisType(const char* aTypeName
)
593 void* he
= PL_HashTableLookup(gTypesToLog
, aTypeName
);
597 static PRInt32
GetSerialNumber(void* aPtr
, PRBool aCreate
)
599 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
601 return PRInt32((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->serialNumber
);
604 serialNumberRecord
*record
= PR_NEW(serialNumberRecord
);
605 record
->serialNumber
= ++gNextSerialNumber
;
606 record
->refCount
= 0;
607 record
->COMPtrCount
= 0;
608 PL_HashTableRawAdd(gSerialNumbers
, hep
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
, reinterpret_cast<void*>(record
));
609 return gNextSerialNumber
;
616 static PRInt32
* GetRefCount(void* aPtr
)
618 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
620 return &((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->refCount
);
626 static PRInt32
* GetCOMPtrCount(void* aPtr
)
628 PLHashEntry
** hep
= PL_HashTableRawLookup(gSerialNumbers
, PLHashNumber(NS_PTR_TO_INT32(aPtr
)), aPtr
);
630 return &((reinterpret_cast<serialNumberRecord
*>((*hep
)->value
))->COMPtrCount
);
636 static void RecycleSerialNumberPtr(void* aPtr
)
638 PL_HashTableRemove(gSerialNumbers
, aPtr
);
641 static PRBool
LogThisObj(PRInt32 aSerialNumber
)
643 return nsnull
!= PL_HashTableLookup(gObjectsToLog
, (const void*)(aSerialNumber
));
647 #define FOPEN_NO_INHERIT "N"
649 #define FOPEN_NO_INHERIT
652 static PRBool
InitLog(const char* envVar
, const char* msg
, FILE* *result
)
654 const char* value
= getenv(envVar
);
656 if (nsCRT::strcmp(value
, "1") == 0) {
658 fprintf(stdout
, "### %s defined -- logging %s to stdout\n",
662 else if (nsCRT::strcmp(value
, "2") == 0) {
664 fprintf(stdout
, "### %s defined -- logging %s to stderr\n",
670 nsCAutoString
fname(value
);
672 if (XRE_GetProcessType() != GeckoProcessType_Default
) {
673 bool hasLogExtension
=
674 fname
.RFind(".log", PR_TRUE
, -1, 4) == kNotFound
? false : true;
676 fname
.Cut(fname
.Length() - 4, 4);
677 fname
.AppendLiteral("_");
678 fname
.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType()));
679 fname
.AppendLiteral("_pid");
680 fname
.AppendInt((PRUint32
)getpid());
682 fname
.AppendLiteral(".log");
685 stream
= ::fopen(fname
.get(), "w" FOPEN_NO_INHERIT
);
686 if (stream
!= NULL
) {
688 fprintf(stdout
, "### %s defined -- logging %s to %s\n",
689 envVar
, msg
, fname
.get());
692 fprintf(stdout
, "### %s defined -- unable to log %s to %s\n",
693 envVar
, msg
, fname
.get());
695 return stream
!= NULL
;
702 static PLHashNumber
HashNumber(const void* aKey
)
704 return PLHashNumber(NS_PTR_TO_INT32(aKey
));
707 static void InitTraceLog(void)
709 if (gInitialized
) return;
710 gInitialized
= PR_TRUE
;
713 defined
= InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog
);
715 gLogLeaksOnly
= InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog
);
716 if (defined
|| gLogLeaksOnly
) {
719 NS_WARNING("out of memory");
721 gLogLeaksOnly
= PR_FALSE
;
725 (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog
);
727 (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog
);
729 defined
= InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog
);
731 gLogToLeaky
= PR_TRUE
;
732 PRFuncPtr p
= nsnull
, q
= nsnull
;
735 PRLibrary
*lib
= nsnull
;
736 p
= PR_FindFunctionSymbolAndLibrary("__log_addref", &lib
);
738 PR_UnloadLibrary(lib
);
741 q
= PR_FindFunctionSymbolAndLibrary("__log_release", &lib
);
743 PR_UnloadLibrary(lib
);
748 leakyLogAddRef
= (void (*)(void*,int,int)) p
;
749 leakyLogRelease
= (void (*)(void*,int,int)) q
;
752 gLogToLeaky
= PR_FALSE
;
753 fprintf(stdout
, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
758 const char* classes
= getenv("XPCOM_MEM_LOG_CLASSES");
760 #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
762 (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog
);
764 if (getenv("XPCOM_MEM_COMPTR_LOG")) {
765 fprintf(stdout
, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
769 const char* comptr_log
= getenv("XPCOM_MEM_COMPTR_LOG");
771 fprintf(stdout
, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
776 // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
777 // as a list of class names to track
778 gTypesToLog
= PL_NewHashTable(256,
782 &typesToLogHashAllocOps
, NULL
);
784 NS_WARNING("out of memory");
785 fprintf(stdout
, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
788 fprintf(stdout
, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
789 const char* cp
= classes
;
791 char* cm
= (char*) strchr(cp
, ',');
795 PL_HashTableAdd(gTypesToLog
, nsCRT::strdup(cp
), (void*)1);
796 fprintf(stdout
, "%s ", cp
);
801 fprintf(stdout
, "\n");
804 gSerialNumbers
= PL_NewHashTable(256,
808 &serialNumberHashAllocOps
, NULL
);
813 const char* objects
= getenv("XPCOM_MEM_LOG_OBJECTS");
815 gObjectsToLog
= PL_NewHashTable(256,
821 if (!gObjectsToLog
) {
822 NS_WARNING("out of memory");
823 fprintf(stdout
, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
825 else if (! (gRefcntsLog
|| gAllocLog
|| gCOMPtrLog
)) {
826 fprintf(stdout
, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
829 fprintf(stdout
, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
830 const char* cp
= objects
;
832 char* cm
= (char*) strchr(cp
, ',');
851 for(PRInt32 serialno
= bottom
; serialno
<= top
; serialno
++) {
852 PL_HashTableAdd(gObjectsToLog
, (const void*)serialno
, (void*)1);
853 fprintf(stdout
, "%d ", serialno
);
859 fprintf(stdout
, "\n");
864 if (gBloatLog
|| gRefcntsLog
|| gAllocLog
|| gLeakyLog
|| gCOMPtrLog
) {
868 gTraceLock
= PR_NewLock();
875 static void PrintStackFrame(void *aPC
, void *aClosure
)
877 FILE *stream
= (FILE*)aClosure
;
878 nsCodeAddressDetails details
;
881 NS_DescribeCodeAddress(aPC
, &details
);
882 NS_FormatCodeAddressDetails(aPC
, &details
, buf
, sizeof(buf
));
889 nsTraceRefcntImpl::WalkTheStack(FILE* aStream
)
891 NS_StackWalk(PrintStackFrame
, 2, aStream
);
894 //----------------------------------------------------------------------
896 // This thing is exported by libstdc++
897 // Yes, this is a gcc only hack
898 #if defined(MOZ_DEMANGLE_SYMBOLS)
900 #include <stdlib.h> // for free()
901 #endif // MOZ_DEMANGLE_SYMBOLS
904 nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol
,
908 NS_ASSERTION(nsnull
!= aSymbol
,"null symbol");
909 NS_ASSERTION(nsnull
!= aBuffer
,"null buffer");
910 NS_ASSERTION(aBufLen
>= 32 ,"pulled 32 out of you know where");
914 #if defined(MOZ_DEMANGLE_SYMBOLS)
915 /* See demangle.h in the gcc source for the voodoo */
916 char * demangled
= abi::__cxa_demangle(aSymbol
,0,0,0);
920 strncpy(aBuffer
,demangled
,aBufLen
);
923 #endif // MOZ_DEMANGLE_SYMBOLS
927 //----------------------------------------------------------------------
929 EXPORT_XPCOM_API(void)
932 #ifdef NS_IMPL_REFCNT_LOGGING
934 nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE
);
937 #ifdef NS_TRACE_MALLOC
938 // XXX we don't have to worry about shutting down trace-malloc; it
939 // handles this itself, through an atexit() callback.
940 if (!NS_TraceMallocHasStarted())
941 NS_TraceMallocStartup(-1); // -1 == no logging
945 EXPORT_XPCOM_API(void)
955 NS_ASSERTION(gInitCount
> 0,
956 "NS_LogTerm without matching NS_LogInit");
958 if (--gInitCount
== 0) {
960 /* FIXME bug 491977: This is only going to operate on the
961 * BlockingResourceBase which is compiled into
962 * libxul/libxpcom_core.so. Anyone using external linkage will
963 * have their own copy of BlockingResourceBase statics which will
964 * not be freed by this method.
966 * It sounds like what we really want is to be able to register a
967 * callback function to call at XPCOM shutdown. Note that with
968 * this solution, however, we need to guarantee that
969 * BlockingResourceBase::Shutdown() runs after all other shutdown
972 BlockingResourceBase::Shutdown();
976 nsTraceRefcntImpl::DumpStatistics();
977 nsTraceRefcntImpl::ResetStatistics();
979 nsTraceRefcntImpl::Shutdown();
980 #ifdef NS_IMPL_REFCNT_LOGGING
981 nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE
);
982 gActivityTLS
= BAD_TLS_INDEX
;
987 } // namespace mozilla
989 EXPORT_XPCOM_API(void)
990 NS_LogAddRef(void* aPtr
, nsrefcnt aRefcnt
,
991 const char* aClazz
, PRUint32 classSize
)
993 #ifdef NS_IMPL_REFCNT_LOGGING
994 ASSERT_ACTIVITY_IS_LEGAL
;
1001 BloatEntry
* entry
= GetBloatEntry(aClazz
, classSize
);
1003 entry
->AddRef(aRefcnt
);
1007 // Here's the case where MOZ_COUNT_CTOR was not used,
1008 // yet we still want to see creation information:
1010 PRBool loggingThisType
= (!gTypesToLog
|| LogThisType(aClazz
));
1011 PRInt32 serialno
= 0;
1012 if (gSerialNumbers
&& loggingThisType
) {
1013 serialno
= GetSerialNumber(aPtr
, aRefcnt
== 1);
1014 NS_ASSERTION(serialno
!= 0,
1015 "Serial number requested for unrecognized pointer! "
1016 "Are you memmoving a refcounted object?");
1017 PRInt32
* count
= GetRefCount(aPtr
);
1023 PRBool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1024 if (aRefcnt
== 1 && gAllocLog
&& loggingThisType
&& loggingThisObject
) {
1025 fprintf(gAllocLog
, "\n<%s> 0x%08X %d Create\n",
1026 aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
);
1027 nsTraceRefcntImpl::WalkTheStack(gAllocLog
);
1030 if (gRefcntsLog
&& loggingThisType
&& loggingThisObject
) {
1032 (*leakyLogAddRef
)(aPtr
, aRefcnt
- 1, aRefcnt
);
1035 // Can't use PR_LOG(), b/c it truncates the line
1036 fprintf(gRefcntsLog
,
1037 "\n<%s> 0x%08X %d AddRef %d\n", aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
, aRefcnt
);
1038 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog
);
1039 fflush(gRefcntsLog
);
1047 EXPORT_XPCOM_API(void)
1048 NS_LogRelease(void* aPtr
, nsrefcnt aRefcnt
, const char* aClazz
)
1050 #ifdef NS_IMPL_REFCNT_LOGGING
1051 ASSERT_ACTIVITY_IS_LEGAL
;
1058 BloatEntry
* entry
= GetBloatEntry(aClazz
, 0);
1060 entry
->Release(aRefcnt
);
1064 PRBool loggingThisType
= (!gTypesToLog
|| LogThisType(aClazz
));
1065 PRInt32 serialno
= 0;
1066 if (gSerialNumbers
&& loggingThisType
) {
1067 serialno
= GetSerialNumber(aPtr
, PR_FALSE
);
1068 NS_ASSERTION(serialno
!= 0,
1069 "Serial number requested for unrecognized pointer! "
1070 "Are you memmoving a refcounted object?");
1071 PRInt32
* count
= GetRefCount(aPtr
);
1077 PRBool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1078 if (gRefcntsLog
&& loggingThisType
&& loggingThisObject
) {
1080 (*leakyLogRelease
)(aPtr
, aRefcnt
+ 1, aRefcnt
);
1083 // Can't use PR_LOG(), b/c it truncates the line
1084 fprintf(gRefcntsLog
,
1085 "\n<%s> 0x%08X %d Release %d\n", aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
, aRefcnt
);
1086 nsTraceRefcntImpl::WalkTheStack(gRefcntsLog
);
1087 fflush(gRefcntsLog
);
1091 // Here's the case where MOZ_COUNT_DTOR was not used,
1092 // yet we still want to see deletion information:
1094 if (aRefcnt
== 0 && gAllocLog
&& loggingThisType
&& loggingThisObject
) {
1096 "\n<%s> 0x%08X %d Destroy\n",
1097 aClazz
, NS_PTR_TO_INT32(aPtr
), serialno
);
1098 nsTraceRefcntImpl::WalkTheStack(gAllocLog
);
1101 if (aRefcnt
== 0 && gSerialNumbers
&& loggingThisType
) {
1102 RecycleSerialNumberPtr(aPtr
);
1110 EXPORT_XPCOM_API(void)
1111 NS_LogCtor(void* aPtr
, const char* aType
, PRUint32 aInstanceSize
)
1113 #ifdef NS_IMPL_REFCNT_LOGGING
1114 ASSERT_ACTIVITY_IS_LEGAL
;
1122 BloatEntry
* entry
= GetBloatEntry(aType
, aInstanceSize
);
1128 PRBool loggingThisType
= (!gTypesToLog
|| LogThisType(aType
));
1129 PRInt32 serialno
= 0;
1130 if (gSerialNumbers
&& loggingThisType
) {
1131 serialno
= GetSerialNumber(aPtr
, PR_TRUE
);
1134 PRBool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1135 if (gAllocLog
&& loggingThisType
&& loggingThisObject
) {
1136 fprintf(gAllocLog
, "\n<%s> 0x%08X %d Ctor (%d)\n",
1137 aType
, NS_PTR_TO_INT32(aPtr
), serialno
, aInstanceSize
);
1138 nsTraceRefcntImpl::WalkTheStack(gAllocLog
);
1147 EXPORT_XPCOM_API(void)
1148 NS_LogDtor(void* aPtr
, const char* aType
, PRUint32 aInstanceSize
)
1150 #ifdef NS_IMPL_REFCNT_LOGGING
1151 ASSERT_ACTIVITY_IS_LEGAL
;
1159 BloatEntry
* entry
= GetBloatEntry(aType
, aInstanceSize
);
1165 PRBool loggingThisType
= (!gTypesToLog
|| LogThisType(aType
));
1166 PRInt32 serialno
= 0;
1167 if (gSerialNumbers
&& loggingThisType
) {
1168 serialno
= GetSerialNumber(aPtr
, PR_FALSE
);
1169 RecycleSerialNumberPtr(aPtr
);
1172 PRBool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1174 // (If we're on a losing architecture, don't do this because we'll be
1175 // using LogDeleteXPCOM instead to get file and line numbers.)
1176 if (gAllocLog
&& loggingThisType
&& loggingThisObject
) {
1177 fprintf(gAllocLog
, "\n<%s> 0x%08X %d Dtor (%d)\n",
1178 aType
, NS_PTR_TO_INT32(aPtr
), serialno
, aInstanceSize
);
1179 nsTraceRefcntImpl::WalkTheStack(gAllocLog
);
1188 EXPORT_XPCOM_API(void)
1189 NS_LogCOMPtrAddRef(void* aCOMPtr
, nsISupports
* aObject
)
1191 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1192 // Get the most-derived object.
1193 void *object
= dynamic_cast<void *>(aObject
);
1195 // This is a very indirect way of finding out what the class is
1196 // of the object being logged. If we're logging a specific type,
1198 if (!gTypesToLog
|| !gSerialNumbers
) {
1201 PRInt32 serialno
= GetSerialNumber(object
, PR_FALSE
);
1202 if (serialno
== 0) {
1211 PRInt32
* count
= GetCOMPtrCount(object
);
1215 PRBool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1217 if (gCOMPtrLog
&& loggingThisObject
) {
1218 fprintf(gCOMPtrLog
, "\n<?> 0x%08X %d nsCOMPtrAddRef %d 0x%08X\n",
1219 NS_PTR_TO_INT32(object
), serialno
, count
?(*count
):-1, NS_PTR_TO_INT32(aCOMPtr
));
1220 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog
);
1229 EXPORT_XPCOM_API(void)
1230 NS_LogCOMPtrRelease(void* aCOMPtr
, nsISupports
* aObject
)
1232 #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1233 // Get the most-derived object.
1234 void *object
= dynamic_cast<void *>(aObject
);
1236 // This is a very indirect way of finding out what the class is
1237 // of the object being logged. If we're logging a specific type,
1239 if (!gTypesToLog
|| !gSerialNumbers
) {
1242 PRInt32 serialno
= GetSerialNumber(object
, PR_FALSE
);
1243 if (serialno
== 0) {
1252 PRInt32
* count
= GetCOMPtrCount(object
);
1256 PRBool loggingThisObject
= (!gObjectsToLog
|| LogThisObj(serialno
));
1258 if (gCOMPtrLog
&& loggingThisObject
) {
1259 fprintf(gCOMPtrLog
, "\n<?> 0x%08X %d nsCOMPtrRelease %d 0x%08X\n",
1260 NS_PTR_TO_INT32(object
), serialno
, count
?(*count
):-1, NS_PTR_TO_INT32(aCOMPtr
));
1261 nsTraceRefcntImpl::WalkTheStack(gCOMPtrLog
);
1270 nsTraceRefcntImpl::Startup()
1275 nsTraceRefcntImpl::Shutdown()
1277 #ifdef NS_IMPL_REFCNT_LOGGING
1280 PL_HashTableDestroy(gBloatView
);
1281 gBloatView
= nsnull
;
1284 PL_HashTableDestroy(gTypesToLog
);
1285 gTypesToLog
= nsnull
;
1287 if (gObjectsToLog
) {
1288 PL_HashTableDestroy(gObjectsToLog
);
1289 gObjectsToLog
= nsnull
;
1291 if (gSerialNumbers
) {
1292 PL_HashTableDestroy(gSerialNumbers
);
1293 gSerialNumbers
= nsnull
;
1299 nsTraceRefcntImpl::SetActivityIsLegal(PRBool aLegal
)
1301 #ifdef NS_IMPL_REFCNT_LOGGING
1302 if (gActivityTLS
== BAD_TLS_INDEX
)
1303 PR_NewThreadPrivateIndex(&gActivityTLS
, nsnull
);
1305 PR_SetThreadPrivate(gActivityTLS
, NS_INT32_TO_PTR(!aLegal
));
1309 NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl
, nsITraceRefcnt
)
1311 NS_IMETHODIMP_(nsrefcnt
) nsTraceRefcntImpl::AddRef(void)
1316 NS_IMETHODIMP_(nsrefcnt
) nsTraceRefcntImpl::Release(void)
1322 nsTraceRefcntImpl::LogAddRef(void *aPtr
, nsrefcnt aNewRefcnt
,
1323 const char *aTypeName
, PRUint32 aSize
)
1325 NS_LogAddRef(aPtr
, aNewRefcnt
, aTypeName
, aSize
);
1330 nsTraceRefcntImpl::LogRelease(void *aPtr
, nsrefcnt aNewRefcnt
,
1331 const char *aTypeName
)
1333 NS_LogRelease(aPtr
, aNewRefcnt
, aTypeName
);
1338 nsTraceRefcntImpl::LogCtor(void *aPtr
, const char *aTypeName
, PRUint32 aSize
)
1340 NS_LogCtor(aPtr
, aTypeName
, aSize
);
1345 nsTraceRefcntImpl::LogDtor(void *aPtr
, const char *aTypeName
, PRUint32 aSize
)
1347 NS_LogDtor(aPtr
, aTypeName
, aSize
);
1352 nsTraceRefcntImpl::LogAddCOMPtr(void *aCOMPtr
, nsISupports
* aObject
)
1354 NS_LogCOMPtrAddRef(aCOMPtr
, aObject
);
1359 nsTraceRefcntImpl::LogReleaseCOMPtr(void *aCOMPtr
, nsISupports
* aObject
)
1361 NS_LogCOMPtrRelease(aCOMPtr
, aObject
);
1365 static const nsTraceRefcntImpl kTraceRefcntImpl
;
1368 nsTraceRefcntImpl::Create(nsISupports
* outer
, const nsIID
& aIID
, void* *aInstancePtr
)
1370 return const_cast<nsTraceRefcntImpl
*>(&kTraceRefcntImpl
)->
1371 QueryInterface(aIID
, aInstancePtr
);