Cleaned up nsTraceRefcnt environment variables. r=kipp,beard

This commit is contained in:
warren%netscape.com 1999-10-23 03:16:19 +00:00
Родитель 882fb6588b
Коммит 39ddb330f1
5 изменённых файлов: 387 добавлений и 443 удалений

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

@ -81,10 +81,9 @@ nsAboutBloat::NewChannel(const char *verb,
inStr = do_QueryInterface(s, &rv); inStr = do_QueryInterface(s, &rv);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
} }
else else if (leaks) {
if (leaks) { // dump the current set of leaks.
// dump the current set of leaks. GC_gcollect();
GC_gcollect();
nsCOMPtr<nsISupports> s; nsCOMPtr<nsISupports> s;
const char* msg = "Memory leaks dumped."; const char* msg = "Memory leaks dumped.";
@ -115,19 +114,14 @@ nsAboutBloat::NewChannel(const char *verb,
dumpFileName += time; dumpFileName += time;
file += (const char*)dumpFileName; file += (const char*)dumpFileName;
nsCOMPtr<nsISupports> out; FILE* out = ::fopen(file, "w");
rv = NS_NewTypicalOutputFileStream(getter_AddRefs(out), file); if (out == nsnull)
if (NS_FAILED(rv)) return rv; return NS_ERROR_FAILURE;
nsCOMPtr<nsIOutputStream> outStr = do_QueryInterface(out, &rv);
if (NS_FAILED(rv)) return rv;
rv = nsTraceRefcnt::DumpStatistics(statType, outStr); rv = nsTraceRefcnt::DumpStatistics(statType, out);
::fclose(out);
if (NS_FAILED(rv)) return rv; if (NS_FAILED(rv)) return rv;
// close the output stream to flush it to disk
outStr = null_nsCOMPtr();
out = null_nsCOMPtr();
size = file.GetFileSize(); size = file.GetFileSize();
nsCOMPtr<nsISupports> in; nsCOMPtr<nsISupports> in;

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

@ -24,8 +24,6 @@
#include "plstr.h" #include "plstr.h"
#include <stdlib.h> #include <stdlib.h>
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIOutputStream.h"
#include "nsIFileStream.h"
#include "nsCRT.h" #include "nsCRT.h"
#if defined(_WIN32) #if defined(_WIN32)
@ -66,24 +64,21 @@ static PRLock* gTraceLock;
#define UNLOCK_TRACELOG() #define UNLOCK_TRACELOG()
#endif /* ! NS_MT_SUPPORTED */ #endif /* ! NS_MT_SUPPORTED */
static PRLogModuleInfo* gTraceRefcntLog;
static PLHashTable* gBloatView; static PLHashTable* gBloatView;
static PLHashTable* gTypesToLog; static PLHashTable* gTypesToLog;
static PRBool gLogging; static PRBool gLogging;
static PRBool gLogAllRefcnts;
static PRBool gLogSomeRefcnts;
static PRBool gLogToLeaky; static PRBool gLogToLeaky;
static PRBool gTrackBloat; static PRBool gLogLeaksOnly;
static PRBool gLogCalls;
static PRBool gLogNewAndDelete;
static PRBool gDumpLeaksOnly;
static void (*leakyLogAddRef)(void* p, int oldrc, int newrc); static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
static void (*leakyLogRelease)(void* p, int oldrc, int newrc); static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
static FILE *gLoggingStream = stderr; static PRBool gInitialized = PR_FALSE;
static FILE *gBloatLog = nsnull;
static FILE *gRefcntsLog = nsnull;
static FILE *gAllocLog = nsnull;
static FILE *gLeakyLog = nsnull;
#define XPCOM_REFCNT_TRACK_BLOAT 0x1 #define XPCOM_REFCNT_TRACK_BLOAT 0x1
#define XPCOM_REFCNT_LOG_ALL 0x2 #define XPCOM_REFCNT_LOG_ALL 0x2
@ -173,7 +168,7 @@ public:
static PRIntn DumpNewEntry(PLHashEntry *he, PRIntn i, void *arg) { static PRIntn DumpNewEntry(PLHashEntry *he, PRIntn i, void *arg) {
BloatEntry* entry = (BloatEntry*)he->value; BloatEntry* entry = (BloatEntry*)he->value;
if (entry) { if (entry) {
nsresult rv = entry->Dump(i, (nsIOutputStream*)arg, &entry->mNewStats); nsresult rv = entry->Dump(i, (FILE*)arg, &entry->mNewStats);
NS_ASSERTION(NS_SUCCEEDED(rv), "Dump failed"); NS_ASSERTION(NS_SUCCEEDED(rv), "Dump failed");
entry->Accumulate(); entry->Accumulate();
} }
@ -184,7 +179,7 @@ public:
BloatEntry* entry = (BloatEntry*)he->value; BloatEntry* entry = (BloatEntry*)he->value;
if (entry) { if (entry) {
entry->Accumulate(); entry->Accumulate();
nsresult rv = entry->Dump(i, (nsIOutputStream*)arg, &entry->mAllStats); nsresult rv = entry->Dump(i, (FILE*)arg, &entry->mAllStats);
NS_ASSERTION(NS_SUCCEEDED(rv), "Dump failed"); NS_ASSERTION(NS_SUCCEEDED(rv), "Dump failed");
} }
return HT_ENUMERATE_NEXT; return HT_ENUMERATE_NEXT;
@ -210,7 +205,7 @@ public:
total->mClassSize += mClassSize; // adjust for average in DumpTotal total->mClassSize += mClassSize; // adjust for average in DumpTotal
} }
nsresult DumpTotal(PRUint32 nClasses, nsIOutputStream* out) { nsresult DumpTotal(PRUint32 nClasses, FILE* out) {
mClassSize /= nClasses; mClassSize /= nClasses;
return Dump(-1, out, &mAllStats); return Dump(-1, out, &mAllStats);
} }
@ -242,30 +237,17 @@ public:
(stats->mCreates != stats->mDestroys)); (stats->mCreates != stats->mDestroys));
} }
static nsresult PrintDumpHeader(nsIOutputStream* out, const char* msg) { static nsresult PrintDumpHeader(FILE* out, const char* msg) {
nsresult rv; fprintf(out, "\n== BloatView: %s\n\n", msg);
char buf[256]; fprintf(out,
PRUint32 cnt, writeCnt;
cnt = PR_snprintf(buf, 256,
" %s -- Bloaty: Refcounting and Memory Bloat Statistics\n", msg);
rv = out->Write(buf, cnt, &writeCnt);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(cnt == writeCnt, "failed to write all data");
cnt = PR_snprintf(buf, 256,
" |<------Class----->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n"); " |<------Class----->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n");
rv = out->Write(buf, cnt, &writeCnt); fprintf(out,
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(cnt == writeCnt, "failed to write all data");
cnt = PR_snprintf(buf, 256,
" Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n"); " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
rv = out->Write(buf, cnt, &writeCnt);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(cnt == writeCnt, "failed to write all data");
return NS_OK; return NS_OK;
} }
nsresult Dump(PRIntn i, nsIOutputStream* out, nsTraceRefcntStats* stats) { nsresult Dump(PRIntn i, FILE* out, nsTraceRefcntStats* stats) {
if (gDumpLeaksOnly && !HaveLeaks(stats)) { if (gLogLeaksOnly && !HaveLeaks(stats)) {
return NS_OK; return NS_OK;
} }
double nRefs = stats->mAddRefs + stats->mReleases; double nRefs = stats->mAddRefs + stats->mReleases;
@ -289,23 +271,18 @@ public:
stats->mCreates != 0 || stats->mCreates != 0 ||
meanObjs != 0 || meanObjs != 0 ||
stddevObjs != 0) { stddevObjs != 0) {
char buf[256]; fprintf(out, "%4d %-20.20s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
PRUint32 cnt, writeCnt; i+1, mClassName,
cnt = PR_snprintf(buf, 256, "%4d %-20.20s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n", mClassSize,
i+1, mClassName, (stats->mCreates - stats->mDestroys) * mClassSize,
mClassSize, stats->mCreates,
(stats->mCreates - stats->mDestroys) * mClassSize, (stats->mCreates - stats->mDestroys),
stats->mCreates, meanObjs,
(stats->mCreates - stats->mDestroys), stddevObjs,
meanObjs, stats->mAddRefs,
stddevObjs, (stats->mAddRefs - stats->mReleases),
stats->mAddRefs, meanRefs,
(stats->mAddRefs - stats->mReleases), stddevRefs);
meanRefs,
stddevRefs);
nsresult rv = out->Write(buf, cnt, &writeCnt);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(cnt == writeCnt, "failed to write all data");
} }
return NS_OK; return NS_OK;
} }
@ -351,50 +328,47 @@ GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize)
#endif /* NS_BUILD_REFCNT_LOGGING */ #endif /* NS_BUILD_REFCNT_LOGGING */
nsresult nsresult
nsTraceRefcnt::DumpStatistics(StatisticsType type, nsTraceRefcnt::DumpStatistics(StatisticsType type, FILE* out)
nsIOutputStream* out)
{ {
nsresult rv = NS_OK; nsresult rv = NS_OK;
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (!gTrackBloat || !gBloatView) { if (gBloatLog == nsnull || gBloatView == nsnull) {
return NS_OK; fprintf(stdout, "### ERROR: Can't dump bloat statistics because XPCOM_MEM_BLOAT_LOG isn't set.\n");
return NS_ERROR_FAILURE;
}
if (out == nsnull) {
out = gBloatLog;
} }
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gDumpLeaksOnly) {
fprintf(gLoggingStream, "Bloaty: Only dumping data about objects that leaked\n");
}
PRBool wasLogging = gLogging; PRBool wasLogging = gLogging;
gLogging = PR_FALSE; // turn off logging for this method gLogging = PR_FALSE; // turn off logging for this method
BloatEntry total("TOTAL", 0); BloatEntry total("TOTAL", 0);
nsCOMPtr<nsIOutputStream> outStr = dont_QueryInterface(out);
if (out == nsnull) {
nsCOMPtr<nsISupports> outSupports;
rv = NS_NewOutputConsoleStream(getter_AddRefs(outSupports));
if (NS_FAILED(rv)) goto done;
outStr = do_QueryInterface(outSupports, &rv);
if (NS_FAILED(rv)) goto done;
}
PRIntn (*dump)(PLHashEntry *he, PRIntn i, void *arg); PRIntn (*dump)(PLHashEntry *he, PRIntn i, void *arg);
const char* msg; const char* msg;
if (type == NEW_STATS) { if (type == NEW_STATS) {
dump = BloatEntry::DumpNewEntry; dump = BloatEntry::DumpNewEntry;
msg = "NEW RESULTS"; if (gLogLeaksOnly)
msg = "NEW (incremental) LEAK STATISTICS";
else
msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
} }
else { else {
dump = BloatEntry::DumpAllEntry; dump = BloatEntry::DumpAllEntry;
msg = "ALL RESULTS"; if (gLogLeaksOnly)
msg = "ALL (cumulative) LEAK STATISTICS";
else
msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
} }
rv = BloatEntry::PrintDumpHeader(outStr, msg); rv = BloatEntry::PrintDumpHeader(out, msg);
if (NS_FAILED(rv)) goto done; if (NS_FAILED(rv)) goto done;
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total); PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
total.DumpTotal(gBloatView->nentries, outStr); total.DumpTotal(gBloatView->nentries, out);
PL_HashTableEnumerateEntries(gBloatView, dump, outStr); PL_HashTableEnumerateEntries(gBloatView, dump, out);
done: done:
gLogging = wasLogging; gLogging = wasLogging;
@ -443,115 +417,118 @@ static PRBool LogThisType(const char* aTypeName)
return nsnull != he; return nsnull != he;
} }
static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
{
const char* value = getenv(envVar);
if (value) {
if (nsCRT::strcmp(value, "1") == 0) {
*result = stdout;
fprintf(stdout, "### %s defined -- logging %s to stdout\n",
envVar, msg);
return PR_TRUE;
}
else if (nsCRT::strcmp(value, "2") == 0) {
*result = stderr;
fprintf(stdout, "### %s defined -- logging %s to stderr\n",
envVar, msg);
return PR_TRUE;
}
else {
FILE *stream = ::fopen(value, "w");
if (stream != NULL) {
*result = stream;
fprintf(stdout, "### %s defined -- logging %s to %s\n",
envVar, msg, value);
return PR_TRUE;
}
else {
fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
envVar, msg, value);
return PR_FALSE;
}
}
}
return PR_FALSE;
}
static void InitTraceLog(void) static void InitTraceLog(void)
{ {
if (0 == gTraceRefcntLog) { if (gInitialized) return;
gTraceRefcntLog = PR_NewLogModule("xpcomrefcnt"); gInitialized = PR_TRUE;
if (getenv("MOZ_DUMP_LEAKS")) { PRBool defined;
gDumpLeaksOnly = PR_TRUE; defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
if (!defined)
gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
if (defined || gLogLeaksOnly) {
RecreateBloatView();
if (NS_WARN_IF_FALSE(gBloatView, "out of memory")) {
gBloatLog = nsnull;
gLogLeaksOnly = PR_FALSE;
} }
}
// See if user is redirecting the trace. (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
const char* traceLogName = getenv("MOZ_TRACE_LOG");
if (traceLogName) {
FILE *stream = ::fopen(traceLogName, "w");
if (stream != NULL)
gLoggingStream = stream;
}
// See if bloaty is enabled (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
if (XPCOM_REFCNT_TRACK_BLOAT & gTraceRefcntLog->level) {
gTrackBloat = PR_TRUE;
RecreateBloatView();
if (NS_WARN_IF_FALSE(gBloatView, "out of memory")) {
gTrackBloat = PR_FALSE;
}
else {
fprintf(gLoggingStream, "XPCOM: using turbo mega bloatvision\n");
}
}
// See if raw nspr logging is enabled defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
if (XPCOM_REFCNT_LOG_ALL & gTraceRefcntLog->level) { if (defined) {
gLogAllRefcnts = PR_TRUE; gLogToLeaky = PR_TRUE;
fprintf(gLoggingStream, "XPCOM: logging all refcnt calls\n"); void* p = nsnull;
} void* q = nsnull;
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 (XP_PC) || defined(XP_MAC)
char* types = getenv("MOZ_TRACE_REFCNT_TYPES");
if (types) {
fprintf(gLoggingStream, "XPCOM: logging some refcnt calls: ");
char* cp = types;
for (;;) {
char* cm = strchr(cp, ',');
if (cm) {
*cm = '\0';
}
PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
fprintf(gLoggingStream, "%s ", cp);
if (!cm) break;
*cm = ',';
cp = cm + 1;
}
fprintf(gLoggingStream, "\n");
}
else {
fprintf(gLoggingStream, "XPCOM: MOZ_TRACE_REFCNTS_TYPE 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 #ifdef HAVE_LIBDL
void* p = dlsym(0, "__log_addref"); p = dlsym(0, "__log_addref");
if (p) { q = dlsym(0, "__log_release");
leakyLogAddRef = (void (*)(void*,int,int)) p;
p = dlsym(0, "__log_release");
if (p) {
leakyLogRelease = (void (*)(void*,int,int)) p;
fprintf(gLoggingStream, "XPCOM: logging addref/release calls to leaky\n");
}
else {
gLogToLeaky = PR_FALSE;
}
}
else {
gLogToLeaky = PR_FALSE;
}
#endif #endif
if (p && q) {
leakyLogAddRef = (void (*)(void*,int,int)) p;
leakyLogRelease = (void (*)(void*,int,int)) q;
} }
else {
gLogToLeaky = PR_FALSE;
fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
}
}
if (gTrackBloat || gLogAllRefcnts || gLogSomeRefcnts || const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
gLogCalls || gLogNewAndDelete) { if (classes) {
gLogging = PR_TRUE; // if XPCOM_LOG_REFCNTS 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,
NULL, NULL);
if (NS_WARN_IF_FALSE(gTypesToLog, "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 = strchr(cp, ',');
if (cm) {
*cm = '\0';
}
PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
fprintf(stdout, "%s ", cp);
if (!cm) break;
*cm = ',';
cp = cm + 1;
}
fprintf(stdout, "\n");
}
}
if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog) {
gLogging = PR_TRUE;
}
#if defined(NS_MT_SUPPORTED) #if defined(NS_MT_SUPPORTED)
gTraceLock = PR_NewLock(); gTraceLock = PR_NewLock();
#endif /* NS_MT_SUPPORTED */ #endif /* NS_MT_SUPPORTED */
}
} }
#endif #endif
@ -902,9 +879,10 @@ nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */ #if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) {
if (gAllocLog || gRefcntsLog) {
HANDLE myProcess = ::GetCurrentProcess(); HANDLE myProcess = ::GetCurrentProcess();
if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) { if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) {
@ -919,7 +897,7 @@ nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName,
0); 0);
// DWORD lastError = 0; // DWORD lastError = 0;
// if (!b) lastError = ::GetLastError(); // if (!b) lastError = ::GetLastError();
// fprintf(gLoggingStream, "loading symbols for library %s => %s [%d]\n", aLibraryName, // fprintf(gLogStream, "loading symbols for library %s => %s [%d]\n", aLibraryName,
// b ? "true" : "false", lastError); // b ? "true" : "false", lastError);
} }
#endif #endif
@ -941,12 +919,12 @@ nsTraceRefcnt::LogAddRef(void* aPtr,
PRUint32 classSize) PRUint32 classSize)
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogging) { if (gLogging) {
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gTrackBloat) { if (gBloatLog) {
BloatEntry* entry = GetBloatEntry(aClazz, classSize); BloatEntry* entry = GetBloatEntry(aClazz, classSize);
if (entry) { if (entry) {
entry->AddRef(aRefCnt); entry->AddRef(aRefCnt);
@ -959,23 +937,23 @@ nsTraceRefcnt::LogAddRef(void* aPtr,
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogNewXPCOM instead to get file and line numbers.) // using LogNewXPCOM instead to get file and line numbers.)
PRBool loggingThisType = (gTypesToLog && LogThisType(aClazz)); PRBool loggingThisType = (gTypesToLog && LogThisType(aClazz));
if (aRefCnt == 1 && (gLogNewAndDelete || loggingThisType)) { if (aRefCnt == 1 && (gAllocLog || loggingThisType)) {
fprintf(gLoggingStream, "\n<%s> 0x%08X Create\n", fprintf(gAllocLog, "\n<%s> 0x%08X Create\n",
aClazz, PRInt32(aPtr)); aClazz, PRInt32(aPtr));
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
} }
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogAddRefCall instead to get file and line numbers.) // using LogAddRefCall instead to get file and line numbers.)
if (gLogAllRefcnts || loggingThisType) { if (gRefcntsLog || loggingThisType) {
if (gLogToLeaky) { if (gLogToLeaky) {
(*leakyLogAddRef)(aPtr, aRefCnt - 1, aRefCnt); (*leakyLogAddRef)(aPtr, aRefCnt - 1, aRefCnt);
} }
else { else {
// Can't use PR_LOG(), b/c it truncates the line // Can't use PR_LOG(), b/c it truncates the line
fprintf(gLoggingStream, fprintf(gRefcntsLog,
"\n<%s> 0x%08X AddRef %d\n", aClazz, PRInt32(aPtr), aRefCnt); "\n<%s> 0x%08X AddRef %d\n", aClazz, PRInt32(aPtr), aRefCnt);
WalkTheStack(gLoggingStream); WalkTheStack(gRefcntsLog);
} }
} }
#endif #endif
@ -992,12 +970,12 @@ nsTraceRefcnt::LogRelease(void* aPtr,
const char* aClazz) const char* aClazz)
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogging) { if (gLogging) {
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gTrackBloat) { if (gBloatLog) {
BloatEntry* entry = GetBloatEntry(aClazz, (PRUint32)-1); BloatEntry* entry = GetBloatEntry(aClazz, (PRUint32)-1);
if (entry) { if (entry) {
entry->Release(aRefCnt); entry->Release(aRefCnt);
@ -1008,15 +986,15 @@ nsTraceRefcnt::LogRelease(void* aPtr,
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogReleaseCall instead to get file and line numbers.) // using LogReleaseCall instead to get file and line numbers.)
PRBool loggingThisType = (gTypesToLog && LogThisType(aClazz)); PRBool loggingThisType = (gTypesToLog && LogThisType(aClazz));
if (gLogAllRefcnts || loggingThisType) { if (gRefcntsLog || loggingThisType) {
if (gLogToLeaky) { if (gLogToLeaky) {
(*leakyLogRelease)(aPtr, aRefCnt + 1, aRefCnt); (*leakyLogRelease)(aPtr, aRefCnt + 1, aRefCnt);
} }
else { else {
// Can't use PR_LOG(), b/c it truncates the line // Can't use PR_LOG(), b/c it truncates the line
fprintf(gLoggingStream, fprintf(gRefcntsLog,
"\n<%s> 0x%08X Release %d\n", aClazz, PRInt32(aPtr), aRefCnt); "\n<%s> 0x%08X Release %d\n", aClazz, PRInt32(aPtr), aRefCnt);
WalkTheStack(gLoggingStream); WalkTheStack(gRefcntsLog);
} }
} }
@ -1025,11 +1003,11 @@ nsTraceRefcnt::LogRelease(void* aPtr,
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogDeleteXPCOM instead to get file and line numbers.) // using LogDeleteXPCOM instead to get file and line numbers.)
if (aRefCnt == 0 && (gLogNewAndDelete || loggingThisType)) { if (aRefCnt == 0 && (gAllocLog || loggingThisType)) {
fprintf(gLoggingStream, fprintf(gAllocLog,
"\n<%s> 0x%08X Destroy\n", "\n<%s> 0x%08X Destroy\n",
aClazz, PRInt32(aPtr)); aClazz, PRInt32(aPtr));
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
} }
#endif #endif
@ -1046,14 +1024,14 @@ nsTraceRefcnt::LogAddRefCall(void* aPtr,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#ifdef NS_LOSING_ARCHITECTURE #ifdef NS_LOSING_ARCHITECTURE
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogCalls) { if (gRefcntsLog) {
LOCK_TRACELOG(); LOCK_TRACELOG();
fprintf(gLoggingStream, "\n<Call> 0x%08X AddRef %d=>%d in %s (line %d)\n", fprintf(gRefcntsLog, "\n<Call> 0x%08X AddRef %d=>%d in %s (line %d)\n",
aPtr, aNewRefcnt-1, aNewRefcnt, aFile, aLine); aPtr, aNewRefcnt-1, aNewRefcnt, aFile, aLine);
WalkTheStack(gLoggingStream); WalkTheStack(gRefcntsLog);
UNLOCK_TRACELOG(); UNLOCK_TRACELOG();
} }
@ -1070,15 +1048,15 @@ nsTraceRefcnt::LogReleaseCall(void* aPtr,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#ifdef NS_LOSING_ARCHITECTURE #ifdef NS_LOSING_ARCHITECTURE
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogCalls) { if (gRefcntsLog) {
LOCK_TRACELOG(); LOCK_TRACELOG();
fprintf(gLoggingStream, "\n<Call> 0x%08X Release %d=>%d in %s (line %d)\n", fprintf(gRefcntsLog, "\n<Call> 0x%08X Release %d=>%d in %s (line %d)\n",
aPtr, aNewRefcnt+1, aNewRefcnt, aFile, aLine); aPtr, aNewRefcnt+1, aNewRefcnt, aFile, aLine);
WalkTheStack(gLoggingStream); WalkTheStack(gRefcntsLog);
UNLOCK_TRACELOG(); UNLOCK_TRACELOG();
} }
@ -1096,15 +1074,15 @@ nsTraceRefcnt::LogNewXPCOM(void* aPtr,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#ifdef NS_LOSING_ARCHITECTURE #ifdef NS_LOSING_ARCHITECTURE
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogNewAndDelete) { if (gAllocLog) {
LOCK_TRACELOG(); LOCK_TRACELOG();
fprintf(gLoggingStream, "\n<%s> 0x%08X NewXPCOM in %s (line %d)\n", fprintf(gAllocLog, "\n<%s> 0x%08X NewXPCOM in %s (line %d)\n",
aType, aPtr, aFile, aLine); aType, aPtr, aFile, aLine);
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
UNLOCK_TRACELOG(); UNLOCK_TRACELOG();
} }
@ -1119,15 +1097,15 @@ nsTraceRefcnt::LogDeleteXPCOM(void* aPtr,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#ifdef NS_LOSING_ARCHITECTURE #ifdef NS_LOSING_ARCHITECTURE
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogNewAndDelete) { if (gAllocLog) {
LOCK_TRACELOG(); LOCK_TRACELOG();
fprintf(gLoggingStream, "\n<%s> 0x%08X Destroy in %s (line %d)\n", fprintf(gAllocLog, "\n<%s> 0x%08X Destroy in %s (line %d)\n",
aType, aPtr, aFile, aLine); "?", aPtr, aFile, aLine);
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
UNLOCK_TRACELOG(); UNLOCK_TRACELOG();
} }
@ -1141,13 +1119,13 @@ nsTraceRefcnt::LogCtor(void* aPtr,
PRUint32 aInstanceSize) PRUint32 aInstanceSize)
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogging) { if (gLogging) {
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gTrackBloat) { if (gBloatLog) {
BloatEntry* entry = GetBloatEntry(aType, aInstanceSize); BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
if (entry) { if (entry) {
entry->Ctor(); entry->Ctor();
@ -1157,10 +1135,10 @@ nsTraceRefcnt::LogCtor(void* aPtr,
#ifndef NS_LOSING_ARCHITECTURE #ifndef NS_LOSING_ARCHITECTURE
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogNewXPCOM instead to get file and line numbers.) // using LogNewXPCOM instead to get file and line numbers.)
if (gLogNewAndDelete || (gTypesToLog && LogThisType(aType))) { if (gAllocLog || (gTypesToLog && LogThisType(aType))) {
fprintf(gLoggingStream, "\n<%s> 0x%08X Ctor (%d)\n", fprintf(gAllocLog, "\n<%s> 0x%08X Ctor (%d)\n",
aType, PRInt32(aPtr), aInstanceSize); aType, PRInt32(aPtr), aInstanceSize);
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
} }
#endif #endif
@ -1174,13 +1152,13 @@ nsTraceRefcnt::LogDtor(void* aPtr, const char* aType,
PRUint32 aInstanceSize) PRUint32 aInstanceSize)
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogging) { if (gLogging) {
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gTrackBloat) { if (gBloatLog) {
BloatEntry* entry = GetBloatEntry(aType, aInstanceSize); BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
if (entry) { if (entry) {
entry->Dtor(); entry->Dtor();
@ -1190,10 +1168,10 @@ nsTraceRefcnt::LogDtor(void* aPtr, const char* aType,
#ifndef NS_LOSING_ARCHITECTURE #ifndef NS_LOSING_ARCHITECTURE
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogDeleteXPCOM instead to get file and line numbers.) // using LogDeleteXPCOM instead to get file and line numbers.)
if (gLogNewAndDelete || (gTypesToLog && LogThisType(aType))) { if (gAllocLog || (gTypesToLog && LogThisType(aType))) {
fprintf(gLoggingStream, "\n<%s> 0x%08X Dtor (%d)\n", fprintf(gAllocLog, "\n<%s> 0x%08X Dtor (%d)\n",
aType, PRInt32(aPtr), aInstanceSize); aType, PRInt32(aPtr), aInstanceSize);
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
} }
#endif #endif

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

@ -107,8 +107,6 @@ PR_END_MACRO
//---------------------------------------------------------------------- //----------------------------------------------------------------------
class nsIOutputStream;
struct nsTraceRefcntStats { struct nsTraceRefcntStats {
nsrefcnt mAddRefs; nsrefcnt mAddRefs;
nsrefcnt mReleases; nsrefcnt mReleases;
@ -182,7 +180,7 @@ public:
}; };
static NS_COM nsresult DumpStatistics(StatisticsType type = ALL_STATS, static NS_COM nsresult DumpStatistics(StatisticsType type = ALL_STATS,
nsIOutputStream* out = 0); FILE* out = 0);
static NS_COM void ResetStatistics(void); static NS_COM void ResetStatistics(void);
@ -196,7 +194,6 @@ public:
char * aBuffer, char * aBuffer,
int aBufLen); int aBufLen);
// XXX change this to take an nsIOutputStream
static NS_COM void WalkTheStack(FILE* aStream); static NS_COM void WalkTheStack(FILE* aStream);
}; };

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

@ -24,8 +24,6 @@
#include "plstr.h" #include "plstr.h"
#include <stdlib.h> #include <stdlib.h>
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIOutputStream.h"
#include "nsIFileStream.h"
#include "nsCRT.h" #include "nsCRT.h"
#if defined(_WIN32) #if defined(_WIN32)
@ -66,24 +64,21 @@ static PRLock* gTraceLock;
#define UNLOCK_TRACELOG() #define UNLOCK_TRACELOG()
#endif /* ! NS_MT_SUPPORTED */ #endif /* ! NS_MT_SUPPORTED */
static PRLogModuleInfo* gTraceRefcntLog;
static PLHashTable* gBloatView; static PLHashTable* gBloatView;
static PLHashTable* gTypesToLog; static PLHashTable* gTypesToLog;
static PRBool gLogging; static PRBool gLogging;
static PRBool gLogAllRefcnts;
static PRBool gLogSomeRefcnts;
static PRBool gLogToLeaky; static PRBool gLogToLeaky;
static PRBool gTrackBloat; static PRBool gLogLeaksOnly;
static PRBool gLogCalls;
static PRBool gLogNewAndDelete;
static PRBool gDumpLeaksOnly;
static void (*leakyLogAddRef)(void* p, int oldrc, int newrc); static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
static void (*leakyLogRelease)(void* p, int oldrc, int newrc); static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
static FILE *gLoggingStream = stderr; static PRBool gInitialized = PR_FALSE;
static FILE *gBloatLog = nsnull;
static FILE *gRefcntsLog = nsnull;
static FILE *gAllocLog = nsnull;
static FILE *gLeakyLog = nsnull;
#define XPCOM_REFCNT_TRACK_BLOAT 0x1 #define XPCOM_REFCNT_TRACK_BLOAT 0x1
#define XPCOM_REFCNT_LOG_ALL 0x2 #define XPCOM_REFCNT_LOG_ALL 0x2
@ -173,7 +168,7 @@ public:
static PRIntn DumpNewEntry(PLHashEntry *he, PRIntn i, void *arg) { static PRIntn DumpNewEntry(PLHashEntry *he, PRIntn i, void *arg) {
BloatEntry* entry = (BloatEntry*)he->value; BloatEntry* entry = (BloatEntry*)he->value;
if (entry) { if (entry) {
nsresult rv = entry->Dump(i, (nsIOutputStream*)arg, &entry->mNewStats); nsresult rv = entry->Dump(i, (FILE*)arg, &entry->mNewStats);
NS_ASSERTION(NS_SUCCEEDED(rv), "Dump failed"); NS_ASSERTION(NS_SUCCEEDED(rv), "Dump failed");
entry->Accumulate(); entry->Accumulate();
} }
@ -184,7 +179,7 @@ public:
BloatEntry* entry = (BloatEntry*)he->value; BloatEntry* entry = (BloatEntry*)he->value;
if (entry) { if (entry) {
entry->Accumulate(); entry->Accumulate();
nsresult rv = entry->Dump(i, (nsIOutputStream*)arg, &entry->mAllStats); nsresult rv = entry->Dump(i, (FILE*)arg, &entry->mAllStats);
NS_ASSERTION(NS_SUCCEEDED(rv), "Dump failed"); NS_ASSERTION(NS_SUCCEEDED(rv), "Dump failed");
} }
return HT_ENUMERATE_NEXT; return HT_ENUMERATE_NEXT;
@ -210,7 +205,7 @@ public:
total->mClassSize += mClassSize; // adjust for average in DumpTotal total->mClassSize += mClassSize; // adjust for average in DumpTotal
} }
nsresult DumpTotal(PRUint32 nClasses, nsIOutputStream* out) { nsresult DumpTotal(PRUint32 nClasses, FILE* out) {
mClassSize /= nClasses; mClassSize /= nClasses;
return Dump(-1, out, &mAllStats); return Dump(-1, out, &mAllStats);
} }
@ -242,30 +237,17 @@ public:
(stats->mCreates != stats->mDestroys)); (stats->mCreates != stats->mDestroys));
} }
static nsresult PrintDumpHeader(nsIOutputStream* out, const char* msg) { static nsresult PrintDumpHeader(FILE* out, const char* msg) {
nsresult rv; fprintf(out, "\n== BloatView: %s\n\n", msg);
char buf[256]; fprintf(out,
PRUint32 cnt, writeCnt;
cnt = PR_snprintf(buf, 256,
" %s -- Bloaty: Refcounting and Memory Bloat Statistics\n", msg);
rv = out->Write(buf, cnt, &writeCnt);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(cnt == writeCnt, "failed to write all data");
cnt = PR_snprintf(buf, 256,
" |<------Class----->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n"); " |<------Class----->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n");
rv = out->Write(buf, cnt, &writeCnt); fprintf(out,
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(cnt == writeCnt, "failed to write all data");
cnt = PR_snprintf(buf, 256,
" Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n"); " Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
rv = out->Write(buf, cnt, &writeCnt);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(cnt == writeCnt, "failed to write all data");
return NS_OK; return NS_OK;
} }
nsresult Dump(PRIntn i, nsIOutputStream* out, nsTraceRefcntStats* stats) { nsresult Dump(PRIntn i, FILE* out, nsTraceRefcntStats* stats) {
if (gDumpLeaksOnly && !HaveLeaks(stats)) { if (gLogLeaksOnly && !HaveLeaks(stats)) {
return NS_OK; return NS_OK;
} }
double nRefs = stats->mAddRefs + stats->mReleases; double nRefs = stats->mAddRefs + stats->mReleases;
@ -289,23 +271,18 @@ public:
stats->mCreates != 0 || stats->mCreates != 0 ||
meanObjs != 0 || meanObjs != 0 ||
stddevObjs != 0) { stddevObjs != 0) {
char buf[256]; fprintf(out, "%4d %-20.20s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
PRUint32 cnt, writeCnt; i+1, mClassName,
cnt = PR_snprintf(buf, 256, "%4d %-20.20s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n", mClassSize,
i+1, mClassName, (stats->mCreates - stats->mDestroys) * mClassSize,
mClassSize, stats->mCreates,
(stats->mCreates - stats->mDestroys) * mClassSize, (stats->mCreates - stats->mDestroys),
stats->mCreates, meanObjs,
(stats->mCreates - stats->mDestroys), stddevObjs,
meanObjs, stats->mAddRefs,
stddevObjs, (stats->mAddRefs - stats->mReleases),
stats->mAddRefs, meanRefs,
(stats->mAddRefs - stats->mReleases), stddevRefs);
meanRefs,
stddevRefs);
nsresult rv = out->Write(buf, cnt, &writeCnt);
if (NS_FAILED(rv)) return rv;
NS_ASSERTION(cnt == writeCnt, "failed to write all data");
} }
return NS_OK; return NS_OK;
} }
@ -351,50 +328,47 @@ GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize)
#endif /* NS_BUILD_REFCNT_LOGGING */ #endif /* NS_BUILD_REFCNT_LOGGING */
nsresult nsresult
nsTraceRefcnt::DumpStatistics(StatisticsType type, nsTraceRefcnt::DumpStatistics(StatisticsType type, FILE* out)
nsIOutputStream* out)
{ {
nsresult rv = NS_OK; nsresult rv = NS_OK;
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (!gTrackBloat || !gBloatView) { if (gBloatLog == nsnull || gBloatView == nsnull) {
return NS_OK; fprintf(stdout, "### ERROR: Can't dump bloat statistics because XPCOM_MEM_BLOAT_LOG isn't set.\n");
return NS_ERROR_FAILURE;
}
if (out == nsnull) {
out = gBloatLog;
} }
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gDumpLeaksOnly) {
fprintf(gLoggingStream, "Bloaty: Only dumping data about objects that leaked\n");
}
PRBool wasLogging = gLogging; PRBool wasLogging = gLogging;
gLogging = PR_FALSE; // turn off logging for this method gLogging = PR_FALSE; // turn off logging for this method
BloatEntry total("TOTAL", 0); BloatEntry total("TOTAL", 0);
nsCOMPtr<nsIOutputStream> outStr = dont_QueryInterface(out);
if (out == nsnull) {
nsCOMPtr<nsISupports> outSupports;
rv = NS_NewOutputConsoleStream(getter_AddRefs(outSupports));
if (NS_FAILED(rv)) goto done;
outStr = do_QueryInterface(outSupports, &rv);
if (NS_FAILED(rv)) goto done;
}
PRIntn (*dump)(PLHashEntry *he, PRIntn i, void *arg); PRIntn (*dump)(PLHashEntry *he, PRIntn i, void *arg);
const char* msg; const char* msg;
if (type == NEW_STATS) { if (type == NEW_STATS) {
dump = BloatEntry::DumpNewEntry; dump = BloatEntry::DumpNewEntry;
msg = "NEW RESULTS"; if (gLogLeaksOnly)
msg = "NEW (incremental) LEAK STATISTICS";
else
msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
} }
else { else {
dump = BloatEntry::DumpAllEntry; dump = BloatEntry::DumpAllEntry;
msg = "ALL RESULTS"; if (gLogLeaksOnly)
msg = "ALL (cumulative) LEAK STATISTICS";
else
msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
} }
rv = BloatEntry::PrintDumpHeader(outStr, msg); rv = BloatEntry::PrintDumpHeader(out, msg);
if (NS_FAILED(rv)) goto done; if (NS_FAILED(rv)) goto done;
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total); PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
total.DumpTotal(gBloatView->nentries, outStr); total.DumpTotal(gBloatView->nentries, out);
PL_HashTableEnumerateEntries(gBloatView, dump, outStr); PL_HashTableEnumerateEntries(gBloatView, dump, out);
done: done:
gLogging = wasLogging; gLogging = wasLogging;
@ -443,115 +417,118 @@ static PRBool LogThisType(const char* aTypeName)
return nsnull != he; return nsnull != he;
} }
static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
{
const char* value = getenv(envVar);
if (value) {
if (nsCRT::strcmp(value, "1") == 0) {
*result = stdout;
fprintf(stdout, "### %s defined -- logging %s to stdout\n",
envVar, msg);
return PR_TRUE;
}
else if (nsCRT::strcmp(value, "2") == 0) {
*result = stderr;
fprintf(stdout, "### %s defined -- logging %s to stderr\n",
envVar, msg);
return PR_TRUE;
}
else {
FILE *stream = ::fopen(value, "w");
if (stream != NULL) {
*result = stream;
fprintf(stdout, "### %s defined -- logging %s to %s\n",
envVar, msg, value);
return PR_TRUE;
}
else {
fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
envVar, msg, value);
return PR_FALSE;
}
}
}
return PR_FALSE;
}
static void InitTraceLog(void) static void InitTraceLog(void)
{ {
if (0 == gTraceRefcntLog) { if (gInitialized) return;
gTraceRefcntLog = PR_NewLogModule("xpcomrefcnt"); gInitialized = PR_TRUE;
if (getenv("MOZ_DUMP_LEAKS")) { PRBool defined;
gDumpLeaksOnly = PR_TRUE; defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
if (!defined)
gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
if (defined || gLogLeaksOnly) {
RecreateBloatView();
if (NS_WARN_IF_FALSE(gBloatView, "out of memory")) {
gBloatLog = nsnull;
gLogLeaksOnly = PR_FALSE;
} }
}
// See if user is redirecting the trace. (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
const char* traceLogName = getenv("MOZ_TRACE_LOG");
if (traceLogName) {
FILE *stream = ::fopen(traceLogName, "w");
if (stream != NULL)
gLoggingStream = stream;
}
// See if bloaty is enabled (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
if (XPCOM_REFCNT_TRACK_BLOAT & gTraceRefcntLog->level) {
gTrackBloat = PR_TRUE;
RecreateBloatView();
if (NS_WARN_IF_FALSE(gBloatView, "out of memory")) {
gTrackBloat = PR_FALSE;
}
else {
fprintf(gLoggingStream, "XPCOM: using turbo mega bloatvision\n");
}
}
// See if raw nspr logging is enabled defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
if (XPCOM_REFCNT_LOG_ALL & gTraceRefcntLog->level) { if (defined) {
gLogAllRefcnts = PR_TRUE; gLogToLeaky = PR_TRUE;
fprintf(gLoggingStream, "XPCOM: logging all refcnt calls\n"); void* p = nsnull;
} void* q = nsnull;
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 (XP_PC) || defined(XP_MAC)
char* types = getenv("MOZ_TRACE_REFCNT_TYPES");
if (types) {
fprintf(gLoggingStream, "XPCOM: logging some refcnt calls: ");
char* cp = types;
for (;;) {
char* cm = strchr(cp, ',');
if (cm) {
*cm = '\0';
}
PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
fprintf(gLoggingStream, "%s ", cp);
if (!cm) break;
*cm = ',';
cp = cm + 1;
}
fprintf(gLoggingStream, "\n");
}
else {
fprintf(gLoggingStream, "XPCOM: MOZ_TRACE_REFCNTS_TYPE 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 #ifdef HAVE_LIBDL
void* p = dlsym(0, "__log_addref"); p = dlsym(0, "__log_addref");
if (p) { q = dlsym(0, "__log_release");
leakyLogAddRef = (void (*)(void*,int,int)) p;
p = dlsym(0, "__log_release");
if (p) {
leakyLogRelease = (void (*)(void*,int,int)) p;
fprintf(gLoggingStream, "XPCOM: logging addref/release calls to leaky\n");
}
else {
gLogToLeaky = PR_FALSE;
}
}
else {
gLogToLeaky = PR_FALSE;
}
#endif #endif
if (p && q) {
leakyLogAddRef = (void (*)(void*,int,int)) p;
leakyLogRelease = (void (*)(void*,int,int)) q;
} }
else {
gLogToLeaky = PR_FALSE;
fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
}
}
if (gTrackBloat || gLogAllRefcnts || gLogSomeRefcnts || const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
gLogCalls || gLogNewAndDelete) { if (classes) {
gLogging = PR_TRUE; // if XPCOM_LOG_REFCNTS 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,
NULL, NULL);
if (NS_WARN_IF_FALSE(gTypesToLog, "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 = strchr(cp, ',');
if (cm) {
*cm = '\0';
}
PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
fprintf(stdout, "%s ", cp);
if (!cm) break;
*cm = ',';
cp = cm + 1;
}
fprintf(stdout, "\n");
}
}
if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog) {
gLogging = PR_TRUE;
}
#if defined(NS_MT_SUPPORTED) #if defined(NS_MT_SUPPORTED)
gTraceLock = PR_NewLock(); gTraceLock = PR_NewLock();
#endif /* NS_MT_SUPPORTED */ #endif /* NS_MT_SUPPORTED */
}
} }
#endif #endif
@ -902,9 +879,10 @@ nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */ #if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) {
if (gAllocLog || gRefcntsLog) {
HANDLE myProcess = ::GetCurrentProcess(); HANDLE myProcess = ::GetCurrentProcess();
if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) { if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) {
@ -919,7 +897,7 @@ nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName,
0); 0);
// DWORD lastError = 0; // DWORD lastError = 0;
// if (!b) lastError = ::GetLastError(); // if (!b) lastError = ::GetLastError();
// fprintf(gLoggingStream, "loading symbols for library %s => %s [%d]\n", aLibraryName, // fprintf(gLogStream, "loading symbols for library %s => %s [%d]\n", aLibraryName,
// b ? "true" : "false", lastError); // b ? "true" : "false", lastError);
} }
#endif #endif
@ -941,12 +919,12 @@ nsTraceRefcnt::LogAddRef(void* aPtr,
PRUint32 classSize) PRUint32 classSize)
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogging) { if (gLogging) {
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gTrackBloat) { if (gBloatLog) {
BloatEntry* entry = GetBloatEntry(aClazz, classSize); BloatEntry* entry = GetBloatEntry(aClazz, classSize);
if (entry) { if (entry) {
entry->AddRef(aRefCnt); entry->AddRef(aRefCnt);
@ -959,23 +937,23 @@ nsTraceRefcnt::LogAddRef(void* aPtr,
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogNewXPCOM instead to get file and line numbers.) // using LogNewXPCOM instead to get file and line numbers.)
PRBool loggingThisType = (gTypesToLog && LogThisType(aClazz)); PRBool loggingThisType = (gTypesToLog && LogThisType(aClazz));
if (aRefCnt == 1 && (gLogNewAndDelete || loggingThisType)) { if (aRefCnt == 1 && (gAllocLog || loggingThisType)) {
fprintf(gLoggingStream, "\n<%s> 0x%08X Create\n", fprintf(gAllocLog, "\n<%s> 0x%08X Create\n",
aClazz, PRInt32(aPtr)); aClazz, PRInt32(aPtr));
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
} }
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogAddRefCall instead to get file and line numbers.) // using LogAddRefCall instead to get file and line numbers.)
if (gLogAllRefcnts || loggingThisType) { if (gRefcntsLog || loggingThisType) {
if (gLogToLeaky) { if (gLogToLeaky) {
(*leakyLogAddRef)(aPtr, aRefCnt - 1, aRefCnt); (*leakyLogAddRef)(aPtr, aRefCnt - 1, aRefCnt);
} }
else { else {
// Can't use PR_LOG(), b/c it truncates the line // Can't use PR_LOG(), b/c it truncates the line
fprintf(gLoggingStream, fprintf(gRefcntsLog,
"\n<%s> 0x%08X AddRef %d\n", aClazz, PRInt32(aPtr), aRefCnt); "\n<%s> 0x%08X AddRef %d\n", aClazz, PRInt32(aPtr), aRefCnt);
WalkTheStack(gLoggingStream); WalkTheStack(gRefcntsLog);
} }
} }
#endif #endif
@ -992,12 +970,12 @@ nsTraceRefcnt::LogRelease(void* aPtr,
const char* aClazz) const char* aClazz)
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogging) { if (gLogging) {
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gTrackBloat) { if (gBloatLog) {
BloatEntry* entry = GetBloatEntry(aClazz, (PRUint32)-1); BloatEntry* entry = GetBloatEntry(aClazz, (PRUint32)-1);
if (entry) { if (entry) {
entry->Release(aRefCnt); entry->Release(aRefCnt);
@ -1008,15 +986,15 @@ nsTraceRefcnt::LogRelease(void* aPtr,
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogReleaseCall instead to get file and line numbers.) // using LogReleaseCall instead to get file and line numbers.)
PRBool loggingThisType = (gTypesToLog && LogThisType(aClazz)); PRBool loggingThisType = (gTypesToLog && LogThisType(aClazz));
if (gLogAllRefcnts || loggingThisType) { if (gRefcntsLog || loggingThisType) {
if (gLogToLeaky) { if (gLogToLeaky) {
(*leakyLogRelease)(aPtr, aRefCnt + 1, aRefCnt); (*leakyLogRelease)(aPtr, aRefCnt + 1, aRefCnt);
} }
else { else {
// Can't use PR_LOG(), b/c it truncates the line // Can't use PR_LOG(), b/c it truncates the line
fprintf(gLoggingStream, fprintf(gRefcntsLog,
"\n<%s> 0x%08X Release %d\n", aClazz, PRInt32(aPtr), aRefCnt); "\n<%s> 0x%08X Release %d\n", aClazz, PRInt32(aPtr), aRefCnt);
WalkTheStack(gLoggingStream); WalkTheStack(gRefcntsLog);
} }
} }
@ -1025,11 +1003,11 @@ nsTraceRefcnt::LogRelease(void* aPtr,
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogDeleteXPCOM instead to get file and line numbers.) // using LogDeleteXPCOM instead to get file and line numbers.)
if (aRefCnt == 0 && (gLogNewAndDelete || loggingThisType)) { if (aRefCnt == 0 && (gAllocLog || loggingThisType)) {
fprintf(gLoggingStream, fprintf(gAllocLog,
"\n<%s> 0x%08X Destroy\n", "\n<%s> 0x%08X Destroy\n",
aClazz, PRInt32(aPtr)); aClazz, PRInt32(aPtr));
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
} }
#endif #endif
@ -1046,14 +1024,14 @@ nsTraceRefcnt::LogAddRefCall(void* aPtr,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#ifdef NS_LOSING_ARCHITECTURE #ifdef NS_LOSING_ARCHITECTURE
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogCalls) { if (gRefcntsLog) {
LOCK_TRACELOG(); LOCK_TRACELOG();
fprintf(gLoggingStream, "\n<Call> 0x%08X AddRef %d=>%d in %s (line %d)\n", fprintf(gRefcntsLog, "\n<Call> 0x%08X AddRef %d=>%d in %s (line %d)\n",
aPtr, aNewRefcnt-1, aNewRefcnt, aFile, aLine); aPtr, aNewRefcnt-1, aNewRefcnt, aFile, aLine);
WalkTheStack(gLoggingStream); WalkTheStack(gRefcntsLog);
UNLOCK_TRACELOG(); UNLOCK_TRACELOG();
} }
@ -1070,15 +1048,15 @@ nsTraceRefcnt::LogReleaseCall(void* aPtr,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#ifdef NS_LOSING_ARCHITECTURE #ifdef NS_LOSING_ARCHITECTURE
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogCalls) { if (gRefcntsLog) {
LOCK_TRACELOG(); LOCK_TRACELOG();
fprintf(gLoggingStream, "\n<Call> 0x%08X Release %d=>%d in %s (line %d)\n", fprintf(gRefcntsLog, "\n<Call> 0x%08X Release %d=>%d in %s (line %d)\n",
aPtr, aNewRefcnt+1, aNewRefcnt, aFile, aLine); aPtr, aNewRefcnt+1, aNewRefcnt, aFile, aLine);
WalkTheStack(gLoggingStream); WalkTheStack(gRefcntsLog);
UNLOCK_TRACELOG(); UNLOCK_TRACELOG();
} }
@ -1096,15 +1074,15 @@ nsTraceRefcnt::LogNewXPCOM(void* aPtr,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#ifdef NS_LOSING_ARCHITECTURE #ifdef NS_LOSING_ARCHITECTURE
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogNewAndDelete) { if (gAllocLog) {
LOCK_TRACELOG(); LOCK_TRACELOG();
fprintf(gLoggingStream, "\n<%s> 0x%08X NewXPCOM in %s (line %d)\n", fprintf(gAllocLog, "\n<%s> 0x%08X NewXPCOM in %s (line %d)\n",
aType, aPtr, aFile, aLine); aType, aPtr, aFile, aLine);
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
UNLOCK_TRACELOG(); UNLOCK_TRACELOG();
} }
@ -1119,15 +1097,15 @@ nsTraceRefcnt::LogDeleteXPCOM(void* aPtr,
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
#ifdef NS_LOSING_ARCHITECTURE #ifdef NS_LOSING_ARCHITECTURE
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogNewAndDelete) { if (gAllocLog) {
LOCK_TRACELOG(); LOCK_TRACELOG();
fprintf(gLoggingStream, "\n<%s> 0x%08X Destroy in %s (line %d)\n", fprintf(gAllocLog, "\n<%s> 0x%08X Destroy in %s (line %d)\n",
aType, aPtr, aFile, aLine); "?", aPtr, aFile, aLine);
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
UNLOCK_TRACELOG(); UNLOCK_TRACELOG();
} }
@ -1141,13 +1119,13 @@ nsTraceRefcnt::LogCtor(void* aPtr,
PRUint32 aInstanceSize) PRUint32 aInstanceSize)
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogging) { if (gLogging) {
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gTrackBloat) { if (gBloatLog) {
BloatEntry* entry = GetBloatEntry(aType, aInstanceSize); BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
if (entry) { if (entry) {
entry->Ctor(); entry->Ctor();
@ -1157,10 +1135,10 @@ nsTraceRefcnt::LogCtor(void* aPtr,
#ifndef NS_LOSING_ARCHITECTURE #ifndef NS_LOSING_ARCHITECTURE
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogNewXPCOM instead to get file and line numbers.) // using LogNewXPCOM instead to get file and line numbers.)
if (gLogNewAndDelete || (gTypesToLog && LogThisType(aType))) { if (gAllocLog || (gTypesToLog && LogThisType(aType))) {
fprintf(gLoggingStream, "\n<%s> 0x%08X Ctor (%d)\n", fprintf(gAllocLog, "\n<%s> 0x%08X Ctor (%d)\n",
aType, PRInt32(aPtr), aInstanceSize); aType, PRInt32(aPtr), aInstanceSize);
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
} }
#endif #endif
@ -1174,13 +1152,13 @@ nsTraceRefcnt::LogDtor(void* aPtr, const char* aType,
PRUint32 aInstanceSize) PRUint32 aInstanceSize)
{ {
#ifdef NS_BUILD_REFCNT_LOGGING #ifdef NS_BUILD_REFCNT_LOGGING
if (gTraceRefcntLog == nsnull) if (!gInitialized)
InitTraceLog(); InitTraceLog();
if (gLogging) { if (gLogging) {
LOCK_TRACELOG(); LOCK_TRACELOG();
if (gTrackBloat) { if (gBloatLog) {
BloatEntry* entry = GetBloatEntry(aType, aInstanceSize); BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
if (entry) { if (entry) {
entry->Dtor(); entry->Dtor();
@ -1190,10 +1168,10 @@ nsTraceRefcnt::LogDtor(void* aPtr, const char* aType,
#ifndef NS_LOSING_ARCHITECTURE #ifndef NS_LOSING_ARCHITECTURE
// (If we're on a losing architecture, don't do this because we'll be // (If we're on a losing architecture, don't do this because we'll be
// using LogDeleteXPCOM instead to get file and line numbers.) // using LogDeleteXPCOM instead to get file and line numbers.)
if (gLogNewAndDelete || (gTypesToLog && LogThisType(aType))) { if (gAllocLog || (gTypesToLog && LogThisType(aType))) {
fprintf(gLoggingStream, "\n<%s> 0x%08X Dtor (%d)\n", fprintf(gAllocLog, "\n<%s> 0x%08X Dtor (%d)\n",
aType, PRInt32(aPtr), aInstanceSize); aType, PRInt32(aPtr), aInstanceSize);
WalkTheStack(gLoggingStream); WalkTheStack(gAllocLog);
} }
#endif #endif

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

@ -107,8 +107,6 @@ PR_END_MACRO
//---------------------------------------------------------------------- //----------------------------------------------------------------------
class nsIOutputStream;
struct nsTraceRefcntStats { struct nsTraceRefcntStats {
nsrefcnt mAddRefs; nsrefcnt mAddRefs;
nsrefcnt mReleases; nsrefcnt mReleases;
@ -182,7 +180,7 @@ public:
}; };
static NS_COM nsresult DumpStatistics(StatisticsType type = ALL_STATS, static NS_COM nsresult DumpStatistics(StatisticsType type = ALL_STATS,
nsIOutputStream* out = 0); FILE* out = 0);
static NS_COM void ResetStatistics(void); static NS_COM void ResetStatistics(void);
@ -196,7 +194,6 @@ public:
char * aBuffer, char * aBuffer,
int aBufLen); int aBufLen);
// XXX change this to take an nsIOutputStream
static NS_COM void WalkTheStack(FILE* aStream); static NS_COM void WalkTheStack(FILE* aStream);
}; };