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