diff --git a/xpcom/base/nsTraceRefcnt.cpp b/xpcom/base/nsTraceRefcnt.cpp index fb92b8da951..7b39feffebd 100644 --- a/xpcom/base/nsTraceRefcnt.cpp +++ b/xpcom/base/nsTraceRefcnt.cpp @@ -22,6 +22,7 @@ #include "prprf.h" #include "prlog.h" #include "plstr.h" +#include #if defined(_WIN32) #include @@ -41,6 +42,14 @@ #endif +#ifdef HAVE_LIBDL +#include +#endif + +#ifdef NS_BUILD_REFCNT_LOGGING +#include "plhash.h" +#include + #if defined(NS_MT_SUPPORTED) #include "prlock.h" @@ -55,11 +64,35 @@ static PRLock* gTraceLock; static PRLogModuleInfo* gTraceRefcntLog; -#ifdef BLOATY -#include "plhash.h" -#include +static PLHashTable* gBloatView; -PLHashTable* gBloatView; +static PLHashTable* gTypesToLog; + +static PRBool gLogging; +static PRBool gLogAllRefcnts; +static PRBool gLogSomeRefcnts; +static PRBool gLogToLeaky; +static PRBool gTrackBloat; +static PRBool gLogCalls; +static PRBool gLogNewAndDelete; +static PRBool gDumpLeaksOnly; + +static void (*leakyLogAddRef)(void* p, int oldrc, int newrc); +static void (*leakyLogRelease)(void* p, int oldrc, int newrc); + +#define XPCOM_REFCNT_TRACK_BLOAT 0x1 +#define XPCOM_REFCNT_LOG_ALL 0x2 +#define XPCOM_REFCNT_LOG_SOME 0x4 +#define XPCOM_REFCNT_LOG_TO_LEAKY 0x8 + +// Should only use this on NS_LOSING_ARCHITECTURE... +#define XPCOM_REFCNT_LOG_CALLS 0x10 +#define XPCOM_REFCNT_LOG_NEW 0x20 + +struct GatherArgs { + nsTraceRefcntStatFunc func; + void* closure; +}; class BloatEntry { public: @@ -67,129 +100,366 @@ public: : mClassName(className), mClassSize(classSize) { Clear(); } + ~BloatEntry() {} + void Clear() { - mAddRefs = 0; - mReleases = 0; + mCurrentStats.mAddRefs = 0; + mCurrentStats.mReleases = 0; + mCurrentStats.mCreates = 0; + mCurrentStats.mDestroys = 0; + mPrevStats.mAddRefs = 0; + mPrevStats.mReleases = 0; + mPrevStats.mCreates = 0; + mPrevStats.mDestroys = 0; mRefsOutstandingTotal = 0; mRefsOutstandingVariance = 0; - mCreates = 0; - mDestroys = 0; mObjsOutstandingTotal = 0; mObjsOutstandingVariance = 0; } + void AddRef(nsrefcnt refcnt) { - mAddRefs++; + mCurrentStats.mAddRefs++; if (refcnt == 1) { - mCreates++; + mCurrentStats.mCreates++; AccountObjs(); } AccountRefs(); } + void Release(nsrefcnt refcnt) { - mReleases++; + mCurrentStats.mReleases++; if (refcnt == 0) { - mDestroys++; + mCurrentStats.mDestroys++; AccountObjs(); } AccountRefs(); } + + void Ctor() { + mCurrentStats.mCreates++; + } + + void Dtor() { + mCurrentStats.mDestroys++; + } + + void Snapshot() { + mPrevStats = mCurrentStats; + } + void AccountRefs() { - PRInt32 cnt = (mAddRefs - mReleases); + PRInt32 cnt = (mCurrentStats.mAddRefs - mCurrentStats.mReleases); // NS_ASSERTION(cnt >= 0, "too many releases"); mRefsOutstandingTotal += cnt; mRefsOutstandingVariance += cnt * cnt; } + void AccountObjs() { - PRInt32 cnt = (mCreates - mDestroys); + PRInt32 cnt = (mCurrentStats.mCreates - mCurrentStats.mDestroys); // NS_ASSERTION(cnt >= 0, "too many releases"); mObjsOutstandingTotal += cnt; mObjsOutstandingVariance += cnt * cnt; } + static PRIntn DumpEntry(PLHashEntry *he, PRIntn i, void *arg) { BloatEntry* entry = (BloatEntry*)he->value; - entry->Dump(i, (FILE*)arg); + if (entry) { + entry->Dump(i, (FILE*)arg); + } return HT_ENUMERATE_NEXT; } + + static PRIntn DestroyEntry(PLHashEntry *he, PRIntn i, void *arg) { + BloatEntry* entry = (BloatEntry*)he->value; + if (entry) { + delete entry; + } + return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT; + } + + static PRIntn SnapshotEntry(PLHashEntry *he, PRIntn i, void *arg) { + BloatEntry* entry = (BloatEntry*)he->value; + if (entry) { + entry->Snapshot(); + } + return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT; + } + + static PRIntn GatherEntry(PLHashEntry *he, PRIntn i, void *arg) { + BloatEntry* entry = (BloatEntry*)he->value; + GatherArgs* ga = (GatherArgs*) arg; + if (arg && entry && ga->func) { + PRBool stop = (*ga->func)(entry->mClassName, entry->mClassSize, + &entry->mCurrentStats, &entry->mPrevStats, + ga->closure); + if (stop) { + return HT_ENUMERATE_STOP; + } + } + return HT_ENUMERATE_NEXT; + } + + PRBool HaveLeaks() const { + return ((mCurrentStats.mAddRefs != mCurrentStats.mReleases) || + (mCurrentStats.mCreates != mCurrentStats.mDestroys)); + } + void Dump(PRIntn i, FILE* fp) { + if (gDumpLeaksOnly && !HaveLeaks()) { + return; + } #if 1 - double meanRefCnts = mRefsOutstandingTotal / (mAddRefs + mReleases); + double meanRefCnts = mRefsOutstandingTotal / + (mCurrentStats.mAddRefs + mCurrentStats.mReleases); double varRefCnts = fabs(mRefsOutstandingVariance / mRefsOutstandingTotal - meanRefCnts * meanRefCnts); - double meanObjs = mObjsOutstandingTotal / (mCreates + mDestroys); + double meanObjs = mObjsOutstandingTotal / + (mCurrentStats.mCreates + mCurrentStats.mDestroys); double varObjs = fabs(mObjsOutstandingVariance / mObjsOutstandingTotal - meanObjs * meanObjs); fprintf(fp, "%4d %-20.20s %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f) %8d %8d\n", i, mClassName, - (mAddRefs - mReleases), - mAddRefs, + (mCurrentStats.mAddRefs - mCurrentStats.mReleases), + mCurrentStats.mAddRefs, meanRefCnts, sqrt(varRefCnts), - (mCreates - mDestroys), - mCreates, + (mCurrentStats.mCreates - mCurrentStats.mDestroys), + mCurrentStats.mCreates, meanObjs, sqrt(varObjs), mClassSize, - (mCreates - mDestroys) * mClassSize); + (mCurrentStats.mCreates - mCurrentStats.mDestroys) * mClassSize); #else fprintf(fp, "%4d %-20.20s %8d %8d %8d %8d %8d %8d\n", i, mClassName, - mCreates, - (mCreates - mDestroys), - mAddRefs, - (mAddRefs - mReleases), + mCurrentStats.mCreates, + (mCurrentStats.mCreates - mCurrentStats.mDestroys), + mCurrentStats.mAddRefs, + (mCurrentStats.mAddRefs - mCurrentStats.mReleases), mClassSize, - (mCreates - mDestroys) * mClassSize); + (mCurrentStats.mCreates - mCurrentStats.mDestroys) * mClassSize); #endif } + protected: const char* mClassName; PRUint32 mClassSize; - PRInt32 mAddRefs; - PRInt32 mReleases; double mRefsOutstandingTotal; double mRefsOutstandingVariance; - PRInt32 mCreates; - PRInt32 mDestroys; double mObjsOutstandingTotal; double mObjsOutstandingVariance; + nsTraceRefcntStats mCurrentStats; + nsTraceRefcntStats mPrevStats; }; - -extern "C" void -NS_DumpBloatStatistics(void) + +static void +RecreateBloatView() { + gBloatView = PL_NewHashTable(256, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, + NULL, NULL); +} + +static BloatEntry* +GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize) +{ + if (!gBloatView) { + RecreateBloatView(); + } + BloatEntry* entry = NULL; + if (gBloatView) { + entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName); + if (entry == NULL) { + entry = new BloatEntry(aTypeName, aInstanceSize); + PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry); + if (e == NULL) { + delete entry; + entry = NULL; + } + } + } + return entry; +} + +void +nsTraceRefcnt::DumpStatistics() +{ + if (!gTrackBloat) { + return; + } + + LOCK_TRACELOG(); + if (gDumpLeaksOnly) { + printf("Bloaty: Only dumping data about objects that leaked\n"); + } + // fprintf(stdout, " Name AddRefs [mean / stddev] Objects [mean / stddev] Size TotalSize\n"); // fprintf(stdout, " Name Tot-Objs Rem-Objs Tot-Adds Rem-Adds Obj-Size Mem-Use\n"); fprintf(stdout, " Bloaty: Refcounting and Memory Bloat Statistics\n"); fprintf(stdout, " |<-------Name------>|<--------------References-------------->|<----------------Objects---------------->|<------Size----->|\n"); fprintf(stdout, " Rem Total Mean StdDev Rem Total Mean StdDev Per-Class Rem\n"); PL_HashTableDump(gBloatView, BloatEntry::DumpEntry, stdout); + + UNLOCK_TRACELOG(); } -#endif +void +nsTraceRefcnt::ResetStatistics() +{ + LOCK_TRACELOG(); + if (gBloatView) { + PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DestroyEntry, 0); + PL_HashTableDestroy(gBloatView); + gBloatView = nsnull; + } + UNLOCK_TRACELOG(); +} + +void +nsTraceRefcnt::GatherStatistics(nsTraceRefcntStatFunc aFunc, + void* aClosure) +{ + LOCK_TRACELOG(); + + if (gBloatView) { + GatherArgs ga; + ga.func = aFunc; + ga.closure = aClosure; + PL_HashTableEnumerateEntries(gBloatView, BloatEntry::GatherEntry, + (void*) &ga); + } + + UNLOCK_TRACELOG(); +} + +void +nsTraceRefcnt::SnapshotStatistics() +{ + LOCK_TRACELOG(); + + if (gBloatView) { + PL_HashTableEnumerateEntries(gBloatView, BloatEntry::SnapshotEntry, 0); + } + + UNLOCK_TRACELOG(); +} + +static PRBool LogThisType(const char* aTypeName) +{ + void* he = PL_HashTableLookup(gTypesToLog, aTypeName); + return nsnull != he; +} static void InitTraceLog(void) { if (0 == gTraceRefcntLog) { gTraceRefcntLog = PR_NewLogModule("xpcomrefcnt"); +#if defined(XP_UNIX) || defined(_WIN32) + if (getenv("MOZ_DUMP_LEAKS")) { + gDumpLeaksOnly = PR_TRUE; + } +#endif + + // See if bloaty is enabled + if (XPCOM_REFCNT_TRACK_BLOAT & gTraceRefcntLog->level) { + gTrackBloat = PR_TRUE; + RecreateBloatView(); + if (NS_WARN_IF_FALSE(gBloatView, "out of memory")) { + gTrackBloat = PR_FALSE; + } + else { + printf("XPCOM: using turbo mega bloatvision\n"); + } + } + + // See if raw nspr logging is enabled + if (XPCOM_REFCNT_LOG_ALL & gTraceRefcntLog->level) { + gLogAllRefcnts = PR_TRUE; + printf("XPCOM: logging all refcnt calls\n"); + } + else if (XPCOM_REFCNT_LOG_SOME & gTraceRefcntLog->level) { + gLogSomeRefcnts = PR_TRUE; + gTypesToLog = PL_NewHashTable(256, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, + NULL, NULL); + if (NS_WARN_IF_FALSE(gTypesToLog, "out of memory")) { + gLogSomeRefcnts = PR_FALSE; + } + else { +#if defined(XP_UNIX) || defined (_WIN32) + char* types = getenv("MOZ_TRACE_REFCNTS_TYPES"); + if (types) { + printf("XPCOM: logging some refcnt calls: "); + char* cp = types; + for (;;) { + char* cm = strchr(cp, ','); + if (cm) { + *cm = '\0'; + } + PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1); + printf("%s ", cp); + if (!cm) break; + *cm = ','; + cp = cm + 1; + } + printf("\n"); + } + else { + printf("XPCOM: MOZ_TRACE_REFCNTS_TYPES wasn't set; can't log some refcnts\n"); + gLogSomeRefcnts = PR_FALSE; + } +#endif + } + } + if (XPCOM_REFCNT_LOG_CALLS & gTraceRefcntLog->level) { + gLogCalls = PR_TRUE; + } + if (XPCOM_REFCNT_LOG_NEW & gTraceRefcntLog->level) { + gLogNewAndDelete = PR_TRUE; + } + + // See if we should log to leaky instead of to nspr + if (XPCOM_REFCNT_LOG_TO_LEAKY & gTraceRefcntLog->level) { + gLogToLeaky = PR_TRUE; +#ifdef HAVE_LIBDL + void* p = dlsym(0, "__log_addref"); + if (p) { + leakyLogAddRef = (void (*)(void*,int,int)) p; + p = dlsym(0, "__log_release"); + if (p) { + leakyLogRelease = (void (*)(void*,int,int)) p; + printf("XPCOM: logging addref/release calls to leaky\n"); + } + else { + gLogToLeaky = PR_FALSE; + } + } + else { + gLogToLeaky = PR_FALSE; + } +#endif + } + + if (gTrackBloat || gLogAllRefcnts || gLogSomeRefcnts || + gLogCalls || gLogNewAndDelete) { + gLogging = PR_TRUE; + } + #if defined(NS_MT_SUPPORTED) gTraceLock = PR_NewLock(); #endif /* NS_MT_SUPPORTED */ -#ifdef BLOATY - gBloatView = PL_NewHashTable(256, - PL_HashString, - PL_CompareStrings, - PL_CompareValues, - NULL, NULL); - NS_ASSERTION(gBloatView, "out of memory"); -#endif } } -int nsIToA16(PRUint32 aNumber, char* aBuffer) +static int nsIToA16(PRUint32 aNumber, char* aBuffer) { static char kHex[] = "0123456789abcdef"; @@ -475,188 +745,7 @@ nsTraceRefcnt::WalkTheStack(char* aBuffer, int aBufLen) #endif -NS_COM void -nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName, - void* aLibrayHandle) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT -#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */ - InitTraceLog(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - HANDLE myProcess = ::GetCurrentProcess(); - - if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) { - return; - } - - BOOL b = ::SymLoadModule(myProcess, - NULL, - (char*)aLibraryName, - (char*)aLibraryName, - 0, - 0); -// DWORD lastError = 0; -// if (!b) lastError = ::GetLastError(); -// printf("loading symbols for library %s => %s [%d]\n", aLibraryName, -// b ? "true" : "false", lastError); - } -#endif -#endif -} - -NS_COM unsigned long -nsTraceRefcnt::AddRef(void* aPtr, - unsigned long aNewRefcnt, - const char* aFile, - PRIntn aLine) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT - InitTraceLog(); - - LOCK_TRACELOG(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - char sb[1000]; - WalkTheStack(sb, sizeof(sb)); - PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, - ("AddRef: %p: %d=>%d [%s] in %s (line %d)", - aPtr, aNewRefcnt-1, aNewRefcnt, sb, aFile, aLine)); - } - UNLOCK_TRACELOG(); -#endif - return aNewRefcnt; - -} - -NS_COM unsigned long -nsTraceRefcnt::Release(void* aPtr, - unsigned long aNewRefcnt, - const char* aFile, - PRIntn aLine) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT - InitTraceLog(); - - LOCK_TRACELOG(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - char sb[1000]; - WalkTheStack(sb, sizeof(sb)); - PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, - ("Release: %p: %d=>%d [%s] in %s (line %d)", - aPtr, aNewRefcnt+1, aNewRefcnt, sb, aFile, aLine)); - } - UNLOCK_TRACELOG(); -#endif - return aNewRefcnt; -} - -NS_COM void -nsTraceRefcnt::Create(void* aPtr, - const char* aType, - const char* aFile, - PRIntn aLine) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT - InitTraceLog(); - - LOCK_TRACELOG(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - char sb[1000]; - WalkTheStack(sb, sizeof(sb)); - PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, - ("Create: %p[%s]: [%s] in %s (line %d)", - aPtr, aType, sb, aFile, aLine)); - } - UNLOCK_TRACELOG(); -#endif -} - -NS_COM void -nsTraceRefcnt::Destroy(void* aPtr, - const char* aFile, - PRIntn aLine) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT - InitTraceLog(); - - LOCK_TRACELOG(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - char sb[1000]; - WalkTheStack(sb, sizeof(sb)); - PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, - ("Destroy: %p: [%s] in %s (line %d)", - aPtr, sb, aFile, aLine)); - } - UNLOCK_TRACELOG(); -#endif -} - - -NS_COM void -nsTraceRefcnt::LogAddRef(void* aPtr, - nsrefcnt aRefCnt, - const char* aClazz, - PRUint32 classSize) -{ - InitTraceLog(); -#ifdef BLOATY - LOCK_TRACELOG(); - BloatEntry* entry = - (BloatEntry*)PL_HashTableLookup(gBloatView, aClazz); - if (entry == NULL) { - entry = new BloatEntry(aClazz, classSize); - PLHashEntry* e = PL_HashTableAdd(gBloatView, aClazz, entry); - if (e == NULL) { - delete entry; - entry = NULL; - } - } - if (entry) { - entry->AddRef(aRefCnt); - } - UNLOCK_TRACELOG(); -#else - if (PR_LOG_TEST(gTraceRefcntLog, PR_LOG_DEBUG)) { - char sb[16384]; - WalkTheStack(sb, sizeof(sb)); - // Can't use PR_LOG(), b/c it truncates the line - printf("%s\t%p\tAddRef\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb); - } -#endif -} - - -NS_COM void -nsTraceRefcnt::LogRelease(void* aPtr, - nsrefcnt aRefCnt, - const char* aClazz, - PRUint32 classSize) -{ - InitTraceLog(); -#ifdef BLOATY - LOCK_TRACELOG(); - BloatEntry* entry = - (BloatEntry*)PL_HashTableLookup(gBloatView, aClazz); - if (entry == NULL) { - entry = new BloatEntry(aClazz, classSize); - PLHashEntry* e = PL_HashTableAdd(gBloatView, aClazz, entry); - if (e == NULL) { - delete entry; - entry = NULL; - } - } - if (entry) { - entry->Release(aRefCnt); - } - UNLOCK_TRACELOG(); -#else - if (PR_LOG_TEST(gTraceRefcntLog, PR_LOG_DEBUG)) { - char sb[16384]; - WalkTheStack(sb, sizeof(sb)); - // Can't use PR_LOG(), b/c it truncates the line - printf("%s\t%p\tRelease\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb); - } -#endif -} +//---------------------------------------------------------------------- // This thing is exported by libiberty.a (-liberty) // Yes, this is a gcc only hack @@ -689,7 +778,9 @@ nsTraceRefcnt::DemangleSymbol(const char * aSymbol, } #endif // MOZ_DEMANGLE_SYMBOLS } + #else // __linux__ + NS_COM void nsTraceRefcnt::DemangleSymbol(const char * aSymbol, char * aBuffer, @@ -703,99 +794,242 @@ nsTraceRefcnt::DemangleSymbol(const char * aSymbol, } #endif // __linux__ +#endif /* NS_BUILD_REFCNT_LOGGING */ + //---------------------------------------------------------------------- -#ifdef DEBUG - -struct CtorEntry { - const char* type; - mozCtorDtorCounter* counter; -}; - -nsVoidArray* nsTraceRefcnt::mCtors; - -void -nsTraceRefcnt::RegisterCtor(const char* aType, - mozCtorDtorCounter* aCounterAddr) +NS_COM void +nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName, + void* aLibrayHandle) { - if (!mCtors) { - mCtors = new nsVoidArray(); - if (!mCtors) { +#ifdef NS_BUILD_REFCNT_LOGGING +#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */ + InitTraceLog(); + if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { + HANDLE myProcess = ::GetCurrentProcess(); + + if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) { return; } + + BOOL b = ::SymLoadModule(myProcess, + NULL, + (char*)aLibraryName, + (char*)aLibraryName, + 0, + 0); +// DWORD lastError = 0; +// if (!b) lastError = ::GetLastError(); +// printf("loading symbols for library %s => %s [%d]\n", aLibraryName, +// b ? "true" : "false", lastError); } - CtorEntry* e = new CtorEntry(); - if (!e) { - return; - } - e->type = aType; - e->counter = aCounterAddr; - mCtors->AppendElement(e); +#endif +#endif } -void -nsTraceRefcnt::UnregisterCtor(const char* aType) +//---------------------------------------------------------------------- + +NS_COM void +nsTraceRefcnt::LogAddRef(void* aPtr, + nsrefcnt aRefCnt, + const char* aClazz, + PRUint32 classSize) { - if (mCtors) { - PRInt32 i, n = mCtors->Count(); - for (i = 0; i < n; i++) { - CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i); - if (0 == PL_strcmp(e->type, aType)) { - delete e; - mCtors->RemoveElementAt(i); - break; +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogging) { + LOCK_TRACELOG(); + + if (gTrackBloat) { + BloatEntry* entry = GetBloatEntry(aClazz, classSize); + if (entry) { + entry->AddRef(aRefCnt); } } - } -} -void -nsTraceRefcnt::DumpLeaks(FILE* out) -{ - if (mCtors) { - PRBool haveLeaks = PR_FALSE; - PRInt32 i, n = mCtors->Count(); - for (i = 0; i < n; i++) { - CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i); - if (e) { - mozCtorDtorCounter* cdc = e->counter; - if (cdc) { - if (cdc->ctors != cdc->dtors) { - haveLeaks = PR_TRUE; - break; - } - } + if (gLogAllRefcnts || (gTypesToLog && LogThisType(aClazz))) { + if (gLogToLeaky) { + (*leakyLogAddRef)(aPtr, aRefCnt - 1, aRefCnt); + } + else { + char sb[16384]; + WalkTheStack(sb, sizeof(sb)); + // Can't use PR_LOG(), b/c it truncates the line + printf("%s\t%p\tAddRef\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb); } } - if (haveLeaks) { - fprintf(out, "*** There are memory leaks:\n"); - fprintf(out, "%-40s %-15s %s\n", "Type", "# created", "# leaked"); - fprintf(out, "%-40s %-15s %s\n", "----", "---------", "--------"); - for (i = 0; i < n; i++) { - CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i); - if (e && e->counter) { - mozCtorDtorCounter* cdc = e->counter; - if (cdc->ctors != cdc->dtors) { - fprintf(out, "%-40s %-15d %d\n", e->type, cdc->ctors, - cdc->ctors - cdc->dtors); - } - } + UNLOCK_TRACELOG(); + } +#endif +} + + +NS_COM void +nsTraceRefcnt::LogRelease(void* aPtr, + nsrefcnt aRefCnt, + const char* aClazz) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogging) { + LOCK_TRACELOG(); + + if (gTrackBloat) { + BloatEntry* entry = GetBloatEntry(aClazz, (PRUint32)-1); + if (entry) { + entry->Release(aRefCnt); } } + if (gLogAllRefcnts || (gTypesToLog && LogThisType(aClazz))) { + if (gLogToLeaky) { + (*leakyLogRelease)(aPtr, aRefCnt + 1, aRefCnt); + } + else { + char sb[16384]; + WalkTheStack(sb, sizeof(sb)); + // Can't use PR_LOG(), b/c it truncates the line + printf("%s\t%p\tRelease\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb); + } + } + + UNLOCK_TRACELOG(); } +#endif } -void -nsTraceRefcnt::FlushCtorRegistry(void) +NS_COM nsrefcnt +nsTraceRefcnt::LogAddRefCall(void* aPtr, + nsrefcnt aNewRefcnt, + const char* aFile, + int aLine) { - if (mCtors) { - PRInt32 i, n = mCtors->Count(); - for (i = 0; i < n; i++) { - CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i); - delete e; - } - delete mCtors; +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogCalls) { + LOCK_TRACELOG(); + + char sb[1000]; + WalkTheStack(sb, sizeof(sb)); + PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, + ("AddRef: %p: %d=>%d [%s] in %s (line %d)", + aPtr, aNewRefcnt-1, aNewRefcnt, sb, aFile, aLine)); + + UNLOCK_TRACELOG(); } +#endif + return aNewRefcnt; +} + +NS_COM nsrefcnt +nsTraceRefcnt::LogReleaseCall(void* aPtr, + nsrefcnt aNewRefcnt, + const char* aFile, + int aLine) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + + if (gLogCalls) { + LOCK_TRACELOG(); + + char sb[1000]; + WalkTheStack(sb, sizeof(sb)); + PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, + ("Release: %p: %d=>%d [%s] in %s (line %d)", + aPtr, aNewRefcnt+1, aNewRefcnt, sb, aFile, aLine)); + + UNLOCK_TRACELOG(); + } +#endif + return aNewRefcnt; +} + +NS_COM void +nsTraceRefcnt::LogNewXPCOM(void* aPtr, + const char* aType, + PRUint32 aInstanceSize, + const char* aFile, + int aLine) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + + if (gLogNewAndDelete) { + LOCK_TRACELOG(); + + char sb[1000]; + WalkTheStack(sb, sizeof(sb)); + PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, + ("Create: %p[%s]: [%s] in %s (line %d)", + aPtr, aType, sb, aFile, aLine)); + + UNLOCK_TRACELOG(); + } +#endif +} + +NS_COM void +nsTraceRefcnt::LogDeleteXPCOM(void* aPtr, + const char* aFile, + int aLine) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + + if (gLogNewAndDelete) { + LOCK_TRACELOG(); + + char sb[1000]; + WalkTheStack(sb, sizeof(sb)); + PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, + ("Destroy: %p: [%s] in %s (line %d)", + aPtr, sb, aFile, aLine)); + + UNLOCK_TRACELOG(); + } +#endif +} + +NS_COM void +nsTraceRefcnt::LogCtor(void* aPtr, + const char* aTypeName, + PRUint32 aInstanceSize) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogging) { + LOCK_TRACELOG(); + + if (gTrackBloat) { + BloatEntry* entry = GetBloatEntry(aTypeName, aInstanceSize); + if (entry) { + entry->Ctor(); + } + } + + UNLOCK_TRACELOG(); + } +#endif +} + +NS_COM void +nsTraceRefcnt::LogDtor(void* aPtr, const char* aTypeName, + PRUint32 aInstanceSize) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogging) { + LOCK_TRACELOG(); + + if (gTrackBloat) { + BloatEntry* entry = GetBloatEntry(aTypeName, aInstanceSize); + if (entry) { + entry->Dtor(); + } + } + + UNLOCK_TRACELOG(); + } +#endif } -#endif /* DEBUG */ diff --git a/xpcom/base/nsTraceRefcnt.h b/xpcom/base/nsTraceRefcnt.h index be15b1bbe8f..2e34abbdab8 100644 --- a/xpcom/base/nsTraceRefcnt.h +++ b/xpcom/base/nsTraceRefcnt.h @@ -22,126 +22,167 @@ #include "nsCom.h" #include -class nsVoidArray; +// Normaly, the implementation of NS_LOG_ADDREF and NS_LOG_RELEASE +// will use a stack crawl to determine who called Addref/Release on an +// xpcom object. If your platform can't implement a stack crawling +// function, then define this to symbol. When the symbol is defined +// then the NS_LOG_ADDREF_CALL and NS_LOG_RELEASE_CALL will expand +// differently. +#undef NS_LOSING_ARCHITECTURE -#ifdef DEBUG -struct mozCtorDtorCounter { - PRInt32 ctors; - PRInt32 dtors; -}; +// By default refcnt logging is not part of the build. +#undef NS_BUILD_REFCNT_LOGGING + +#if (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) +// Make refcnt logging part of the build. This doesn't mean that +// actual logging will occur (that requires a seperate enable; see +// nsTraceRefcnt.h for more information). +#define NS_BUILD_REFCNT_LOGGING 1 #endif +// If NO_BUILD_REFCNT_LOGGING is defined then disable refcnt logging +// in the build. This overrides FORCE_BUILD_REFCNT_LOGGING. +#if defined(NO_BUILD_REFCNT_LOGGING) +#undef NS_BUILD_REFCNT_LOGGING +#endif + +#ifdef NS_BUILD_REFCNT_LOGGING + +#define NS_LOG_ADDREF(_p, _rc, _type, _size) \ + nsTraceRefcnt::LogAddRef((_p), (_rc), (_type), (PRUint32) (_size)) + +#define NS_LOG_RELEASE(_p, _rc, _type) \ + nsTraceRefcnt::LogRelease((_p), (_rc), (_type)) + +#define NS_LOG_NEW_XPCOM(_p,_type,_size,_file,_line) \ + nsTraceRefcnt::LogNewXPCOM((_p),(_type),(PRUint32)(_size),(_file),(_line)) + +#define NS_LOG_DELETE_XPCOM(_p,_file,_line) \ + nsTraceRefcnt::LogDeleteXPCOM((_p),(_file),(_line)) + +#ifdef NS_LOSING_ARCHITECTURE + +#define NS_LOG_ADDREF_CALL(_p,_rc,_file,_line) \ + nsTraceRefcnt::LogAddRefCall((_p), (_rc), (_file), (_line)) + +#define NS_LOG_RELEASE_CALL(_p,_rc,_file,_line) \ + nsTraceRefcnt::LogReleaseCall((_p), (_rc), (_file), (_line)) + +#else + +#define NS_LOG_ADDREF_CALL(_p,_rc,_file,_line) _rc +#define NS_LOG_RELEASE_CALL(_p,_rc,_file,_line) _rc + +#endif + +#define MOZ_DECL_CTOR_COUNTER(_type) + +#define MOZ_COUNT_CTOR(_type) \ +PR_BEGIN_MACRO \ + nsTraceRefcnt::LogCtor((void*)this, #_type, sizeof(*this)); \ +PR_END_MACRO + +#define MOZ_COUNT_DTOR(_type) \ +PR_BEGIN_MACRO \ + nsTraceRefcnt::LogDtor((void*)this, #_type, sizeof(*this)); \ +PR_END_MACRO + +#else /* !NS_BUILD_REFCNT_LOGGING */ + +#define NS_LOG_ADDREF(_p, _rc, _type, _size) +#define NS_LOG_RELEASE(_p, _rc, _type) +#define NS_LOG_NEW_XPCOM(_p,_type,_size,_file,_line) +#define NS_LOG_DELETE_XPCOM(_p,_file,_line) +#define NS_LOG_ADDREF_CALL(_p,_rc,_file,_line) _rc +#define NS_LOG_RELEASE_CALL(_p,_rc,_file,_line) _rc +#define MOZ_DECL_CTOR_COUNTER(_type) +#define MOZ_COUNT_CTOR(_type) +#define MOZ_COUNT_DTOR(_type) + +#endif /* NS_BUILD_REFCNT_LOGGING */ + +//---------------------------------------------------------------------- + +struct nsTraceRefcntStats { + nsrefcnt mAddRefs; + nsrefcnt mReleases; + nsrefcnt mCreates; + nsrefcnt mDestroys; +}; + +// Function type used by GatherStatistics. For each type that data has +// been gathered for, this function is called with the counts of the +// various operations that have been logged. The function can return +// PR_FALSE if the gathering should stop. +// +// aCurrentStats is the current value of the counters. aPrevStats is +// the previous value of the counters which is established by the +// nsTraceRefcnt::SnapshotStatistics call. +typedef PRBool (PR_CALLBACK *nsTraceRefcntStatFunc) + (const char* aTypeName, + PRUint32 aInstanceSize, + nsTraceRefcntStats* aCurrentStats, + nsTraceRefcntStats* aPrevStats, + void *aClosure); + /** - * This class is used to support tracing (and logging using nspr) of - * addref and release calls. Note that only calls that use the - * NS_ADDREF and related macros in nsISupports can be traced. - * - * The name of the nspr log module is "xpcomrefcnt" (case matters). - * - * This code only performs tracing built with debugging AND when - * built with -DMOZ_TRACE_XPCOM_REFCNT (because it's expensive!). + * Note: The implementations for these methods are no-ops in a build + * where NS_BUILD_REFCNT_LOGGING is disabled. */ class nsTraceRefcnt { public: - static NS_COM unsigned long AddRef(void* aPtr, - unsigned long aNewRefcnt, - const char* aFile, - int aLine); + static NS_COM void LogAddRef(void* aPtr, + nsrefcnt aNewRefCnt, + const char* aTypeName, + PRUint32 aInstanceSize); - static NS_COM unsigned long Release(void* aPtr, - unsigned long aNewRefcnt, - const char* aFile, - int aLine); + static NS_COM void LogRelease(void* aPtr, + nsrefcnt aNewRefCnt, + const char* aTypeName); - static NS_COM void Create(void* aPtr, - const char* aType, - const char* aFile, - int aLine); + static NS_COM void LogNewXPCOM(void* aPtr, + const char* aTypeName, + PRUint32 aInstanceSize, + const char* aFile, + int aLine); - static NS_COM void Destroy(void* aPtr, - const char* aFile, - int aLine); + static NS_COM void LogDeleteXPCOM(void* aPtr, + const char* aFile, + int aLine); + + static NS_COM nsrefcnt LogAddRefCall(void* aPtr, + nsrefcnt aNewRefcnt, + const char* aFile, + int aLine); + + static NS_COM nsrefcnt LogReleaseCall(void* aPtr, + nsrefcnt aNewRefcnt, + const char* aFile, + int aLine); + + static NS_COM void LogCtor(void* aPtr, const char* aTypeName, + PRUint32 aInstanceSize); + + static NS_COM void LogDtor(void* aPtr, const char* aTypeName, + PRUint32 aInstanceSize); + + static NS_COM void DumpStatistics(void); + + static NS_COM void ResetStatistics(void); + + static NS_COM void SnapshotStatistics(void); + + static NS_COM void GatherStatistics(nsTraceRefcntStatFunc aFunc, + void* aClosure); static NS_COM void LoadLibrarySymbols(const char* aLibraryName, void* aLibrayHandle); - static NS_COM void WalkTheStack(char* aBuffer, int aBufLen); - static NS_COM void DemangleSymbol(const char * aSymbol, char * aBuffer, int aBufLen); - static NS_COM void LogAddRef(void* aPtr, - nsrefcnt aRefCnt, - const char* aClass, - PRUint32 classSize); - - static NS_COM void LogRelease(void* aPtr, - nsrefcnt aRefCnt, - const char* aClass, - PRUint32 classSize); - -#ifdef DEBUG - /** - * Register a constructor with the xpcom library. This records the - * type name and the address of the counter so that later on when - * DumpLeaks is called, we can print out those objects ctors whose - * counter is not zero (the ones that have live object references - * still out there) - */ - static NS_COM void RegisterCtor(const char* aType, - mozCtorDtorCounter* aCounterAddr); - - static NS_COM void UnregisterCtor(const char* aType); - - /** - * Dump the leaking constructors out. - */ - static NS_COM void DumpLeaks(FILE* out); - - /** - * Erase the ctor registration data. - */ - static NS_COM void FlushCtorRegistry(void); -#endif - -protected: -#ifdef DEBUG - static nsVoidArray* mCtors; -#endif + static NS_COM void WalkTheStack(char* aBuffer, int aBufLen); }; -//---------------------------------------------------------------------- - -#ifdef DEBUG - -#define MOZ_DECL_CTOR_COUNTER(_type) \ - static mozCtorDtorCounter gCounter_##_type - -#define MOZ_COUNT_CTOR(_type) \ -PR_BEGIN_MACRO \ - if (0 == gCounter_##_type . ctors) { \ - nsTraceRefcnt::RegisterCtor(#_type, &gCounter_##_type); \ - } \ - gCounter_##_type . ctors++; \ -PR_END_MACRO - -#define MOZ_COUNT_DTOR(_type) \ -PR_BEGIN_MACRO \ - gCounter_##_type . dtors ++; \ -PR_END_MACRO - -#else - -#define MOZ_DECL_CTOR_COUNTER(_type) -#define MOZ_COUNT_CTOR(_type) -#define MOZ_COUNT_DTOR(_type) - -#endif /* DEBUG */ - -#ifdef BLOATY -extern "C" void -NS_DumpBloatStatistics(void); -#endif - #endif /* nsTraceRefcnt_h___ */ diff --git a/xpcom/base/nsTraceRefcntImpl.cpp b/xpcom/base/nsTraceRefcntImpl.cpp index fb92b8da951..7b39feffebd 100644 --- a/xpcom/base/nsTraceRefcntImpl.cpp +++ b/xpcom/base/nsTraceRefcntImpl.cpp @@ -22,6 +22,7 @@ #include "prprf.h" #include "prlog.h" #include "plstr.h" +#include #if defined(_WIN32) #include @@ -41,6 +42,14 @@ #endif +#ifdef HAVE_LIBDL +#include +#endif + +#ifdef NS_BUILD_REFCNT_LOGGING +#include "plhash.h" +#include + #if defined(NS_MT_SUPPORTED) #include "prlock.h" @@ -55,11 +64,35 @@ static PRLock* gTraceLock; static PRLogModuleInfo* gTraceRefcntLog; -#ifdef BLOATY -#include "plhash.h" -#include +static PLHashTable* gBloatView; -PLHashTable* gBloatView; +static PLHashTable* gTypesToLog; + +static PRBool gLogging; +static PRBool gLogAllRefcnts; +static PRBool gLogSomeRefcnts; +static PRBool gLogToLeaky; +static PRBool gTrackBloat; +static PRBool gLogCalls; +static PRBool gLogNewAndDelete; +static PRBool gDumpLeaksOnly; + +static void (*leakyLogAddRef)(void* p, int oldrc, int newrc); +static void (*leakyLogRelease)(void* p, int oldrc, int newrc); + +#define XPCOM_REFCNT_TRACK_BLOAT 0x1 +#define XPCOM_REFCNT_LOG_ALL 0x2 +#define XPCOM_REFCNT_LOG_SOME 0x4 +#define XPCOM_REFCNT_LOG_TO_LEAKY 0x8 + +// Should only use this on NS_LOSING_ARCHITECTURE... +#define XPCOM_REFCNT_LOG_CALLS 0x10 +#define XPCOM_REFCNT_LOG_NEW 0x20 + +struct GatherArgs { + nsTraceRefcntStatFunc func; + void* closure; +}; class BloatEntry { public: @@ -67,129 +100,366 @@ public: : mClassName(className), mClassSize(classSize) { Clear(); } + ~BloatEntry() {} + void Clear() { - mAddRefs = 0; - mReleases = 0; + mCurrentStats.mAddRefs = 0; + mCurrentStats.mReleases = 0; + mCurrentStats.mCreates = 0; + mCurrentStats.mDestroys = 0; + mPrevStats.mAddRefs = 0; + mPrevStats.mReleases = 0; + mPrevStats.mCreates = 0; + mPrevStats.mDestroys = 0; mRefsOutstandingTotal = 0; mRefsOutstandingVariance = 0; - mCreates = 0; - mDestroys = 0; mObjsOutstandingTotal = 0; mObjsOutstandingVariance = 0; } + void AddRef(nsrefcnt refcnt) { - mAddRefs++; + mCurrentStats.mAddRefs++; if (refcnt == 1) { - mCreates++; + mCurrentStats.mCreates++; AccountObjs(); } AccountRefs(); } + void Release(nsrefcnt refcnt) { - mReleases++; + mCurrentStats.mReleases++; if (refcnt == 0) { - mDestroys++; + mCurrentStats.mDestroys++; AccountObjs(); } AccountRefs(); } + + void Ctor() { + mCurrentStats.mCreates++; + } + + void Dtor() { + mCurrentStats.mDestroys++; + } + + void Snapshot() { + mPrevStats = mCurrentStats; + } + void AccountRefs() { - PRInt32 cnt = (mAddRefs - mReleases); + PRInt32 cnt = (mCurrentStats.mAddRefs - mCurrentStats.mReleases); // NS_ASSERTION(cnt >= 0, "too many releases"); mRefsOutstandingTotal += cnt; mRefsOutstandingVariance += cnt * cnt; } + void AccountObjs() { - PRInt32 cnt = (mCreates - mDestroys); + PRInt32 cnt = (mCurrentStats.mCreates - mCurrentStats.mDestroys); // NS_ASSERTION(cnt >= 0, "too many releases"); mObjsOutstandingTotal += cnt; mObjsOutstandingVariance += cnt * cnt; } + static PRIntn DumpEntry(PLHashEntry *he, PRIntn i, void *arg) { BloatEntry* entry = (BloatEntry*)he->value; - entry->Dump(i, (FILE*)arg); + if (entry) { + entry->Dump(i, (FILE*)arg); + } return HT_ENUMERATE_NEXT; } + + static PRIntn DestroyEntry(PLHashEntry *he, PRIntn i, void *arg) { + BloatEntry* entry = (BloatEntry*)he->value; + if (entry) { + delete entry; + } + return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT; + } + + static PRIntn SnapshotEntry(PLHashEntry *he, PRIntn i, void *arg) { + BloatEntry* entry = (BloatEntry*)he->value; + if (entry) { + entry->Snapshot(); + } + return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT; + } + + static PRIntn GatherEntry(PLHashEntry *he, PRIntn i, void *arg) { + BloatEntry* entry = (BloatEntry*)he->value; + GatherArgs* ga = (GatherArgs*) arg; + if (arg && entry && ga->func) { + PRBool stop = (*ga->func)(entry->mClassName, entry->mClassSize, + &entry->mCurrentStats, &entry->mPrevStats, + ga->closure); + if (stop) { + return HT_ENUMERATE_STOP; + } + } + return HT_ENUMERATE_NEXT; + } + + PRBool HaveLeaks() const { + return ((mCurrentStats.mAddRefs != mCurrentStats.mReleases) || + (mCurrentStats.mCreates != mCurrentStats.mDestroys)); + } + void Dump(PRIntn i, FILE* fp) { + if (gDumpLeaksOnly && !HaveLeaks()) { + return; + } #if 1 - double meanRefCnts = mRefsOutstandingTotal / (mAddRefs + mReleases); + double meanRefCnts = mRefsOutstandingTotal / + (mCurrentStats.mAddRefs + mCurrentStats.mReleases); double varRefCnts = fabs(mRefsOutstandingVariance / mRefsOutstandingTotal - meanRefCnts * meanRefCnts); - double meanObjs = mObjsOutstandingTotal / (mCreates + mDestroys); + double meanObjs = mObjsOutstandingTotal / + (mCurrentStats.mCreates + mCurrentStats.mDestroys); double varObjs = fabs(mObjsOutstandingVariance / mObjsOutstandingTotal - meanObjs * meanObjs); fprintf(fp, "%4d %-20.20s %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f) %8d %8d\n", i, mClassName, - (mAddRefs - mReleases), - mAddRefs, + (mCurrentStats.mAddRefs - mCurrentStats.mReleases), + mCurrentStats.mAddRefs, meanRefCnts, sqrt(varRefCnts), - (mCreates - mDestroys), - mCreates, + (mCurrentStats.mCreates - mCurrentStats.mDestroys), + mCurrentStats.mCreates, meanObjs, sqrt(varObjs), mClassSize, - (mCreates - mDestroys) * mClassSize); + (mCurrentStats.mCreates - mCurrentStats.mDestroys) * mClassSize); #else fprintf(fp, "%4d %-20.20s %8d %8d %8d %8d %8d %8d\n", i, mClassName, - mCreates, - (mCreates - mDestroys), - mAddRefs, - (mAddRefs - mReleases), + mCurrentStats.mCreates, + (mCurrentStats.mCreates - mCurrentStats.mDestroys), + mCurrentStats.mAddRefs, + (mCurrentStats.mAddRefs - mCurrentStats.mReleases), mClassSize, - (mCreates - mDestroys) * mClassSize); + (mCurrentStats.mCreates - mCurrentStats.mDestroys) * mClassSize); #endif } + protected: const char* mClassName; PRUint32 mClassSize; - PRInt32 mAddRefs; - PRInt32 mReleases; double mRefsOutstandingTotal; double mRefsOutstandingVariance; - PRInt32 mCreates; - PRInt32 mDestroys; double mObjsOutstandingTotal; double mObjsOutstandingVariance; + nsTraceRefcntStats mCurrentStats; + nsTraceRefcntStats mPrevStats; }; - -extern "C" void -NS_DumpBloatStatistics(void) + +static void +RecreateBloatView() { + gBloatView = PL_NewHashTable(256, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, + NULL, NULL); +} + +static BloatEntry* +GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize) +{ + if (!gBloatView) { + RecreateBloatView(); + } + BloatEntry* entry = NULL; + if (gBloatView) { + entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName); + if (entry == NULL) { + entry = new BloatEntry(aTypeName, aInstanceSize); + PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry); + if (e == NULL) { + delete entry; + entry = NULL; + } + } + } + return entry; +} + +void +nsTraceRefcnt::DumpStatistics() +{ + if (!gTrackBloat) { + return; + } + + LOCK_TRACELOG(); + if (gDumpLeaksOnly) { + printf("Bloaty: Only dumping data about objects that leaked\n"); + } + // fprintf(stdout, " Name AddRefs [mean / stddev] Objects [mean / stddev] Size TotalSize\n"); // fprintf(stdout, " Name Tot-Objs Rem-Objs Tot-Adds Rem-Adds Obj-Size Mem-Use\n"); fprintf(stdout, " Bloaty: Refcounting and Memory Bloat Statistics\n"); fprintf(stdout, " |<-------Name------>|<--------------References-------------->|<----------------Objects---------------->|<------Size----->|\n"); fprintf(stdout, " Rem Total Mean StdDev Rem Total Mean StdDev Per-Class Rem\n"); PL_HashTableDump(gBloatView, BloatEntry::DumpEntry, stdout); + + UNLOCK_TRACELOG(); } -#endif +void +nsTraceRefcnt::ResetStatistics() +{ + LOCK_TRACELOG(); + if (gBloatView) { + PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DestroyEntry, 0); + PL_HashTableDestroy(gBloatView); + gBloatView = nsnull; + } + UNLOCK_TRACELOG(); +} + +void +nsTraceRefcnt::GatherStatistics(nsTraceRefcntStatFunc aFunc, + void* aClosure) +{ + LOCK_TRACELOG(); + + if (gBloatView) { + GatherArgs ga; + ga.func = aFunc; + ga.closure = aClosure; + PL_HashTableEnumerateEntries(gBloatView, BloatEntry::GatherEntry, + (void*) &ga); + } + + UNLOCK_TRACELOG(); +} + +void +nsTraceRefcnt::SnapshotStatistics() +{ + LOCK_TRACELOG(); + + if (gBloatView) { + PL_HashTableEnumerateEntries(gBloatView, BloatEntry::SnapshotEntry, 0); + } + + UNLOCK_TRACELOG(); +} + +static PRBool LogThisType(const char* aTypeName) +{ + void* he = PL_HashTableLookup(gTypesToLog, aTypeName); + return nsnull != he; +} static void InitTraceLog(void) { if (0 == gTraceRefcntLog) { gTraceRefcntLog = PR_NewLogModule("xpcomrefcnt"); +#if defined(XP_UNIX) || defined(_WIN32) + if (getenv("MOZ_DUMP_LEAKS")) { + gDumpLeaksOnly = PR_TRUE; + } +#endif + + // See if bloaty is enabled + if (XPCOM_REFCNT_TRACK_BLOAT & gTraceRefcntLog->level) { + gTrackBloat = PR_TRUE; + RecreateBloatView(); + if (NS_WARN_IF_FALSE(gBloatView, "out of memory")) { + gTrackBloat = PR_FALSE; + } + else { + printf("XPCOM: using turbo mega bloatvision\n"); + } + } + + // See if raw nspr logging is enabled + if (XPCOM_REFCNT_LOG_ALL & gTraceRefcntLog->level) { + gLogAllRefcnts = PR_TRUE; + printf("XPCOM: logging all refcnt calls\n"); + } + else if (XPCOM_REFCNT_LOG_SOME & gTraceRefcntLog->level) { + gLogSomeRefcnts = PR_TRUE; + gTypesToLog = PL_NewHashTable(256, + PL_HashString, + PL_CompareStrings, + PL_CompareValues, + NULL, NULL); + if (NS_WARN_IF_FALSE(gTypesToLog, "out of memory")) { + gLogSomeRefcnts = PR_FALSE; + } + else { +#if defined(XP_UNIX) || defined (_WIN32) + char* types = getenv("MOZ_TRACE_REFCNTS_TYPES"); + if (types) { + printf("XPCOM: logging some refcnt calls: "); + char* cp = types; + for (;;) { + char* cm = strchr(cp, ','); + if (cm) { + *cm = '\0'; + } + PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1); + printf("%s ", cp); + if (!cm) break; + *cm = ','; + cp = cm + 1; + } + printf("\n"); + } + else { + printf("XPCOM: MOZ_TRACE_REFCNTS_TYPES wasn't set; can't log some refcnts\n"); + gLogSomeRefcnts = PR_FALSE; + } +#endif + } + } + if (XPCOM_REFCNT_LOG_CALLS & gTraceRefcntLog->level) { + gLogCalls = PR_TRUE; + } + if (XPCOM_REFCNT_LOG_NEW & gTraceRefcntLog->level) { + gLogNewAndDelete = PR_TRUE; + } + + // See if we should log to leaky instead of to nspr + if (XPCOM_REFCNT_LOG_TO_LEAKY & gTraceRefcntLog->level) { + gLogToLeaky = PR_TRUE; +#ifdef HAVE_LIBDL + void* p = dlsym(0, "__log_addref"); + if (p) { + leakyLogAddRef = (void (*)(void*,int,int)) p; + p = dlsym(0, "__log_release"); + if (p) { + leakyLogRelease = (void (*)(void*,int,int)) p; + printf("XPCOM: logging addref/release calls to leaky\n"); + } + else { + gLogToLeaky = PR_FALSE; + } + } + else { + gLogToLeaky = PR_FALSE; + } +#endif + } + + if (gTrackBloat || gLogAllRefcnts || gLogSomeRefcnts || + gLogCalls || gLogNewAndDelete) { + gLogging = PR_TRUE; + } + #if defined(NS_MT_SUPPORTED) gTraceLock = PR_NewLock(); #endif /* NS_MT_SUPPORTED */ -#ifdef BLOATY - gBloatView = PL_NewHashTable(256, - PL_HashString, - PL_CompareStrings, - PL_CompareValues, - NULL, NULL); - NS_ASSERTION(gBloatView, "out of memory"); -#endif } } -int nsIToA16(PRUint32 aNumber, char* aBuffer) +static int nsIToA16(PRUint32 aNumber, char* aBuffer) { static char kHex[] = "0123456789abcdef"; @@ -475,188 +745,7 @@ nsTraceRefcnt::WalkTheStack(char* aBuffer, int aBufLen) #endif -NS_COM void -nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName, - void* aLibrayHandle) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT -#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */ - InitTraceLog(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - HANDLE myProcess = ::GetCurrentProcess(); - - if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) { - return; - } - - BOOL b = ::SymLoadModule(myProcess, - NULL, - (char*)aLibraryName, - (char*)aLibraryName, - 0, - 0); -// DWORD lastError = 0; -// if (!b) lastError = ::GetLastError(); -// printf("loading symbols for library %s => %s [%d]\n", aLibraryName, -// b ? "true" : "false", lastError); - } -#endif -#endif -} - -NS_COM unsigned long -nsTraceRefcnt::AddRef(void* aPtr, - unsigned long aNewRefcnt, - const char* aFile, - PRIntn aLine) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT - InitTraceLog(); - - LOCK_TRACELOG(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - char sb[1000]; - WalkTheStack(sb, sizeof(sb)); - PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, - ("AddRef: %p: %d=>%d [%s] in %s (line %d)", - aPtr, aNewRefcnt-1, aNewRefcnt, sb, aFile, aLine)); - } - UNLOCK_TRACELOG(); -#endif - return aNewRefcnt; - -} - -NS_COM unsigned long -nsTraceRefcnt::Release(void* aPtr, - unsigned long aNewRefcnt, - const char* aFile, - PRIntn aLine) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT - InitTraceLog(); - - LOCK_TRACELOG(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - char sb[1000]; - WalkTheStack(sb, sizeof(sb)); - PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, - ("Release: %p: %d=>%d [%s] in %s (line %d)", - aPtr, aNewRefcnt+1, aNewRefcnt, sb, aFile, aLine)); - } - UNLOCK_TRACELOG(); -#endif - return aNewRefcnt; -} - -NS_COM void -nsTraceRefcnt::Create(void* aPtr, - const char* aType, - const char* aFile, - PRIntn aLine) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT - InitTraceLog(); - - LOCK_TRACELOG(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - char sb[1000]; - WalkTheStack(sb, sizeof(sb)); - PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, - ("Create: %p[%s]: [%s] in %s (line %d)", - aPtr, aType, sb, aFile, aLine)); - } - UNLOCK_TRACELOG(); -#endif -} - -NS_COM void -nsTraceRefcnt::Destroy(void* aPtr, - const char* aFile, - PRIntn aLine) -{ -#ifdef MOZ_TRACE_XPCOM_REFCNT - InitTraceLog(); - - LOCK_TRACELOG(); - if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { - char sb[1000]; - WalkTheStack(sb, sizeof(sb)); - PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, - ("Destroy: %p: [%s] in %s (line %d)", - aPtr, sb, aFile, aLine)); - } - UNLOCK_TRACELOG(); -#endif -} - - -NS_COM void -nsTraceRefcnt::LogAddRef(void* aPtr, - nsrefcnt aRefCnt, - const char* aClazz, - PRUint32 classSize) -{ - InitTraceLog(); -#ifdef BLOATY - LOCK_TRACELOG(); - BloatEntry* entry = - (BloatEntry*)PL_HashTableLookup(gBloatView, aClazz); - if (entry == NULL) { - entry = new BloatEntry(aClazz, classSize); - PLHashEntry* e = PL_HashTableAdd(gBloatView, aClazz, entry); - if (e == NULL) { - delete entry; - entry = NULL; - } - } - if (entry) { - entry->AddRef(aRefCnt); - } - UNLOCK_TRACELOG(); -#else - if (PR_LOG_TEST(gTraceRefcntLog, PR_LOG_DEBUG)) { - char sb[16384]; - WalkTheStack(sb, sizeof(sb)); - // Can't use PR_LOG(), b/c it truncates the line - printf("%s\t%p\tAddRef\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb); - } -#endif -} - - -NS_COM void -nsTraceRefcnt::LogRelease(void* aPtr, - nsrefcnt aRefCnt, - const char* aClazz, - PRUint32 classSize) -{ - InitTraceLog(); -#ifdef BLOATY - LOCK_TRACELOG(); - BloatEntry* entry = - (BloatEntry*)PL_HashTableLookup(gBloatView, aClazz); - if (entry == NULL) { - entry = new BloatEntry(aClazz, classSize); - PLHashEntry* e = PL_HashTableAdd(gBloatView, aClazz, entry); - if (e == NULL) { - delete entry; - entry = NULL; - } - } - if (entry) { - entry->Release(aRefCnt); - } - UNLOCK_TRACELOG(); -#else - if (PR_LOG_TEST(gTraceRefcntLog, PR_LOG_DEBUG)) { - char sb[16384]; - WalkTheStack(sb, sizeof(sb)); - // Can't use PR_LOG(), b/c it truncates the line - printf("%s\t%p\tRelease\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb); - } -#endif -} +//---------------------------------------------------------------------- // This thing is exported by libiberty.a (-liberty) // Yes, this is a gcc only hack @@ -689,7 +778,9 @@ nsTraceRefcnt::DemangleSymbol(const char * aSymbol, } #endif // MOZ_DEMANGLE_SYMBOLS } + #else // __linux__ + NS_COM void nsTraceRefcnt::DemangleSymbol(const char * aSymbol, char * aBuffer, @@ -703,99 +794,242 @@ nsTraceRefcnt::DemangleSymbol(const char * aSymbol, } #endif // __linux__ +#endif /* NS_BUILD_REFCNT_LOGGING */ + //---------------------------------------------------------------------- -#ifdef DEBUG - -struct CtorEntry { - const char* type; - mozCtorDtorCounter* counter; -}; - -nsVoidArray* nsTraceRefcnt::mCtors; - -void -nsTraceRefcnt::RegisterCtor(const char* aType, - mozCtorDtorCounter* aCounterAddr) +NS_COM void +nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName, + void* aLibrayHandle) { - if (!mCtors) { - mCtors = new nsVoidArray(); - if (!mCtors) { +#ifdef NS_BUILD_REFCNT_LOGGING +#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */ + InitTraceLog(); + if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) { + HANDLE myProcess = ::GetCurrentProcess(); + + if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) { return; } + + BOOL b = ::SymLoadModule(myProcess, + NULL, + (char*)aLibraryName, + (char*)aLibraryName, + 0, + 0); +// DWORD lastError = 0; +// if (!b) lastError = ::GetLastError(); +// printf("loading symbols for library %s => %s [%d]\n", aLibraryName, +// b ? "true" : "false", lastError); } - CtorEntry* e = new CtorEntry(); - if (!e) { - return; - } - e->type = aType; - e->counter = aCounterAddr; - mCtors->AppendElement(e); +#endif +#endif } -void -nsTraceRefcnt::UnregisterCtor(const char* aType) +//---------------------------------------------------------------------- + +NS_COM void +nsTraceRefcnt::LogAddRef(void* aPtr, + nsrefcnt aRefCnt, + const char* aClazz, + PRUint32 classSize) { - if (mCtors) { - PRInt32 i, n = mCtors->Count(); - for (i = 0; i < n; i++) { - CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i); - if (0 == PL_strcmp(e->type, aType)) { - delete e; - mCtors->RemoveElementAt(i); - break; +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogging) { + LOCK_TRACELOG(); + + if (gTrackBloat) { + BloatEntry* entry = GetBloatEntry(aClazz, classSize); + if (entry) { + entry->AddRef(aRefCnt); } } - } -} -void -nsTraceRefcnt::DumpLeaks(FILE* out) -{ - if (mCtors) { - PRBool haveLeaks = PR_FALSE; - PRInt32 i, n = mCtors->Count(); - for (i = 0; i < n; i++) { - CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i); - if (e) { - mozCtorDtorCounter* cdc = e->counter; - if (cdc) { - if (cdc->ctors != cdc->dtors) { - haveLeaks = PR_TRUE; - break; - } - } + if (gLogAllRefcnts || (gTypesToLog && LogThisType(aClazz))) { + if (gLogToLeaky) { + (*leakyLogAddRef)(aPtr, aRefCnt - 1, aRefCnt); + } + else { + char sb[16384]; + WalkTheStack(sb, sizeof(sb)); + // Can't use PR_LOG(), b/c it truncates the line + printf("%s\t%p\tAddRef\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb); } } - if (haveLeaks) { - fprintf(out, "*** There are memory leaks:\n"); - fprintf(out, "%-40s %-15s %s\n", "Type", "# created", "# leaked"); - fprintf(out, "%-40s %-15s %s\n", "----", "---------", "--------"); - for (i = 0; i < n; i++) { - CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i); - if (e && e->counter) { - mozCtorDtorCounter* cdc = e->counter; - if (cdc->ctors != cdc->dtors) { - fprintf(out, "%-40s %-15d %d\n", e->type, cdc->ctors, - cdc->ctors - cdc->dtors); - } - } + UNLOCK_TRACELOG(); + } +#endif +} + + +NS_COM void +nsTraceRefcnt::LogRelease(void* aPtr, + nsrefcnt aRefCnt, + const char* aClazz) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogging) { + LOCK_TRACELOG(); + + if (gTrackBloat) { + BloatEntry* entry = GetBloatEntry(aClazz, (PRUint32)-1); + if (entry) { + entry->Release(aRefCnt); } } + if (gLogAllRefcnts || (gTypesToLog && LogThisType(aClazz))) { + if (gLogToLeaky) { + (*leakyLogRelease)(aPtr, aRefCnt + 1, aRefCnt); + } + else { + char sb[16384]; + WalkTheStack(sb, sizeof(sb)); + // Can't use PR_LOG(), b/c it truncates the line + printf("%s\t%p\tRelease\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb); + } + } + + UNLOCK_TRACELOG(); } +#endif } -void -nsTraceRefcnt::FlushCtorRegistry(void) +NS_COM nsrefcnt +nsTraceRefcnt::LogAddRefCall(void* aPtr, + nsrefcnt aNewRefcnt, + const char* aFile, + int aLine) { - if (mCtors) { - PRInt32 i, n = mCtors->Count(); - for (i = 0; i < n; i++) { - CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i); - delete e; - } - delete mCtors; +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogCalls) { + LOCK_TRACELOG(); + + char sb[1000]; + WalkTheStack(sb, sizeof(sb)); + PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, + ("AddRef: %p: %d=>%d [%s] in %s (line %d)", + aPtr, aNewRefcnt-1, aNewRefcnt, sb, aFile, aLine)); + + UNLOCK_TRACELOG(); } +#endif + return aNewRefcnt; +} + +NS_COM nsrefcnt +nsTraceRefcnt::LogReleaseCall(void* aPtr, + nsrefcnt aNewRefcnt, + const char* aFile, + int aLine) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + + if (gLogCalls) { + LOCK_TRACELOG(); + + char sb[1000]; + WalkTheStack(sb, sizeof(sb)); + PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, + ("Release: %p: %d=>%d [%s] in %s (line %d)", + aPtr, aNewRefcnt+1, aNewRefcnt, sb, aFile, aLine)); + + UNLOCK_TRACELOG(); + } +#endif + return aNewRefcnt; +} + +NS_COM void +nsTraceRefcnt::LogNewXPCOM(void* aPtr, + const char* aType, + PRUint32 aInstanceSize, + const char* aFile, + int aLine) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + + if (gLogNewAndDelete) { + LOCK_TRACELOG(); + + char sb[1000]; + WalkTheStack(sb, sizeof(sb)); + PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, + ("Create: %p[%s]: [%s] in %s (line %d)", + aPtr, aType, sb, aFile, aLine)); + + UNLOCK_TRACELOG(); + } +#endif +} + +NS_COM void +nsTraceRefcnt::LogDeleteXPCOM(void* aPtr, + const char* aFile, + int aLine) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + + if (gLogNewAndDelete) { + LOCK_TRACELOG(); + + char sb[1000]; + WalkTheStack(sb, sizeof(sb)); + PR_LOG(gTraceRefcntLog, PR_LOG_DEBUG, + ("Destroy: %p: [%s] in %s (line %d)", + aPtr, sb, aFile, aLine)); + + UNLOCK_TRACELOG(); + } +#endif +} + +NS_COM void +nsTraceRefcnt::LogCtor(void* aPtr, + const char* aTypeName, + PRUint32 aInstanceSize) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogging) { + LOCK_TRACELOG(); + + if (gTrackBloat) { + BloatEntry* entry = GetBloatEntry(aTypeName, aInstanceSize); + if (entry) { + entry->Ctor(); + } + } + + UNLOCK_TRACELOG(); + } +#endif +} + +NS_COM void +nsTraceRefcnt::LogDtor(void* aPtr, const char* aTypeName, + PRUint32 aInstanceSize) +{ +#ifdef NS_BUILD_REFCNT_LOGGING + InitTraceLog(); + if (gLogging) { + LOCK_TRACELOG(); + + if (gTrackBloat) { + BloatEntry* entry = GetBloatEntry(aTypeName, aInstanceSize); + if (entry) { + entry->Dtor(); + } + } + + UNLOCK_TRACELOG(); + } +#endif } -#endif /* DEBUG */ diff --git a/xpcom/base/nsTraceRefcntImpl.h b/xpcom/base/nsTraceRefcntImpl.h index be15b1bbe8f..2e34abbdab8 100644 --- a/xpcom/base/nsTraceRefcntImpl.h +++ b/xpcom/base/nsTraceRefcntImpl.h @@ -22,126 +22,167 @@ #include "nsCom.h" #include -class nsVoidArray; +// Normaly, the implementation of NS_LOG_ADDREF and NS_LOG_RELEASE +// will use a stack crawl to determine who called Addref/Release on an +// xpcom object. If your platform can't implement a stack crawling +// function, then define this to symbol. When the symbol is defined +// then the NS_LOG_ADDREF_CALL and NS_LOG_RELEASE_CALL will expand +// differently. +#undef NS_LOSING_ARCHITECTURE -#ifdef DEBUG -struct mozCtorDtorCounter { - PRInt32 ctors; - PRInt32 dtors; -}; +// By default refcnt logging is not part of the build. +#undef NS_BUILD_REFCNT_LOGGING + +#if (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING)) +// Make refcnt logging part of the build. This doesn't mean that +// actual logging will occur (that requires a seperate enable; see +// nsTraceRefcnt.h for more information). +#define NS_BUILD_REFCNT_LOGGING 1 #endif +// If NO_BUILD_REFCNT_LOGGING is defined then disable refcnt logging +// in the build. This overrides FORCE_BUILD_REFCNT_LOGGING. +#if defined(NO_BUILD_REFCNT_LOGGING) +#undef NS_BUILD_REFCNT_LOGGING +#endif + +#ifdef NS_BUILD_REFCNT_LOGGING + +#define NS_LOG_ADDREF(_p, _rc, _type, _size) \ + nsTraceRefcnt::LogAddRef((_p), (_rc), (_type), (PRUint32) (_size)) + +#define NS_LOG_RELEASE(_p, _rc, _type) \ + nsTraceRefcnt::LogRelease((_p), (_rc), (_type)) + +#define NS_LOG_NEW_XPCOM(_p,_type,_size,_file,_line) \ + nsTraceRefcnt::LogNewXPCOM((_p),(_type),(PRUint32)(_size),(_file),(_line)) + +#define NS_LOG_DELETE_XPCOM(_p,_file,_line) \ + nsTraceRefcnt::LogDeleteXPCOM((_p),(_file),(_line)) + +#ifdef NS_LOSING_ARCHITECTURE + +#define NS_LOG_ADDREF_CALL(_p,_rc,_file,_line) \ + nsTraceRefcnt::LogAddRefCall((_p), (_rc), (_file), (_line)) + +#define NS_LOG_RELEASE_CALL(_p,_rc,_file,_line) \ + nsTraceRefcnt::LogReleaseCall((_p), (_rc), (_file), (_line)) + +#else + +#define NS_LOG_ADDREF_CALL(_p,_rc,_file,_line) _rc +#define NS_LOG_RELEASE_CALL(_p,_rc,_file,_line) _rc + +#endif + +#define MOZ_DECL_CTOR_COUNTER(_type) + +#define MOZ_COUNT_CTOR(_type) \ +PR_BEGIN_MACRO \ + nsTraceRefcnt::LogCtor((void*)this, #_type, sizeof(*this)); \ +PR_END_MACRO + +#define MOZ_COUNT_DTOR(_type) \ +PR_BEGIN_MACRO \ + nsTraceRefcnt::LogDtor((void*)this, #_type, sizeof(*this)); \ +PR_END_MACRO + +#else /* !NS_BUILD_REFCNT_LOGGING */ + +#define NS_LOG_ADDREF(_p, _rc, _type, _size) +#define NS_LOG_RELEASE(_p, _rc, _type) +#define NS_LOG_NEW_XPCOM(_p,_type,_size,_file,_line) +#define NS_LOG_DELETE_XPCOM(_p,_file,_line) +#define NS_LOG_ADDREF_CALL(_p,_rc,_file,_line) _rc +#define NS_LOG_RELEASE_CALL(_p,_rc,_file,_line) _rc +#define MOZ_DECL_CTOR_COUNTER(_type) +#define MOZ_COUNT_CTOR(_type) +#define MOZ_COUNT_DTOR(_type) + +#endif /* NS_BUILD_REFCNT_LOGGING */ + +//---------------------------------------------------------------------- + +struct nsTraceRefcntStats { + nsrefcnt mAddRefs; + nsrefcnt mReleases; + nsrefcnt mCreates; + nsrefcnt mDestroys; +}; + +// Function type used by GatherStatistics. For each type that data has +// been gathered for, this function is called with the counts of the +// various operations that have been logged. The function can return +// PR_FALSE if the gathering should stop. +// +// aCurrentStats is the current value of the counters. aPrevStats is +// the previous value of the counters which is established by the +// nsTraceRefcnt::SnapshotStatistics call. +typedef PRBool (PR_CALLBACK *nsTraceRefcntStatFunc) + (const char* aTypeName, + PRUint32 aInstanceSize, + nsTraceRefcntStats* aCurrentStats, + nsTraceRefcntStats* aPrevStats, + void *aClosure); + /** - * This class is used to support tracing (and logging using nspr) of - * addref and release calls. Note that only calls that use the - * NS_ADDREF and related macros in nsISupports can be traced. - * - * The name of the nspr log module is "xpcomrefcnt" (case matters). - * - * This code only performs tracing built with debugging AND when - * built with -DMOZ_TRACE_XPCOM_REFCNT (because it's expensive!). + * Note: The implementations for these methods are no-ops in a build + * where NS_BUILD_REFCNT_LOGGING is disabled. */ class nsTraceRefcnt { public: - static NS_COM unsigned long AddRef(void* aPtr, - unsigned long aNewRefcnt, - const char* aFile, - int aLine); + static NS_COM void LogAddRef(void* aPtr, + nsrefcnt aNewRefCnt, + const char* aTypeName, + PRUint32 aInstanceSize); - static NS_COM unsigned long Release(void* aPtr, - unsigned long aNewRefcnt, - const char* aFile, - int aLine); + static NS_COM void LogRelease(void* aPtr, + nsrefcnt aNewRefCnt, + const char* aTypeName); - static NS_COM void Create(void* aPtr, - const char* aType, - const char* aFile, - int aLine); + static NS_COM void LogNewXPCOM(void* aPtr, + const char* aTypeName, + PRUint32 aInstanceSize, + const char* aFile, + int aLine); - static NS_COM void Destroy(void* aPtr, - const char* aFile, - int aLine); + static NS_COM void LogDeleteXPCOM(void* aPtr, + const char* aFile, + int aLine); + + static NS_COM nsrefcnt LogAddRefCall(void* aPtr, + nsrefcnt aNewRefcnt, + const char* aFile, + int aLine); + + static NS_COM nsrefcnt LogReleaseCall(void* aPtr, + nsrefcnt aNewRefcnt, + const char* aFile, + int aLine); + + static NS_COM void LogCtor(void* aPtr, const char* aTypeName, + PRUint32 aInstanceSize); + + static NS_COM void LogDtor(void* aPtr, const char* aTypeName, + PRUint32 aInstanceSize); + + static NS_COM void DumpStatistics(void); + + static NS_COM void ResetStatistics(void); + + static NS_COM void SnapshotStatistics(void); + + static NS_COM void GatherStatistics(nsTraceRefcntStatFunc aFunc, + void* aClosure); static NS_COM void LoadLibrarySymbols(const char* aLibraryName, void* aLibrayHandle); - static NS_COM void WalkTheStack(char* aBuffer, int aBufLen); - static NS_COM void DemangleSymbol(const char * aSymbol, char * aBuffer, int aBufLen); - static NS_COM void LogAddRef(void* aPtr, - nsrefcnt aRefCnt, - const char* aClass, - PRUint32 classSize); - - static NS_COM void LogRelease(void* aPtr, - nsrefcnt aRefCnt, - const char* aClass, - PRUint32 classSize); - -#ifdef DEBUG - /** - * Register a constructor with the xpcom library. This records the - * type name and the address of the counter so that later on when - * DumpLeaks is called, we can print out those objects ctors whose - * counter is not zero (the ones that have live object references - * still out there) - */ - static NS_COM void RegisterCtor(const char* aType, - mozCtorDtorCounter* aCounterAddr); - - static NS_COM void UnregisterCtor(const char* aType); - - /** - * Dump the leaking constructors out. - */ - static NS_COM void DumpLeaks(FILE* out); - - /** - * Erase the ctor registration data. - */ - static NS_COM void FlushCtorRegistry(void); -#endif - -protected: -#ifdef DEBUG - static nsVoidArray* mCtors; -#endif + static NS_COM void WalkTheStack(char* aBuffer, int aBufLen); }; -//---------------------------------------------------------------------- - -#ifdef DEBUG - -#define MOZ_DECL_CTOR_COUNTER(_type) \ - static mozCtorDtorCounter gCounter_##_type - -#define MOZ_COUNT_CTOR(_type) \ -PR_BEGIN_MACRO \ - if (0 == gCounter_##_type . ctors) { \ - nsTraceRefcnt::RegisterCtor(#_type, &gCounter_##_type); \ - } \ - gCounter_##_type . ctors++; \ -PR_END_MACRO - -#define MOZ_COUNT_DTOR(_type) \ -PR_BEGIN_MACRO \ - gCounter_##_type . dtors ++; \ -PR_END_MACRO - -#else - -#define MOZ_DECL_CTOR_COUNTER(_type) -#define MOZ_COUNT_CTOR(_type) -#define MOZ_COUNT_DTOR(_type) - -#endif /* DEBUG */ - -#ifdef BLOATY -extern "C" void -NS_DumpBloatStatistics(void); -#endif - #endif /* nsTraceRefcnt_h___ */