michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsTraceRefcnt.h" michael@0: #include "mozilla/IntegerPrintfMacros.h" michael@0: #include "nsXPCOMPrivate.h" michael@0: #include "nscore.h" michael@0: #include "nsISupports.h" michael@0: #include "nsTArray.h" michael@0: #include "prenv.h" michael@0: #include "plstr.h" michael@0: #include "prlink.h" michael@0: #include "nsCRT.h" michael@0: #include michael@0: #include "nsStackWalkPrivate.h" michael@0: #include "nsStackWalk.h" michael@0: #include "nsString.h" michael@0: michael@0: #include "nsXULAppAPI.h" michael@0: #ifdef XP_WIN michael@0: #include michael@0: #define getpid _getpid michael@0: #else michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef NS_TRACE_MALLOC michael@0: #include "nsTraceMalloc.h" michael@0: #endif michael@0: michael@0: #include "mozilla/BlockingResourceBase.h" michael@0: #include "mozilla/PoisonIOInterposer.h" michael@0: michael@0: #ifdef HAVE_DLOPEN michael@0: #include michael@0: #endif michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void michael@0: NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues, michael@0: double *meanResult, double *stdDevResult) michael@0: { michael@0: double mean = 0.0, var = 0.0, stdDev = 0.0; michael@0: if (n > 0.0 && sumOfValues >= 0) { michael@0: mean = sumOfValues / n; michael@0: double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues); michael@0: if (temp < 0.0 || n <= 1) michael@0: var = 0.0; michael@0: else michael@0: var = temp / (n * (n - 1)); michael@0: // for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this: michael@0: stdDev = var != 0.0 ? sqrt(var) : 0.0; michael@0: } michael@0: *meanResult = mean; michael@0: *stdDevResult = stdDev; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: #if !defined(XP_WIN) || (!defined(MOZ_OPTIMIZE) || defined(MOZ_PROFILING) || defined(DEBUG)) michael@0: #define STACKWALKING_AVAILABLE michael@0: #endif michael@0: michael@0: #define NS_IMPL_REFCNT_LOGGING michael@0: michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: #include "plhash.h" michael@0: #include "prmem.h" michael@0: michael@0: #include "prlock.h" michael@0: michael@0: // TraceRefcnt has to use bare PRLock instead of mozilla::Mutex michael@0: // because TraceRefcnt can be used very early in startup. michael@0: static PRLock* gTraceLock; michael@0: michael@0: #define LOCK_TRACELOG() PR_Lock(gTraceLock) michael@0: #define UNLOCK_TRACELOG() PR_Unlock(gTraceLock) michael@0: michael@0: static PLHashTable* gBloatView; michael@0: static PLHashTable* gTypesToLog; michael@0: static PLHashTable* gObjectsToLog; michael@0: static PLHashTable* gSerialNumbers; michael@0: static intptr_t gNextSerialNumber; michael@0: michael@0: static bool gLogging; michael@0: static bool gLogToLeaky; michael@0: static bool gLogLeaksOnly; michael@0: michael@0: static void (*leakyLogAddRef)(void* p, int oldrc, int newrc); michael@0: static void (*leakyLogRelease)(void* p, int oldrc, int newrc); michael@0: michael@0: #define BAD_TLS_INDEX ((unsigned) -1) michael@0: michael@0: // if gActivityTLS == BAD_TLS_INDEX, then we're michael@0: // unitialized... otherwise this points to a NSPR TLS thread index michael@0: // indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then michael@0: // activity is ok, otherwise not! michael@0: static unsigned gActivityTLS = BAD_TLS_INDEX; michael@0: michael@0: static bool gInitialized; michael@0: static nsrefcnt gInitCount; michael@0: michael@0: static FILE *gBloatLog = nullptr; michael@0: static FILE *gRefcntsLog = nullptr; michael@0: static FILE *gAllocLog = nullptr; michael@0: static FILE *gLeakyLog = nullptr; michael@0: static FILE *gCOMPtrLog = nullptr; michael@0: michael@0: struct serialNumberRecord { michael@0: intptr_t serialNumber; michael@0: int32_t refCount; michael@0: int32_t COMPtrCount; michael@0: }; michael@0: michael@0: struct nsTraceRefcntStats { michael@0: uint64_t mAddRefs; michael@0: uint64_t mReleases; michael@0: uint64_t mCreates; michael@0: uint64_t mDestroys; michael@0: double mRefsOutstandingTotal; michael@0: double mRefsOutstandingSquared; michael@0: double mObjsOutstandingTotal; michael@0: double mObjsOutstandingSquared; michael@0: }; michael@0: michael@0: // I hope to turn this on for everybody once we hit it a little less. michael@0: #ifdef DEBUG michael@0: static const char kStaticCtorDtorWarning[] = michael@0: "XPCOM objects created/destroyed from static ctor/dtor"; michael@0: michael@0: static void michael@0: AssertActivityIsLegal() michael@0: { michael@0: if (gActivityTLS == BAD_TLS_INDEX || michael@0: NS_PTR_TO_INT32(PR_GetThreadPrivate(gActivityTLS)) != 0) { michael@0: if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) { michael@0: NS_RUNTIMEABORT(kStaticCtorDtorWarning); michael@0: } else { michael@0: NS_WARNING(kStaticCtorDtorWarning); michael@0: } michael@0: } michael@0: } michael@0: # define ASSERT_ACTIVITY_IS_LEGAL \ michael@0: PR_BEGIN_MACRO \ michael@0: AssertActivityIsLegal(); \ michael@0: PR_END_MACRO michael@0: #else michael@0: # define ASSERT_ACTIVITY_IS_LEGAL PR_BEGIN_MACRO PR_END_MACRO michael@0: #endif // DEBUG michael@0: michael@0: // These functions are copied from nsprpub/lib/ds/plhash.c, with changes michael@0: // to the functions not called Default* to free the serialNumberRecord or michael@0: // the BloatEntry. michael@0: michael@0: static void * michael@0: DefaultAllocTable(void *pool, size_t size) michael@0: { michael@0: return PR_MALLOC(size); michael@0: } michael@0: michael@0: static void michael@0: DefaultFreeTable(void *pool, void *item) michael@0: { michael@0: PR_Free(item); michael@0: } michael@0: michael@0: static PLHashEntry * michael@0: DefaultAllocEntry(void *pool, const void *key) michael@0: { michael@0: return PR_NEW(PLHashEntry); michael@0: } michael@0: michael@0: static void michael@0: SerialNumberFreeEntry(void *pool, PLHashEntry *he, unsigned flag) michael@0: { michael@0: if (flag == HT_FREE_ENTRY) { michael@0: PR_Free(reinterpret_cast(he->value)); michael@0: PR_Free(he); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: TypesToLogFreeEntry(void *pool, PLHashEntry *he, unsigned flag) michael@0: { michael@0: if (flag == HT_FREE_ENTRY) { michael@0: free(const_cast(reinterpret_cast(he->key))); michael@0: PR_Free(he); michael@0: } michael@0: } michael@0: michael@0: static const PLHashAllocOps serialNumberHashAllocOps = { michael@0: DefaultAllocTable, DefaultFreeTable, michael@0: DefaultAllocEntry, SerialNumberFreeEntry michael@0: }; michael@0: michael@0: static const PLHashAllocOps typesToLogHashAllocOps = { michael@0: DefaultAllocTable, DefaultFreeTable, michael@0: DefaultAllocEntry, TypesToLogFreeEntry michael@0: }; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: class BloatEntry { michael@0: public: michael@0: BloatEntry(const char* className, uint32_t classSize) michael@0: : mClassSize(classSize) { michael@0: mClassName = PL_strdup(className); michael@0: Clear(&mNewStats); michael@0: Clear(&mAllStats); michael@0: mTotalLeaked = 0; michael@0: } michael@0: michael@0: ~BloatEntry() { michael@0: PL_strfree(mClassName); michael@0: } michael@0: michael@0: uint32_t GetClassSize() { return (uint32_t)mClassSize; } michael@0: const char* GetClassName() { return mClassName; } michael@0: michael@0: static void Clear(nsTraceRefcntStats* stats) { michael@0: stats->mAddRefs = 0; michael@0: stats->mReleases = 0; michael@0: stats->mCreates = 0; michael@0: stats->mDestroys = 0; michael@0: stats->mRefsOutstandingTotal = 0; michael@0: stats->mRefsOutstandingSquared = 0; michael@0: stats->mObjsOutstandingTotal = 0; michael@0: stats->mObjsOutstandingSquared = 0; michael@0: } michael@0: michael@0: void Accumulate() { michael@0: mAllStats.mAddRefs += mNewStats.mAddRefs; michael@0: mAllStats.mReleases += mNewStats.mReleases; michael@0: mAllStats.mCreates += mNewStats.mCreates; michael@0: mAllStats.mDestroys += mNewStats.mDestroys; michael@0: mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal; michael@0: mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared; michael@0: mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal; michael@0: mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared; michael@0: Clear(&mNewStats); michael@0: } michael@0: michael@0: void AddRef(nsrefcnt refcnt) { michael@0: mNewStats.mAddRefs++; michael@0: if (refcnt == 1) { michael@0: Ctor(); michael@0: } michael@0: AccountRefs(); michael@0: } michael@0: michael@0: void Release(nsrefcnt refcnt) { michael@0: mNewStats.mReleases++; michael@0: if (refcnt == 0) { michael@0: Dtor(); michael@0: } michael@0: AccountRefs(); michael@0: } michael@0: michael@0: void Ctor() { michael@0: mNewStats.mCreates++; michael@0: AccountObjs(); michael@0: } michael@0: michael@0: void Dtor() { michael@0: mNewStats.mDestroys++; michael@0: AccountObjs(); michael@0: } michael@0: michael@0: void AccountRefs() { michael@0: uint64_t cnt = (mNewStats.mAddRefs - mNewStats.mReleases); michael@0: mNewStats.mRefsOutstandingTotal += cnt; michael@0: mNewStats.mRefsOutstandingSquared += cnt * cnt; michael@0: } michael@0: michael@0: void AccountObjs() { michael@0: uint64_t cnt = (mNewStats.mCreates - mNewStats.mDestroys); michael@0: mNewStats.mObjsOutstandingTotal += cnt; michael@0: mNewStats.mObjsOutstandingSquared += cnt * cnt; michael@0: } michael@0: michael@0: static int DumpEntry(PLHashEntry *he, int i, void *arg) { michael@0: BloatEntry* entry = (BloatEntry*)he->value; michael@0: if (entry) { michael@0: entry->Accumulate(); michael@0: static_cast*>(arg)->AppendElement(entry); michael@0: } michael@0: return HT_ENUMERATE_NEXT; michael@0: } michael@0: michael@0: static int TotalEntries(PLHashEntry *he, int i, void *arg) { michael@0: BloatEntry* entry = (BloatEntry*)he->value; michael@0: if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) { michael@0: entry->Total((BloatEntry*)arg); michael@0: } michael@0: return HT_ENUMERATE_NEXT; michael@0: } michael@0: michael@0: void Total(BloatEntry* total) { michael@0: total->mAllStats.mAddRefs += mNewStats.mAddRefs + mAllStats.mAddRefs; michael@0: total->mAllStats.mReleases += mNewStats.mReleases + mAllStats.mReleases; michael@0: total->mAllStats.mCreates += mNewStats.mCreates + mAllStats.mCreates; michael@0: total->mAllStats.mDestroys += mNewStats.mDestroys + mAllStats.mDestroys; michael@0: total->mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal + mAllStats.mRefsOutstandingTotal; michael@0: total->mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared + mAllStats.mRefsOutstandingSquared; michael@0: total->mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal + mAllStats.mObjsOutstandingTotal; michael@0: total->mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared + mAllStats.mObjsOutstandingSquared; michael@0: uint64_t count = (mNewStats.mCreates + mAllStats.mCreates); michael@0: total->mClassSize += mClassSize * count; // adjust for average in DumpTotal michael@0: total->mTotalLeaked += (uint64_t)(mClassSize * michael@0: ((mNewStats.mCreates + mAllStats.mCreates) michael@0: -(mNewStats.mDestroys + mAllStats.mDestroys))); michael@0: } michael@0: michael@0: void DumpTotal(FILE* out) { michael@0: mClassSize /= mAllStats.mCreates; michael@0: Dump(-1, out, nsTraceRefcnt::ALL_STATS); michael@0: } michael@0: michael@0: static bool HaveLeaks(nsTraceRefcntStats* stats) { michael@0: return ((stats->mAddRefs != stats->mReleases) || michael@0: (stats->mCreates != stats->mDestroys)); michael@0: } michael@0: michael@0: bool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcnt::StatisticsType type) { michael@0: fprintf(out, "\n== BloatView: %s, %s process %d\n", msg, michael@0: XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid()); michael@0: nsTraceRefcntStats& stats = michael@0: (type == nsTraceRefcnt::NEW_STATS) ? mNewStats : mAllStats; michael@0: if (gLogLeaksOnly && !HaveLeaks(&stats)) michael@0: return false; michael@0: michael@0: fprintf(out, michael@0: "\n" \ michael@0: " |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n" \ michael@0: " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n"); michael@0: michael@0: this->DumpTotal(out); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void Dump(int i, FILE* out, nsTraceRefcnt::StatisticsType type) { michael@0: nsTraceRefcntStats* stats = (type == nsTraceRefcnt::NEW_STATS) ? &mNewStats : &mAllStats; michael@0: if (gLogLeaksOnly && !HaveLeaks(stats)) { michael@0: return; michael@0: } michael@0: michael@0: double meanRefs, stddevRefs; michael@0: NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases, michael@0: stats->mRefsOutstandingTotal, michael@0: stats->mRefsOutstandingSquared, michael@0: &meanRefs, &stddevRefs); michael@0: michael@0: double meanObjs, stddevObjs; michael@0: NS_MeanAndStdDev(stats->mCreates + stats->mDestroys, michael@0: stats->mObjsOutstandingTotal, michael@0: stats->mObjsOutstandingSquared, michael@0: &meanObjs, &stddevObjs); michael@0: michael@0: if ((stats->mAddRefs - stats->mReleases) != 0 || michael@0: stats->mAddRefs != 0 || michael@0: meanRefs != 0 || michael@0: stddevRefs != 0 || michael@0: (stats->mCreates - stats->mDestroys) != 0 || michael@0: stats->mCreates != 0 || michael@0: meanObjs != 0 || michael@0: stddevObjs != 0) { michael@0: 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", michael@0: i+1, mClassName, michael@0: (int32_t)mClassSize, michael@0: (nsCRT::strcmp(mClassName, "TOTAL")) michael@0: ?(uint64_t)((stats->mCreates - stats->mDestroys) * mClassSize) michael@0: :mTotalLeaked, michael@0: stats->mCreates, michael@0: (stats->mCreates - stats->mDestroys), michael@0: meanObjs, michael@0: stddevObjs, michael@0: stats->mAddRefs, michael@0: (stats->mAddRefs - stats->mReleases), michael@0: meanRefs, michael@0: stddevRefs); michael@0: } michael@0: } michael@0: michael@0: protected: michael@0: char* mClassName; michael@0: double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat michael@0: uint64_t mTotalLeaked; // used only for TOTAL entry michael@0: nsTraceRefcntStats mNewStats; michael@0: nsTraceRefcntStats mAllStats; michael@0: }; michael@0: michael@0: static void michael@0: BloatViewFreeEntry(void *pool, PLHashEntry *he, unsigned flag) michael@0: { michael@0: if (flag == HT_FREE_ENTRY) { michael@0: BloatEntry* entry = reinterpret_cast(he->value); michael@0: delete entry; michael@0: PR_Free(he); michael@0: } michael@0: } michael@0: michael@0: const static PLHashAllocOps bloatViewHashAllocOps = { michael@0: DefaultAllocTable, DefaultFreeTable, michael@0: DefaultAllocEntry, BloatViewFreeEntry michael@0: }; michael@0: michael@0: static void michael@0: RecreateBloatView() michael@0: { michael@0: gBloatView = PL_NewHashTable(256, michael@0: PL_HashString, michael@0: PL_CompareStrings, michael@0: PL_CompareValues, michael@0: &bloatViewHashAllocOps, nullptr); michael@0: } michael@0: michael@0: static BloatEntry* michael@0: GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize) michael@0: { michael@0: if (!gBloatView) { michael@0: RecreateBloatView(); michael@0: } michael@0: BloatEntry* entry = nullptr; michael@0: if (gBloatView) { michael@0: entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName); michael@0: if (entry == nullptr && aInstanceSize > 0) { michael@0: michael@0: entry = new BloatEntry(aTypeName, aInstanceSize); michael@0: PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry); michael@0: if (e == nullptr) { michael@0: delete entry; michael@0: entry = nullptr; michael@0: } michael@0: } else { michael@0: NS_ASSERTION(aInstanceSize == 0 || michael@0: entry->GetClassSize() == aInstanceSize, michael@0: "bad size recorded"); michael@0: } michael@0: } michael@0: return entry; michael@0: } michael@0: michael@0: static int DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure) michael@0: { michael@0: serialNumberRecord* record = reinterpret_cast(aHashEntry->value); michael@0: #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR michael@0: fprintf((FILE*) aClosure, "%" PRIdPTR michael@0: " @%p (%d references; %d from COMPtrs)\n", michael@0: record->serialNumber, michael@0: NS_INT32_TO_PTR(aHashEntry->key), michael@0: record->refCount, michael@0: record->COMPtrCount); michael@0: #else michael@0: fprintf((FILE*) aClosure, "%" PRIdPTR michael@0: " @%p (%d references)\n", michael@0: record->serialNumber, michael@0: NS_INT32_TO_PTR(aHashEntry->key), michael@0: record->refCount); michael@0: #endif michael@0: return HT_ENUMERATE_NEXT; michael@0: } michael@0: michael@0: michael@0: template <> michael@0: class nsDefaultComparator { michael@0: public: michael@0: bool Equals(BloatEntry* const& aA, BloatEntry* const& aB) const { michael@0: return PL_strcmp(aA->GetClassName(), aB->GetClassName()) == 0; michael@0: } michael@0: bool LessThan(BloatEntry* const& aA, BloatEntry* const& aB) const { michael@0: return PL_strcmp(aA->GetClassName(), aB->GetClassName()) < 0; michael@0: } michael@0: }; michael@0: michael@0: #endif /* NS_IMPL_REFCNT_LOGGING */ michael@0: michael@0: nsresult michael@0: nsTraceRefcnt::DumpStatistics(StatisticsType type, FILE* out) michael@0: { michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: if (gBloatLog == nullptr || gBloatView == nullptr) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (out == nullptr) { michael@0: out = gBloatLog; michael@0: } michael@0: michael@0: LOCK_TRACELOG(); michael@0: michael@0: bool wasLogging = gLogging; michael@0: gLogging = false; // turn off logging for this method michael@0: michael@0: BloatEntry total("TOTAL", 0); michael@0: PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total); michael@0: const char* msg; michael@0: if (type == NEW_STATS) { michael@0: if (gLogLeaksOnly) michael@0: msg = "NEW (incremental) LEAK STATISTICS"; michael@0: else michael@0: msg = "NEW (incremental) LEAK AND BLOAT STATISTICS"; michael@0: } michael@0: else { michael@0: if (gLogLeaksOnly) michael@0: msg = "ALL (cumulative) LEAK STATISTICS"; michael@0: else michael@0: msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS"; michael@0: } michael@0: const bool leaked = total.PrintDumpHeader(out, msg, type); michael@0: michael@0: nsTArray entries; michael@0: PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries); michael@0: const uint32_t count = entries.Length(); michael@0: michael@0: if (!gLogLeaksOnly || leaked) { michael@0: // Sort the entries alphabetically by classname. michael@0: entries.Sort(); michael@0: michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: BloatEntry* entry = entries[i]; michael@0: entry->Dump(i, out, type); michael@0: } michael@0: michael@0: fprintf(out, "\n"); michael@0: } michael@0: michael@0: fprintf(out, "nsTraceRefcnt::DumpStatistics: %d entries\n", count); michael@0: michael@0: if (gSerialNumbers) { michael@0: fprintf(out, "\nSerial Numbers of Leaked Objects:\n"); michael@0: PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out); michael@0: } michael@0: michael@0: gLogging = wasLogging; michael@0: UNLOCK_TRACELOG(); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsTraceRefcnt::ResetStatistics() michael@0: { michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: LOCK_TRACELOG(); michael@0: if (gBloatView) { michael@0: PL_HashTableDestroy(gBloatView); michael@0: gBloatView = nullptr; michael@0: } michael@0: UNLOCK_TRACELOG(); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: static bool LogThisType(const char* aTypeName) michael@0: { michael@0: void* he = PL_HashTableLookup(gTypesToLog, aTypeName); michael@0: return nullptr != he; michael@0: } michael@0: michael@0: static intptr_t GetSerialNumber(void* aPtr, bool aCreate) michael@0: { michael@0: PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr); michael@0: if (hep && *hep) { michael@0: return reinterpret_cast((*hep)->value)->serialNumber; michael@0: } michael@0: else if (aCreate) { michael@0: serialNumberRecord *record = PR_NEW(serialNumberRecord); michael@0: record->serialNumber = ++gNextSerialNumber; michael@0: record->refCount = 0; michael@0: record->COMPtrCount = 0; michael@0: PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, reinterpret_cast(record)); michael@0: return gNextSerialNumber; michael@0: } michael@0: else { michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: static int32_t* GetRefCount(void* aPtr) michael@0: { michael@0: PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr); michael@0: if (hep && *hep) { michael@0: return &((reinterpret_cast((*hep)->value))->refCount); michael@0: } else { michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR) michael@0: static int32_t* GetCOMPtrCount(void* aPtr) michael@0: { michael@0: PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr); michael@0: if (hep && *hep) { michael@0: return &((reinterpret_cast((*hep)->value))->COMPtrCount); michael@0: } else { michael@0: return nullptr; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: static void RecycleSerialNumberPtr(void* aPtr) michael@0: { michael@0: PL_HashTableRemove(gSerialNumbers, aPtr); michael@0: } michael@0: michael@0: static bool LogThisObj(intptr_t aSerialNumber) michael@0: { michael@0: return nullptr != PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber); michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: #define FOPEN_NO_INHERIT "N" michael@0: #else michael@0: #define FOPEN_NO_INHERIT michael@0: #endif michael@0: michael@0: static bool InitLog(const char* envVar, const char* msg, FILE* *result) michael@0: { michael@0: const char* value = getenv(envVar); michael@0: if (value) { michael@0: if (nsCRT::strcmp(value, "1") == 0) { michael@0: *result = stdout; michael@0: fprintf(stdout, "### %s defined -- logging %s to stdout\n", michael@0: envVar, msg); michael@0: return true; michael@0: } michael@0: else if (nsCRT::strcmp(value, "2") == 0) { michael@0: *result = stderr; michael@0: fprintf(stdout, "### %s defined -- logging %s to stderr\n", michael@0: envVar, msg); michael@0: return true; michael@0: } michael@0: else { michael@0: FILE *stream; michael@0: nsAutoCString fname(value); michael@0: if (XRE_GetProcessType() != GeckoProcessType_Default) { michael@0: bool hasLogExtension = michael@0: fname.RFind(".log", true, -1, 4) == kNotFound ? false : true; michael@0: if (hasLogExtension) michael@0: fname.Cut(fname.Length() - 4, 4); michael@0: fname.AppendLiteral("_"); michael@0: fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType())); michael@0: fname.AppendLiteral("_pid"); michael@0: fname.AppendInt((uint32_t)getpid()); michael@0: if (hasLogExtension) michael@0: fname.AppendLiteral(".log"); michael@0: } michael@0: stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT); michael@0: if (stream != nullptr) { michael@0: MozillaRegisterDebugFD(fileno(stream)); michael@0: *result = stream; michael@0: fprintf(stdout, "### %s defined -- logging %s to %s\n", michael@0: envVar, msg, fname.get()); michael@0: } michael@0: else { michael@0: fprintf(stdout, "### %s defined -- unable to log %s to %s\n", michael@0: envVar, msg, fname.get()); michael@0: } michael@0: return stream != nullptr; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: michael@0: static PLHashNumber HashNumber(const void* aKey) michael@0: { michael@0: return PLHashNumber(NS_PTR_TO_INT32(aKey)); michael@0: } michael@0: michael@0: static void InitTraceLog(void) michael@0: { michael@0: if (gInitialized) return; michael@0: gInitialized = true; michael@0: michael@0: bool defined; michael@0: defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog); michael@0: if (!defined) michael@0: gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog); michael@0: if (defined || gLogLeaksOnly) { michael@0: RecreateBloatView(); michael@0: if (!gBloatView) { michael@0: NS_WARNING("out of memory"); michael@0: gBloatLog = nullptr; michael@0: gLogLeaksOnly = false; michael@0: } michael@0: } michael@0: michael@0: (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog); michael@0: michael@0: (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog); michael@0: michael@0: defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog); michael@0: if (defined) { michael@0: gLogToLeaky = true; michael@0: PRFuncPtr p = nullptr, q = nullptr; michael@0: #ifdef HAVE_DLOPEN michael@0: { michael@0: PRLibrary *lib = nullptr; michael@0: p = PR_FindFunctionSymbolAndLibrary("__log_addref", &lib); michael@0: if (lib) { michael@0: PR_UnloadLibrary(lib); michael@0: lib = nullptr; michael@0: } michael@0: q = PR_FindFunctionSymbolAndLibrary("__log_release", &lib); michael@0: if (lib) { michael@0: PR_UnloadLibrary(lib); michael@0: } michael@0: } michael@0: #endif michael@0: if (p && q) { michael@0: leakyLogAddRef = (void (*)(void*,int,int)) p; michael@0: leakyLogRelease = (void (*)(void*,int,int)) q; michael@0: } michael@0: else { michael@0: gLogToLeaky = false; michael@0: fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n"); michael@0: fflush(stdout); michael@0: } michael@0: } michael@0: michael@0: const char* classes = getenv("XPCOM_MEM_LOG_CLASSES"); michael@0: michael@0: #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR michael@0: if (classes) { michael@0: (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog); michael@0: } else { michael@0: if (getenv("XPCOM_MEM_COMPTR_LOG")) { michael@0: fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n"); michael@0: } michael@0: } michael@0: #else michael@0: const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG"); michael@0: if (comptr_log) { michael@0: fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n"); michael@0: } michael@0: #endif michael@0: michael@0: if (classes) { michael@0: // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted michael@0: // as a list of class names to track michael@0: gTypesToLog = PL_NewHashTable(256, michael@0: PL_HashString, michael@0: PL_CompareStrings, michael@0: PL_CompareValues, michael@0: &typesToLogHashAllocOps, nullptr); michael@0: if (!gTypesToLog) { michael@0: NS_WARNING("out of memory"); michael@0: fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n"); michael@0: } michael@0: else { michael@0: fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: "); michael@0: const char* cp = classes; michael@0: for (;;) { michael@0: char* cm = (char*) strchr(cp, ','); michael@0: if (cm) { michael@0: *cm = '\0'; michael@0: } michael@0: PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1); michael@0: fprintf(stdout, "%s ", cp); michael@0: if (!cm) break; michael@0: *cm = ','; michael@0: cp = cm + 1; michael@0: } michael@0: fprintf(stdout, "\n"); michael@0: } michael@0: michael@0: gSerialNumbers = PL_NewHashTable(256, michael@0: HashNumber, michael@0: PL_CompareValues, michael@0: PL_CompareValues, michael@0: &serialNumberHashAllocOps, nullptr); michael@0: michael@0: michael@0: } michael@0: michael@0: const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS"); michael@0: if (objects) { michael@0: gObjectsToLog = PL_NewHashTable(256, michael@0: HashNumber, michael@0: PL_CompareValues, michael@0: PL_CompareValues, michael@0: nullptr, nullptr); michael@0: michael@0: if (!gObjectsToLog) { michael@0: NS_WARNING("out of memory"); michael@0: fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n"); michael@0: } michael@0: else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) { michael@0: fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n"); michael@0: } michael@0: else { michael@0: fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: "); michael@0: const char* cp = objects; michael@0: for (;;) { michael@0: char* cm = (char*) strchr(cp, ','); michael@0: if (cm) { michael@0: *cm = '\0'; michael@0: } michael@0: intptr_t top = 0; michael@0: intptr_t bottom = 0; michael@0: while (*cp) { michael@0: if (*cp == '-') { michael@0: bottom = top; michael@0: top = 0; michael@0: ++cp; michael@0: } michael@0: top *= 10; michael@0: top += *cp - '0'; michael@0: ++cp; michael@0: } michael@0: if (!bottom) { michael@0: bottom = top; michael@0: } michael@0: for (intptr_t serialno = bottom; serialno <= top; serialno++) { michael@0: PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1); michael@0: fprintf(stdout, "%" PRIdPTR " ", serialno); michael@0: } michael@0: if (!cm) break; michael@0: *cm = ','; michael@0: cp = cm + 1; michael@0: } michael@0: fprintf(stdout, "\n"); michael@0: } michael@0: } michael@0: michael@0: michael@0: if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) { michael@0: gLogging = true; michael@0: } michael@0: michael@0: gTraceLock = PR_NewLock(); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: extern "C" { michael@0: michael@0: #ifdef STACKWALKING_AVAILABLE michael@0: static void PrintStackFrame(void *aPC, void *aSP, void *aClosure) michael@0: { michael@0: FILE *stream = (FILE*)aClosure; michael@0: nsCodeAddressDetails details; michael@0: char buf[1024]; michael@0: michael@0: NS_DescribeCodeAddress(aPC, &details); michael@0: NS_FormatCodeAddressDetails(aPC, &details, buf, sizeof(buf)); michael@0: fputs(buf, stream); michael@0: } michael@0: #endif michael@0: michael@0: } michael@0: michael@0: void michael@0: nsTraceRefcnt::WalkTheStack(FILE* aStream) michael@0: { michael@0: #ifdef STACKWALKING_AVAILABLE michael@0: NS_StackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream, michael@0: 0, nullptr); michael@0: #endif michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: // This thing is exported by libstdc++ michael@0: // Yes, this is a gcc only hack michael@0: #if defined(MOZ_DEMANGLE_SYMBOLS) michael@0: #include michael@0: #include // for free() michael@0: #endif // MOZ_DEMANGLE_SYMBOLS michael@0: michael@0: void michael@0: nsTraceRefcnt::DemangleSymbol(const char * aSymbol, michael@0: char * aBuffer, michael@0: int aBufLen) michael@0: { michael@0: NS_ASSERTION(nullptr != aSymbol,"null symbol"); michael@0: NS_ASSERTION(nullptr != aBuffer,"null buffer"); michael@0: NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where"); michael@0: michael@0: aBuffer[0] = '\0'; michael@0: michael@0: #if defined(MOZ_DEMANGLE_SYMBOLS) michael@0: /* See demangle.h in the gcc source for the voodoo */ michael@0: char * demangled = abi::__cxa_demangle(aSymbol,0,0,0); michael@0: michael@0: if (demangled) michael@0: { michael@0: strncpy(aBuffer,demangled,aBufLen); michael@0: free(demangled); michael@0: } michael@0: #endif // MOZ_DEMANGLE_SYMBOLS michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: EXPORT_XPCOM_API(void) michael@0: NS_LogInit() michael@0: { michael@0: // FIXME: This is called multiple times, we should probably not allow that. michael@0: #ifdef STACKWALKING_AVAILABLE michael@0: StackWalkInitCriticalAddress(); michael@0: #endif michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: if (++gInitCount) michael@0: nsTraceRefcnt::SetActivityIsLegal(true); michael@0: #endif michael@0: michael@0: #ifdef NS_TRACE_MALLOC michael@0: // XXX we don't have to worry about shutting down trace-malloc; it michael@0: // handles this itself, through an atexit() callback. michael@0: if (!NS_TraceMallocHasStarted()) michael@0: NS_TraceMallocStartup(-1); // -1 == no logging michael@0: #endif michael@0: } michael@0: michael@0: EXPORT_XPCOM_API(void) michael@0: NS_LogTerm() michael@0: { michael@0: mozilla::LogTerm(); michael@0: } michael@0: michael@0: namespace mozilla { michael@0: void michael@0: LogTerm() michael@0: { michael@0: NS_ASSERTION(gInitCount > 0, michael@0: "NS_LogTerm without matching NS_LogInit"); michael@0: michael@0: if (--gInitCount == 0) { michael@0: #ifdef DEBUG michael@0: /* FIXME bug 491977: This is only going to operate on the michael@0: * BlockingResourceBase which is compiled into michael@0: * libxul/libxpcom_core.so. Anyone using external linkage will michael@0: * have their own copy of BlockingResourceBase statics which will michael@0: * not be freed by this method. michael@0: * michael@0: * It sounds like what we really want is to be able to register a michael@0: * callback function to call at XPCOM shutdown. Note that with michael@0: * this solution, however, we need to guarantee that michael@0: * BlockingResourceBase::Shutdown() runs after all other shutdown michael@0: * functions. michael@0: */ michael@0: BlockingResourceBase::Shutdown(); michael@0: #endif michael@0: michael@0: if (gInitialized) { michael@0: nsTraceRefcnt::DumpStatistics(); michael@0: nsTraceRefcnt::ResetStatistics(); michael@0: } michael@0: nsTraceRefcnt::Shutdown(); michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: nsTraceRefcnt::SetActivityIsLegal(false); michael@0: gActivityTLS = BAD_TLS_INDEX; michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: EXPORT_XPCOM_API(void) michael@0: NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt, michael@0: const char* aClazz, uint32_t classSize) michael@0: { michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: ASSERT_ACTIVITY_IS_LEGAL; michael@0: if (!gInitialized) michael@0: InitTraceLog(); michael@0: if (gLogging) { michael@0: LOCK_TRACELOG(); michael@0: michael@0: if (gBloatLog) { michael@0: BloatEntry* entry = GetBloatEntry(aClazz, classSize); michael@0: if (entry) { michael@0: entry->AddRef(aRefcnt); michael@0: } michael@0: } michael@0: michael@0: // Here's the case where MOZ_COUNT_CTOR was not used, michael@0: // yet we still want to see creation information: michael@0: michael@0: bool loggingThisType = (!gTypesToLog || LogThisType(aClazz)); michael@0: intptr_t serialno = 0; michael@0: if (gSerialNumbers && loggingThisType) { michael@0: serialno = GetSerialNumber(aPtr, aRefcnt == 1); michael@0: NS_ASSERTION(serialno != 0, michael@0: "Serial number requested for unrecognized pointer! " michael@0: "Are you memmoving a refcounted object?"); michael@0: int32_t* count = GetRefCount(aPtr); michael@0: if(count) michael@0: (*count)++; michael@0: michael@0: } michael@0: michael@0: bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); michael@0: if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) { michael@0: fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Create\n", michael@0: aClazz, NS_PTR_TO_INT32(aPtr), serialno); michael@0: nsTraceRefcnt::WalkTheStack(gAllocLog); michael@0: } michael@0: michael@0: if (gRefcntsLog && loggingThisType && loggingThisObject) { michael@0: if (gLogToLeaky) { michael@0: (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt); michael@0: } michael@0: else { michael@0: // Can't use PR_LOG(), b/c it truncates the line michael@0: fprintf(gRefcntsLog, michael@0: "\n<%s> 0x%08X %" PRIuPTR " AddRef %" PRIuPTR "\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt); michael@0: nsTraceRefcnt::WalkTheStack(gRefcntsLog); michael@0: fflush(gRefcntsLog); michael@0: } michael@0: } michael@0: UNLOCK_TRACELOG(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: EXPORT_XPCOM_API(void) michael@0: NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClazz) michael@0: { michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: ASSERT_ACTIVITY_IS_LEGAL; michael@0: if (!gInitialized) michael@0: InitTraceLog(); michael@0: if (gLogging) { michael@0: LOCK_TRACELOG(); michael@0: michael@0: if (gBloatLog) { michael@0: BloatEntry* entry = GetBloatEntry(aClazz, 0); michael@0: if (entry) { michael@0: entry->Release(aRefcnt); michael@0: } michael@0: } michael@0: michael@0: bool loggingThisType = (!gTypesToLog || LogThisType(aClazz)); michael@0: intptr_t serialno = 0; michael@0: if (gSerialNumbers && loggingThisType) { michael@0: serialno = GetSerialNumber(aPtr, false); michael@0: NS_ASSERTION(serialno != 0, michael@0: "Serial number requested for unrecognized pointer! " michael@0: "Are you memmoving a refcounted object?"); michael@0: int32_t* count = GetRefCount(aPtr); michael@0: if(count) michael@0: (*count)--; michael@0: michael@0: } michael@0: michael@0: bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); michael@0: if (gRefcntsLog && loggingThisType && loggingThisObject) { michael@0: if (gLogToLeaky) { michael@0: (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt); michael@0: } michael@0: else { michael@0: // Can't use PR_LOG(), b/c it truncates the line michael@0: fprintf(gRefcntsLog, michael@0: "\n<%s> 0x%08X %" PRIuPTR " Release %" PRIuPTR "\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt); michael@0: nsTraceRefcnt::WalkTheStack(gRefcntsLog); michael@0: fflush(gRefcntsLog); michael@0: } michael@0: } michael@0: michael@0: // Here's the case where MOZ_COUNT_DTOR was not used, michael@0: // yet we still want to see deletion information: michael@0: michael@0: if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) { michael@0: fprintf(gAllocLog, michael@0: "\n<%s> 0x%08X %" PRIdPTR " Destroy\n", michael@0: aClazz, NS_PTR_TO_INT32(aPtr), serialno); michael@0: nsTraceRefcnt::WalkTheStack(gAllocLog); michael@0: } michael@0: michael@0: if (aRefcnt == 0 && gSerialNumbers && loggingThisType) { michael@0: RecycleSerialNumberPtr(aPtr); michael@0: } michael@0: michael@0: UNLOCK_TRACELOG(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: EXPORT_XPCOM_API(void) michael@0: NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize) michael@0: { michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: ASSERT_ACTIVITY_IS_LEGAL; michael@0: if (!gInitialized) michael@0: InitTraceLog(); michael@0: michael@0: if (gLogging) { michael@0: LOCK_TRACELOG(); michael@0: michael@0: if (gBloatLog) { michael@0: BloatEntry* entry = GetBloatEntry(aType, aInstanceSize); michael@0: if (entry) { michael@0: entry->Ctor(); michael@0: } michael@0: } michael@0: michael@0: bool loggingThisType = (!gTypesToLog || LogThisType(aType)); michael@0: intptr_t serialno = 0; michael@0: if (gSerialNumbers && loggingThisType) { michael@0: serialno = GetSerialNumber(aPtr, true); michael@0: } michael@0: michael@0: bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); michael@0: if (gAllocLog && loggingThisType && loggingThisObject) { michael@0: fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Ctor (%d)\n", michael@0: aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize); michael@0: nsTraceRefcnt::WalkTheStack(gAllocLog); michael@0: } michael@0: michael@0: UNLOCK_TRACELOG(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: michael@0: EXPORT_XPCOM_API(void) michael@0: NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize) michael@0: { michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: ASSERT_ACTIVITY_IS_LEGAL; michael@0: if (!gInitialized) michael@0: InitTraceLog(); michael@0: michael@0: if (gLogging) { michael@0: LOCK_TRACELOG(); michael@0: michael@0: if (gBloatLog) { michael@0: BloatEntry* entry = GetBloatEntry(aType, aInstanceSize); michael@0: if (entry) { michael@0: entry->Dtor(); michael@0: } michael@0: } michael@0: michael@0: bool loggingThisType = (!gTypesToLog || LogThisType(aType)); michael@0: intptr_t serialno = 0; michael@0: if (gSerialNumbers && loggingThisType) { michael@0: serialno = GetSerialNumber(aPtr, false); michael@0: RecycleSerialNumberPtr(aPtr); michael@0: } michael@0: michael@0: bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); michael@0: michael@0: // (If we're on a losing architecture, don't do this because we'll be michael@0: // using LogDeleteXPCOM instead to get file and line numbers.) michael@0: if (gAllocLog && loggingThisType && loggingThisObject) { michael@0: fprintf(gAllocLog, "\n<%s> 0x%08X %" PRIdPTR " Dtor (%d)\n", michael@0: aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize); michael@0: nsTraceRefcnt::WalkTheStack(gAllocLog); michael@0: } michael@0: michael@0: UNLOCK_TRACELOG(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: michael@0: EXPORT_XPCOM_API(void) michael@0: NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject) michael@0: { michael@0: #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR) michael@0: // Get the most-derived object. michael@0: void *object = dynamic_cast(aObject); michael@0: michael@0: // This is a very indirect way of finding out what the class is michael@0: // of the object being logged. If we're logging a specific type, michael@0: // then michael@0: if (!gTypesToLog || !gSerialNumbers) { michael@0: return; michael@0: } michael@0: intptr_t serialno = GetSerialNumber(object, false); michael@0: if (serialno == 0) { michael@0: return; michael@0: } michael@0: michael@0: if (!gInitialized) michael@0: InitTraceLog(); michael@0: if (gLogging) { michael@0: LOCK_TRACELOG(); michael@0: michael@0: int32_t* count = GetCOMPtrCount(object); michael@0: if(count) michael@0: (*count)++; michael@0: michael@0: bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); michael@0: michael@0: if (gCOMPtrLog && loggingThisObject) { michael@0: fprintf(gCOMPtrLog, "\n 0x%08X %" PRIdPTR " nsCOMPtrAddRef %d 0x%08X\n", michael@0: NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr)); michael@0: nsTraceRefcnt::WalkTheStack(gCOMPtrLog); michael@0: } michael@0: michael@0: UNLOCK_TRACELOG(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: michael@0: EXPORT_XPCOM_API(void) michael@0: NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject) michael@0: { michael@0: #if defined(NS_IMPL_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR) michael@0: // Get the most-derived object. michael@0: void *object = dynamic_cast(aObject); michael@0: michael@0: // This is a very indirect way of finding out what the class is michael@0: // of the object being logged. If we're logging a specific type, michael@0: // then michael@0: if (!gTypesToLog || !gSerialNumbers) { michael@0: return; michael@0: } michael@0: intptr_t serialno = GetSerialNumber(object, false); michael@0: if (serialno == 0) { michael@0: return; michael@0: } michael@0: michael@0: if (!gInitialized) michael@0: InitTraceLog(); michael@0: if (gLogging) { michael@0: LOCK_TRACELOG(); michael@0: michael@0: int32_t* count = GetCOMPtrCount(object); michael@0: if(count) michael@0: (*count)--; michael@0: michael@0: bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); michael@0: michael@0: if (gCOMPtrLog && loggingThisObject) { michael@0: fprintf(gCOMPtrLog, "\n 0x%08X %" PRIdPTR " nsCOMPtrRelease %d 0x%08X\n", michael@0: NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr)); michael@0: nsTraceRefcnt::WalkTheStack(gCOMPtrLog); michael@0: } michael@0: michael@0: UNLOCK_TRACELOG(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsTraceRefcnt::Startup() michael@0: { michael@0: } michael@0: michael@0: static void maybeUnregisterAndCloseFile(FILE *&f) { michael@0: if (!f) michael@0: return; michael@0: michael@0: MozillaUnRegisterDebugFILE(f); michael@0: fclose(f); michael@0: f = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsTraceRefcnt::Shutdown() michael@0: { michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: michael@0: if (gBloatView) { michael@0: PL_HashTableDestroy(gBloatView); michael@0: gBloatView = nullptr; michael@0: } michael@0: if (gTypesToLog) { michael@0: PL_HashTableDestroy(gTypesToLog); michael@0: gTypesToLog = nullptr; michael@0: } michael@0: if (gObjectsToLog) { michael@0: PL_HashTableDestroy(gObjectsToLog); michael@0: gObjectsToLog = nullptr; michael@0: } michael@0: if (gSerialNumbers) { michael@0: PL_HashTableDestroy(gSerialNumbers); michael@0: gSerialNumbers = nullptr; michael@0: } michael@0: maybeUnregisterAndCloseFile(gBloatLog); michael@0: maybeUnregisterAndCloseFile(gRefcntsLog); michael@0: maybeUnregisterAndCloseFile(gAllocLog); michael@0: maybeUnregisterAndCloseFile(gLeakyLog); michael@0: maybeUnregisterAndCloseFile(gCOMPtrLog); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: nsTraceRefcnt::SetActivityIsLegal(bool aLegal) michael@0: { michael@0: #ifdef NS_IMPL_REFCNT_LOGGING michael@0: if (gActivityTLS == BAD_TLS_INDEX) michael@0: PR_NewThreadPrivateIndex(&gActivityTLS, nullptr); michael@0: michael@0: PR_SetThreadPrivate(gActivityTLS, NS_INT32_TO_PTR(!aLegal)); michael@0: #endif michael@0: }