Bug 1442765 - Part 2: Switch nsTraceRefcnt's hashtables to use xpcom hashtables. r=mccr8

--HG--
extra : rebase_source : 5fecdd86a3ef27d211cb43d4c602162db7554f2f
This commit is contained in:
Eric Rahm 2018-03-05 16:50:00 -08:00
Родитель e63e92aa34
Коммит cf21a1de96
1 изменённых файлов: 99 добавлений и 265 удалений

Просмотреть файл

@ -11,7 +11,9 @@
#include "mozilla/StaticPtr.h" #include "mozilla/StaticPtr.h"
#include "nsXPCOMPrivate.h" #include "nsXPCOMPrivate.h"
#include "nscore.h" #include "nscore.h"
#include "nsClassHashtable.h"
#include "nsISupports.h" #include "nsISupports.h"
#include "nsHashKeys.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "nsTHashtable.h" #include "nsTHashtable.h"
#include "prenv.h" #include "prenv.h"
@ -52,8 +54,6 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "plhash.h"
#include "prthread.h" #include "prthread.h"
// We use a spin lock instead of a regular mutex because this lock is usually // We use a spin lock instead of a regular mutex because this lock is usually
@ -80,10 +80,19 @@ struct MOZ_STACK_CLASS AutoTraceLogLock final
~AutoTraceLogLock() { if (doRelease) gTraceLogLocked = 0; } ~AutoTraceLogLock() { if (doRelease) gTraceLogLocked = 0; }
}; };
static PLHashTable* gBloatView; class BloatEntry;
static PLHashTable* gTypesToLog; struct SerialNumberRecord;
static PLHashTable* gObjectsToLog;
static PLHashTable* gSerialNumbers; using BloatHash = nsClassHashtable<nsDepCharHashKey, BloatEntry>;
using CharPtrSet = nsTHashtable<nsCharPtrHashKey>;
using IntPtrSet = nsTHashtable<IntPtrHashKey>;
using SerialHash = nsClassHashtable<nsVoidPtrHashKey, SerialNumberRecord>;
static StaticAutoPtr<BloatHash> gBloatView;
static StaticAutoPtr<CharPtrSet> gTypesToLog;
static StaticAutoPtr<IntPtrSet> gObjectsToLog;
static StaticAutoPtr<SerialHash> gSerialNumbers;
static intptr_t gNextSerialNumber; static intptr_t gNextSerialNumber;
static bool gDumpedStatistics = false; static bool gDumpedStatistics = false;
static bool gLogJSStacks = false; static bool gLogJSStacks = false;
@ -205,56 +214,6 @@ AssertActivityIsLegal()
# define ASSERT_ACTIVITY_IS_LEGAL do { } while(0) # define ASSERT_ACTIVITY_IS_LEGAL do { } while(0)
#endif // DEBUG #endif // DEBUG
// These functions are copied from nsprpub/lib/ds/plhash.c, with changes
// to the functions not called Default* to free the SerialNumberRecord or
// the BloatEntry.
static void*
DefaultAllocTable(void* aPool, size_t aSize)
{
return malloc(aSize);
}
static void
DefaultFreeTable(void* aPool, void* aItem)
{
free(aItem);
}
static PLHashEntry*
DefaultAllocEntry(void* aPool, const void* aKey)
{
return (PLHashEntry*) malloc(sizeof(PLHashEntry));
}
static void
SerialNumberFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
{
if (aFlag == HT_FREE_ENTRY) {
delete static_cast<SerialNumberRecord*>(aHashEntry->value);
free(aHashEntry);
}
}
static void
TypesToLogFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
{
if (aFlag == HT_FREE_ENTRY) {
free(const_cast<char*>(static_cast<const char*>(aHashEntry->key)));
free(aHashEntry);
}
}
static const PLHashAllocOps serialNumberHashAllocOps = {
DefaultAllocTable, DefaultFreeTable,
DefaultAllocEntry, SerialNumberFreeEntry
};
static const PLHashAllocOps typesToLogHashAllocOps = {
DefaultAllocTable, DefaultFreeTable,
DefaultAllocEntry, TypesToLogFreeEntry
};
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
class CodeAddressServiceStringTable final class CodeAddressServiceStringTable final
@ -338,24 +297,6 @@ public:
mStats.mDestroys++; mStats.mDestroys++;
} }
static int DumpEntry(PLHashEntry* aHashEntry, int aIndex, void* aArg)
{
BloatEntry* entry = (BloatEntry*)aHashEntry->value;
if (entry) {
static_cast<nsTArray<BloatEntry*>*>(aArg)->AppendElement(entry);
}
return HT_ENUMERATE_NEXT;
}
static int TotalEntries(PLHashEntry* aHashEntry, int aIndex, void* aArg)
{
BloatEntry* entry = (BloatEntry*)aHashEntry->value;
if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
entry->Total((BloatEntry*)aArg);
}
return HT_ENUMERATE_NEXT;
}
void Total(BloatEntry* aTotal) void Total(BloatEntry* aTotal)
{ {
aTotal->mStats.mCreates += mStats.mCreates; aTotal->mStats.mCreates += mStats.mCreates;
@ -411,29 +352,10 @@ protected:
nsTraceRefcntStats mStats; nsTraceRefcntStats mStats;
}; };
static void
BloatViewFreeEntry(void* aPool, PLHashEntry* aHashEntry, unsigned aFlag)
{
if (aFlag == HT_FREE_ENTRY) {
BloatEntry* entry = static_cast<BloatEntry*>(aHashEntry->value);
delete entry;
free(aHashEntry);
}
}
const static PLHashAllocOps bloatViewHashAllocOps = {
DefaultAllocTable, DefaultFreeTable,
DefaultAllocEntry, BloatViewFreeEntry
};
static void static void
RecreateBloatView() RecreateBloatView()
{ {
gBloatView = PL_NewHashTable(256, gBloatView = new BloatHash(256);
PL_HashString,
PL_CompareStrings,
PL_CompareValues,
&bloatViewHashAllocOps, nullptr);
} }
static BloatEntry* static BloatEntry*
@ -442,48 +364,39 @@ GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
if (!gBloatView) { if (!gBloatView) {
RecreateBloatView(); RecreateBloatView();
} }
BloatEntry* entry = nullptr; BloatEntry* entry = gBloatView->Get(aTypeName);
if (gBloatView) { if (!entry && aInstanceSize > 0) {
entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName); entry = new BloatEntry(aTypeName, aInstanceSize);
if (!entry && aInstanceSize > 0) { gBloatView->Put(aTypeName, entry);
} else {
entry = new BloatEntry(aTypeName, aInstanceSize); MOZ_ASSERT(aInstanceSize == 0 || entry->GetClassSize() == aInstanceSize,
PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry); "Mismatched sizes were recorded in the memory leak logging table. "
if (!e) { "The usual cause of this is having a templated class that uses "
delete entry; "MOZ_COUNT_{C,D}TOR in the constructor or destructor, respectively. "
entry = nullptr; "As a workaround, the MOZ_COUNT_{C,D}TOR calls can be moved to a "
} "non-templated base class. Another possible cause is a runnable with "
} else { "an mName that matches another refcounted class.");
MOZ_ASSERT(aInstanceSize == 0 || entry->GetClassSize() == aInstanceSize,
"Mismatched sizes were recorded in the memory leak logging table. "
"The usual cause of this is having a templated class that uses "
"MOZ_COUNT_{C,D}TOR in the constructor or destructor, respectively. "
"As a workaround, the MOZ_COUNT_{C,D}TOR calls can be moved to a "
"non-templated base class. Another possible cause is a runnable with "
"an mName that matches another refcounted class.");
}
} }
return entry; return entry;
} }
static int static void
DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure) DumpSerialNumbers(const SerialHash::Iterator& aHashEntry, FILE* aFd)
{ {
SerialNumberRecord* record = SerialNumberRecord* record = aHashEntry.Data();
static_cast<SerialNumberRecord*>(aHashEntry->value); auto* outputFile = aFd;
auto* outputFile = static_cast<FILE*>(aClosure);
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR #ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
fprintf(outputFile, "%" PRIdPTR fprintf(outputFile, "%" PRIdPTR
" @%p (%d references; %d from COMPtrs)\n", " @%p (%d references; %d from COMPtrs)\n",
record->serialNumber, record->serialNumber,
aHashEntry->key, aHashEntry.Key(),
record->refCount, record->refCount,
record->COMPtrCount); record->COMPtrCount);
#else #else
fprintf(outputFile, "%" PRIdPTR fprintf(outputFile, "%" PRIdPTR
" @%p (%d references)\n", " @%p (%d references)\n",
record->serialNumber, record->serialNumber,
aHashEntry->key, aHashEntry.Key(),
record->refCount); record->refCount);
#endif #endif
if (!record->allocationStack.empty()) { if (!record->allocationStack.empty()) {
@ -506,8 +419,6 @@ DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure)
fprintf(outputFile, "There is no JS context on the stack.\n"); fprintf(outputFile, "There is no JS context on the stack.\n");
} }
} }
return HT_ENUMERATE_NEXT;
} }
@ -545,7 +456,13 @@ nsTraceRefcnt::DumpStatistics()
gLogging = NoLogging; gLogging = NoLogging;
BloatEntry total("TOTAL", 0); BloatEntry total("TOTAL", 0);
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total); for (auto iter = gBloatView->Iter(); !iter.Done(); iter.Next()) {
BloatEntry* entry = iter.Data();
if (nsCRT::strcmp(entry->GetClassName(), "TOTAL") != 0) {
entry->Total(&total);
}
}
const char* msg; const char* msg;
if (gLogLeaksOnly) { if (gLogLeaksOnly) {
msg = "ALL (cumulative) LEAK STATISTICS"; msg = "ALL (cumulative) LEAK STATISTICS";
@ -555,7 +472,10 @@ nsTraceRefcnt::DumpStatistics()
const bool leaked = total.PrintDumpHeader(gBloatLog, msg); const bool leaked = total.PrintDumpHeader(gBloatLog, msg);
nsTArray<BloatEntry*> entries; nsTArray<BloatEntry*> entries;
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries); for (auto iter = gBloatView->Iter(); !iter.Done(); iter.Next()) {
entries.AppendElement(iter.Data());
}
const uint32_t count = entries.Length(); const uint32_t count = entries.Length();
if (!gLogLeaksOnly || leaked) { if (!gLogLeaksOnly || leaked) {
@ -574,7 +494,9 @@ nsTraceRefcnt::DumpStatistics()
if (gSerialNumbers) { if (gSerialNumbers) {
fprintf(gBloatLog, "\nSerial Numbers of Leaked Objects:\n"); fprintf(gBloatLog, "\nSerial Numbers of Leaked Objects:\n");
PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, gBloatLog); for (auto iter = gSerialNumbers->Iter(); !iter.Done(); iter.Next()) {
DumpSerialNumbers(iter, gBloatLog);
}
} }
return NS_OK; return NS_OK;
@ -584,87 +506,40 @@ void
nsTraceRefcnt::ResetStatistics() nsTraceRefcnt::ResetStatistics()
{ {
AutoTraceLogLock lock; AutoTraceLogLock lock;
if (gBloatView) { gBloatView = nullptr;
PL_HashTableDestroy(gBloatView);
gBloatView = nullptr;
}
}
static bool
LogThisType(const char* aTypeName)
{
void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
return he != nullptr;
}
static PLHashNumber
HashNumber(const void* aKey)
{
return PLHashNumber(NS_PTR_TO_INT32(aKey));
} }
static intptr_t static intptr_t
GetSerialNumber(void* aPtr, bool aCreate) GetSerialNumber(void* aPtr, bool aCreate)
{ {
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
HashNumber(aPtr),
aPtr);
if (hep && *hep) {
MOZ_RELEASE_ASSERT(!aCreate, "If an object already has a serial number, we should be destroying it.");
return static_cast<SerialNumberRecord*>((*hep)->value)->serialNumber;
}
if (!aCreate) { if (!aCreate) {
return 0; auto record = gSerialNumbers->Get(aPtr);
return record ? record->serialNumber : 0;
} }
SerialNumberRecord* record = new SerialNumberRecord(); auto entry = gSerialNumbers->LookupForAdd(aPtr);
if (entry) {
MOZ_CRASH("If an object already has a serial number, we should be destroying it.");
}
auto record = entry.OrInsert([]() { return new SerialNumberRecord(); });
WalkTheStackSavingLocations(record->allocationStack); WalkTheStackSavingLocations(record->allocationStack);
PL_HashTableRawAdd(gSerialNumbers, hep, HashNumber(aPtr),
aPtr, static_cast<void*>(record));
if (gLogJSStacks) { if (gLogJSStacks) {
record->SaveJSStack(); record->SaveJSStack();
} }
return gNextSerialNumber; return gNextSerialNumber;
} }
static int32_t*
GetRefCount(void* aPtr)
{
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
HashNumber(aPtr),
aPtr);
if (hep && *hep) {
return &(static_cast<SerialNumberRecord*>((*hep)->value)->refCount);
} else {
return nullptr;
}
}
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
static int32_t*
GetCOMPtrCount(void* aPtr)
{
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers,
HashNumber(aPtr),
aPtr);
if (hep && *hep) {
return &(static_cast<SerialNumberRecord*>((*hep)->value)->COMPtrCount);
}
return nullptr;
}
#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
static void static void
RecycleSerialNumberPtr(void* aPtr) RecycleSerialNumberPtr(void* aPtr)
{ {
PL_HashTableRemove(gSerialNumbers, aPtr); gSerialNumbers->Remove(aPtr);
} }
static bool static bool
LogThisObj(intptr_t aSerialNumber) LogThisObj(intptr_t aSerialNumber)
{ {
return (bool)PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber); return gObjectsToLog->Contains(aSerialNumber);
} }
using EnvCharType = mozilla::filesystem::Path::value_type; using EnvCharType = mozilla::filesystem::Path::value_type;
@ -802,54 +677,33 @@ InitTraceLog()
if (classes) { if (classes) {
// if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
// as a list of class names to track // as a list of class names to track
gTypesToLog = PL_NewHashTable(256, gTypesToLog = new CharPtrSet(256);
PL_HashString,
PL_CompareStrings, fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
PL_CompareValues, const char* cp = classes;
&typesToLogHashAllocOps, nullptr); for (;;) {
if (!gTypesToLog) { char* cm = (char*)strchr(cp, ',');
NS_WARNING("out of memory"); if (cm) {
fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n"); *cm = '\0';
} else {
fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
const char* cp = classes;
for (;;) {
char* cm = (char*)strchr(cp, ',');
if (cm) {
*cm = '\0';
}
PL_HashTableAdd(gTypesToLog, strdup(cp), (void*)1);
fprintf(stdout, "%s ", cp);
if (!cm) {
break;
}
*cm = ',';
cp = cm + 1;
} }
fprintf(stdout, "\n"); gTypesToLog->PutEntry(cp);
fprintf(stdout, "%s ", cp);
if (!cm) {
break;
}
*cm = ',';
cp = cm + 1;
} }
fprintf(stdout, "\n");
gSerialNumbers = PL_NewHashTable(256, gSerialNumbers = new SerialHash(256);
HashNumber,
PL_CompareValues,
PL_CompareValues,
&serialNumberHashAllocOps, nullptr);
} }
const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS"); const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
if (objects) { if (objects) {
gObjectsToLog = PL_NewHashTable(256, gObjectsToLog = new IntPtrSet(256);
HashNumber,
PL_CompareValues,
PL_CompareValues,
nullptr, nullptr);
if (!gObjectsToLog) { if (!(gRefcntsLog || gAllocLog || gCOMPtrLog)) {
NS_WARNING("out of memory");
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
} else if (!(gRefcntsLog || gAllocLog || gCOMPtrLog)) {
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n"); fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
} else { } else {
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: "); fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
@ -875,7 +729,7 @@ InitTraceLog()
bottom = top; bottom = top;
} }
for (intptr_t serialno = bottom; serialno <= top; serialno++) { for (intptr_t serialno = bottom; serialno <= top; serialno++) {
PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1); gObjectsToLog->PutEntry(serialno);
fprintf(stdout, "%" PRIdPTR " ", serialno); fprintf(stdout, "%" PRIdPTR " ", serialno);
} }
if (!cm) { if (!cm) {
@ -1093,18 +947,17 @@ NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
// Here's the case where MOZ_COUNT_CTOR was not used, // Here's the case where MOZ_COUNT_CTOR was not used,
// yet we still want to see creation information: // yet we still want to see creation information:
bool loggingThisType = (!gTypesToLog || LogThisType(aClass)); bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aClass));
intptr_t serialno = 0; intptr_t serialno = 0;
if (gSerialNumbers && loggingThisType) { if (gSerialNumbers && loggingThisType) {
serialno = GetSerialNumber(aPtr, aRefcnt == 1); serialno = GetSerialNumber(aPtr, aRefcnt == 1);
MOZ_ASSERT(serialno != 0, MOZ_ASSERT(serialno != 0,
"Serial number requested for unrecognized pointer! " "Serial number requested for unrecognized pointer! "
"Are you memmoving a refcounted object?"); "Are you memmoving a refcounted object?");
int32_t* count = GetRefCount(aPtr); auto record = gSerialNumbers->Get(aPtr);
if (count) { if (record) {
(*count)++; ++record->refCount;
} }
} }
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
@ -1143,18 +996,17 @@ NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClass)
} }
} }
bool loggingThisType = (!gTypesToLog || LogThisType(aClass)); bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aClass));
intptr_t serialno = 0; intptr_t serialno = 0;
if (gSerialNumbers && loggingThisType) { if (gSerialNumbers && loggingThisType) {
serialno = GetSerialNumber(aPtr, false); serialno = GetSerialNumber(aPtr, false);
MOZ_ASSERT(serialno != 0, MOZ_ASSERT(serialno != 0,
"Serial number requested for unrecognized pointer! " "Serial number requested for unrecognized pointer! "
"Are you memmoving a refcounted object?"); "Are you memmoving a refcounted object?");
int32_t* count = GetRefCount(aPtr); auto record = gSerialNumbers->Get(aPtr);
if (count) { if (record) {
(*count)--; --record->refCount;
} }
} }
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
@ -1202,7 +1054,7 @@ NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
} }
} }
bool loggingThisType = (!gTypesToLog || LogThisType(aType)); bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aType));
intptr_t serialno = 0; intptr_t serialno = 0;
if (gSerialNumbers && loggingThisType) { if (gSerialNumbers && loggingThisType) {
serialno = GetSerialNumber(aPtr, true); serialno = GetSerialNumber(aPtr, true);
@ -1239,7 +1091,7 @@ NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
} }
} }
bool loggingThisType = (!gTypesToLog || LogThisType(aType)); bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aType));
intptr_t serialno = 0; intptr_t serialno = 0;
if (gSerialNumbers && loggingThisType) { if (gSerialNumbers && loggingThisType) {
serialno = GetSerialNumber(aPtr, false); serialno = GetSerialNumber(aPtr, false);
@ -1285,16 +1137,13 @@ NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
return; return;
} }
int32_t* count = GetCOMPtrCount(object); auto record = gSerialNumbers->Get(object);
if (count) { int32_t count = record ? ++record->COMPtrCount : -1;
(*count)++;
}
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
if (gCOMPtrLog && loggingThisObject) { if (gCOMPtrLog && loggingThisObject) {
fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrAddRef %d %p\n", fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrAddRef %d %p\n",
object, serialno, count ? (*count) : -1, aCOMPtr); object, serialno, count, aCOMPtr);
WalkTheStackCached(gCOMPtrLog); WalkTheStackCached(gCOMPtrLog);
} }
} }
@ -1326,16 +1175,13 @@ NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
return; return;
} }
int32_t* count = GetCOMPtrCount(object); auto record = gSerialNumbers->Get(object);
if (count) { int32_t count = record ? --record->COMPtrCount : -1;
(*count)--;
}
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno)); bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
if (gCOMPtrLog && loggingThisObject) { if (gCOMPtrLog && loggingThisObject) {
fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrRelease %d %p\n", fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrRelease %d %p\n",
object, serialno, count ? (*count) : -1, aCOMPtr); object, serialno, count, aCOMPtr);
WalkTheStackCached(gCOMPtrLog); WalkTheStackCached(gCOMPtrLog);
} }
} }
@ -1346,22 +1192,10 @@ void
nsTraceRefcnt::Shutdown() nsTraceRefcnt::Shutdown()
{ {
gCodeAddressService = nullptr; gCodeAddressService = nullptr;
if (gBloatView) { gBloatView = nullptr;
PL_HashTableDestroy(gBloatView); gTypesToLog = nullptr;
gBloatView = nullptr; gObjectsToLog = nullptr;
} gSerialNumbers = nullptr;
if (gTypesToLog) {
PL_HashTableDestroy(gTypesToLog);
gTypesToLog = nullptr;
}
if (gObjectsToLog) {
PL_HashTableDestroy(gObjectsToLog);
gObjectsToLog = nullptr;
}
if (gSerialNumbers) {
PL_HashTableDestroy(gSerialNumbers);
gSerialNumbers = nullptr;
}
maybeUnregisterAndCloseFile(gBloatLog); maybeUnregisterAndCloseFile(gBloatLog);
maybeUnregisterAndCloseFile(gRefcntsLog); maybeUnregisterAndCloseFile(gRefcntsLog);
maybeUnregisterAndCloseFile(gAllocLog); maybeUnregisterAndCloseFile(gAllocLog);