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 "nsXPCOMPrivate.h"
#include "nscore.h"
#include "nsClassHashtable.h"
#include "nsISupports.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "nsTHashtable.h"
#include "prenv.h"
@ -52,8 +54,6 @@
////////////////////////////////////////////////////////////////////////////////
#include "plhash.h"
#include "prthread.h"
// 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; }
};
static PLHashTable* gBloatView;
static PLHashTable* gTypesToLog;
static PLHashTable* gObjectsToLog;
static PLHashTable* gSerialNumbers;
class BloatEntry;
struct SerialNumberRecord;
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 bool gDumpedStatistics = false;
static bool gLogJSStacks = false;
@ -205,56 +214,6 @@ AssertActivityIsLegal()
# define ASSERT_ACTIVITY_IS_LEGAL do { } while(0)
#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
@ -338,24 +297,6 @@ public:
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)
{
aTotal->mStats.mCreates += mStats.mCreates;
@ -411,29 +352,10 @@ protected:
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
RecreateBloatView()
{
gBloatView = PL_NewHashTable(256,
PL_HashString,
PL_CompareStrings,
PL_CompareValues,
&bloatViewHashAllocOps, nullptr);
gBloatView = new BloatHash(256);
}
static BloatEntry*
@ -442,48 +364,39 @@ GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
if (!gBloatView) {
RecreateBloatView();
}
BloatEntry* entry = nullptr;
if (gBloatView) {
entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
if (!entry && aInstanceSize > 0) {
entry = new BloatEntry(aTypeName, aInstanceSize);
PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
if (!e) {
delete entry;
entry = nullptr;
}
} else {
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.");
}
BloatEntry* entry = gBloatView->Get(aTypeName);
if (!entry && aInstanceSize > 0) {
entry = new BloatEntry(aTypeName, aInstanceSize);
gBloatView->Put(aTypeName, entry);
} else {
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;
}
static int
DumpSerialNumbers(PLHashEntry* aHashEntry, int aIndex, void* aClosure)
static void
DumpSerialNumbers(const SerialHash::Iterator& aHashEntry, FILE* aFd)
{
SerialNumberRecord* record =
static_cast<SerialNumberRecord*>(aHashEntry->value);
auto* outputFile = static_cast<FILE*>(aClosure);
SerialNumberRecord* record = aHashEntry.Data();
auto* outputFile = aFd;
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
fprintf(outputFile, "%" PRIdPTR
" @%p (%d references; %d from COMPtrs)\n",
record->serialNumber,
aHashEntry->key,
aHashEntry.Key(),
record->refCount,
record->COMPtrCount);
#else
fprintf(outputFile, "%" PRIdPTR
" @%p (%d references)\n",
record->serialNumber,
aHashEntry->key,
aHashEntry.Key(),
record->refCount);
#endif
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");
}
}
return HT_ENUMERATE_NEXT;
}
@ -545,7 +456,13 @@ nsTraceRefcnt::DumpStatistics()
gLogging = NoLogging;
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;
if (gLogLeaksOnly) {
msg = "ALL (cumulative) LEAK STATISTICS";
@ -555,7 +472,10 @@ nsTraceRefcnt::DumpStatistics()
const bool leaked = total.PrintDumpHeader(gBloatLog, msg);
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();
if (!gLogLeaksOnly || leaked) {
@ -574,7 +494,9 @@ nsTraceRefcnt::DumpStatistics()
if (gSerialNumbers) {
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;
@ -584,87 +506,40 @@ void
nsTraceRefcnt::ResetStatistics()
{
AutoTraceLogLock lock;
if (gBloatView) {
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));
gBloatView = nullptr;
}
static intptr_t
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) {
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);
PL_HashTableRawAdd(gSerialNumbers, hep, HashNumber(aPtr),
aPtr, static_cast<void*>(record));
if (gLogJSStacks) {
record->SaveJSStack();
}
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
RecycleSerialNumberPtr(void* aPtr)
{
PL_HashTableRemove(gSerialNumbers, aPtr);
gSerialNumbers->Remove(aPtr);
}
static bool
LogThisObj(intptr_t aSerialNumber)
{
return (bool)PL_HashTableLookup(gObjectsToLog, (const void*)aSerialNumber);
return gObjectsToLog->Contains(aSerialNumber);
}
using EnvCharType = mozilla::filesystem::Path::value_type;
@ -802,54 +677,33 @@ InitTraceLog()
if (classes) {
// if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
// as a list of class names to track
gTypesToLog = PL_NewHashTable(256,
PL_HashString,
PL_CompareStrings,
PL_CompareValues,
&typesToLogHashAllocOps, nullptr);
if (!gTypesToLog) {
NS_WARNING("out of memory");
fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
} 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;
gTypesToLog = new CharPtrSet(256);
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';
}
fprintf(stdout, "\n");
gTypesToLog->PutEntry(cp);
fprintf(stdout, "%s ", cp);
if (!cm) {
break;
}
*cm = ',';
cp = cm + 1;
}
fprintf(stdout, "\n");
gSerialNumbers = PL_NewHashTable(256,
HashNumber,
PL_CompareValues,
PL_CompareValues,
&serialNumberHashAllocOps, nullptr);
gSerialNumbers = new SerialHash(256);
}
const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
if (objects) {
gObjectsToLog = PL_NewHashTable(256,
HashNumber,
PL_CompareValues,
PL_CompareValues,
nullptr, nullptr);
gObjectsToLog = new IntPtrSet(256);
if (!gObjectsToLog) {
NS_WARNING("out of memory");
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
} else if (!(gRefcntsLog || gAllocLog || gCOMPtrLog)) {
if (!(gRefcntsLog || gAllocLog || gCOMPtrLog)) {
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
} else {
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
@ -875,7 +729,7 @@ InitTraceLog()
bottom = top;
}
for (intptr_t serialno = bottom; serialno <= top; serialno++) {
PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
gObjectsToLog->PutEntry(serialno);
fprintf(stdout, "%" PRIdPTR " ", serialno);
}
if (!cm) {
@ -1093,18 +947,17 @@ NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
// Here's the case where MOZ_COUNT_CTOR was not used,
// yet we still want to see creation information:
bool loggingThisType = (!gTypesToLog || LogThisType(aClass));
bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aClass));
intptr_t serialno = 0;
if (gSerialNumbers && loggingThisType) {
serialno = GetSerialNumber(aPtr, aRefcnt == 1);
MOZ_ASSERT(serialno != 0,
"Serial number requested for unrecognized pointer! "
"Are you memmoving a refcounted object?");
int32_t* count = GetRefCount(aPtr);
if (count) {
(*count)++;
auto record = gSerialNumbers->Get(aPtr);
if (record) {
++record->refCount;
}
}
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;
if (gSerialNumbers && loggingThisType) {
serialno = GetSerialNumber(aPtr, false);
MOZ_ASSERT(serialno != 0,
"Serial number requested for unrecognized pointer! "
"Are you memmoving a refcounted object?");
int32_t* count = GetRefCount(aPtr);
if (count) {
(*count)--;
auto record = gSerialNumbers->Get(aPtr);
if (record) {
--record->refCount;
}
}
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;
if (gSerialNumbers && loggingThisType) {
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;
if (gSerialNumbers && loggingThisType) {
serialno = GetSerialNumber(aPtr, false);
@ -1285,16 +1137,13 @@ NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
return;
}
int32_t* count = GetCOMPtrCount(object);
if (count) {
(*count)++;
}
auto record = gSerialNumbers->Get(object);
int32_t count = record ? ++record->COMPtrCount : -1;
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
if (gCOMPtrLog && loggingThisObject) {
fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrAddRef %d %p\n",
object, serialno, count ? (*count) : -1, aCOMPtr);
object, serialno, count, aCOMPtr);
WalkTheStackCached(gCOMPtrLog);
}
}
@ -1326,16 +1175,13 @@ NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
return;
}
int32_t* count = GetCOMPtrCount(object);
if (count) {
(*count)--;
}
auto record = gSerialNumbers->Get(object);
int32_t count = record ? --record->COMPtrCount : -1;
bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
if (gCOMPtrLog && loggingThisObject) {
fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrRelease %d %p\n",
object, serialno, count ? (*count) : -1, aCOMPtr);
object, serialno, count, aCOMPtr);
WalkTheStackCached(gCOMPtrLog);
}
}
@ -1346,22 +1192,10 @@ void
nsTraceRefcnt::Shutdown()
{
gCodeAddressService = nullptr;
if (gBloatView) {
PL_HashTableDestroy(gBloatView);
gBloatView = nullptr;
}
if (gTypesToLog) {
PL_HashTableDestroy(gTypesToLog);
gTypesToLog = nullptr;
}
if (gObjectsToLog) {
PL_HashTableDestroy(gObjectsToLog);
gObjectsToLog = nullptr;
}
if (gSerialNumbers) {
PL_HashTableDestroy(gSerialNumbers);
gSerialNumbers = nullptr;
}
gBloatView = nullptr;
gTypesToLog = nullptr;
gObjectsToLog = nullptr;
gSerialNumbers = nullptr;
maybeUnregisterAndCloseFile(gBloatLog);
maybeUnregisterAndCloseFile(gRefcntsLog);
maybeUnregisterAndCloseFile(gAllocLog);