Added 'Bloaty' refcounting and memory bloat statistics code

This commit is contained in:
warren%netscape.com 1999-10-07 21:50:20 +00:00
parent 3a7ce2580e
commit 37676fadb0
8 changed files with 397 additions and 22 deletions

View File

@ -84,7 +84,7 @@ _class::AddRef(void) \
NS_IMETHODIMP_(nsrefcnt) \
_class::Release(void) \
{ \
return fOuter->Release(); \
return fOuter->Release(); \
} \
\
NS_IMETHODIMP \
@ -98,15 +98,21 @@ NS_IMETHODIMP_(nsrefcnt) \
_class::Internal::AddRef(void) \
{ \
_class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
return ++agg->mRefCnt; \
NS_PRECONDITION(PRInt32(agg->mRefCnt) >= 0, "illegal refcnt"); \
++agg->mRefCnt; \
NS_LOG_ADDREF(this, agg->mRefCnt, #_class); \
return agg->mRefCnt; \
} \
\
NS_IMETHODIMP_(nsrefcnt) \
_class::Internal::Release(void) \
{ \
_class* agg = (_class*)((char*)(this) - offsetof(_class, fAggregated)); \
if (--agg->mRefCnt == 0) { \
delete agg; \
NS_PRECONDITION(0 != agg->mRefCnt, "dup release"); \
--agg->mRefCnt; \
NS_LOG_RELEASE(this, agg->mRefCnt, #_class); \
if (agg->mRefCnt == 0) { \
NS_DELETEXPCOM(agg); \
return 0; \
} \
return agg->mRefCnt; \

View File

@ -76,6 +76,20 @@ typedef unsigned long nsrefcnt;
typedef PRUint32 nsrefcnt;
#endif
/**
* "Bloaty" is a facility to dump statistics on object allocations and
* refcounting. To turn it on, you must define BLOATY in this file, and
* rebuild the world. (That seems easier than hacking makefiles to ensure
* that environment variables get checked everywhere.)
*/
#ifdef DEBUG_warrenx
#define BLOATY 1
#endif
#ifdef BLOATY
#define MOZ_LOG_REFCNT 1
#endif
#include "nsTraceRefcnt.h"
/**
@ -768,10 +782,10 @@ NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
#ifdef MOZ_LOG_REFCNT
#define NS_LOG_ADDREF(_ptr, _refcnt, _class) \
nsTraceRefcnt::LogAddRef((_ptr), (_refcnt), (_class))
nsTraceRefcnt::LogAddRef((_ptr), (_refcnt), (_class), (sizeof(*this)))
#define NS_LOG_RELEASE(_ptr, _refcnt, _class) \
nsTraceRefcnt::LogRelease((_ptr), (_refcnt), (_class))
nsTraceRefcnt::LogRelease((_ptr), (_refcnt), (_class), (sizeof(*this)))
#else
#define NS_LOG_ADDREF(_ptr, _refcnt, _class)

View File

@ -16,6 +16,7 @@
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
#include "nsISupports.h"
#include "nsVoidArray.h"
#include "prprf.h"
@ -54,6 +55,119 @@ static PRLock* gTraceLock;
static PRLogModuleInfo* gTraceRefcntLog;
#ifdef BLOATY
#include "plhash.h"
#include <math.h>
PLHashTable* gBloatView;
class BloatEntry {
public:
BloatEntry(const char* className, PRUint32 classSize)
: mClassName(className), mClassSize(classSize) {
Clear();
}
~BloatEntry() {}
void Clear() {
mAddRefs = 0;
mReleases = 0;
mRefsOutstandingTotal = 0;
mRefsOutstandingVariance = 0;
mCreates = 0;
mDestroys = 0;
mObjsOutstandingTotal = 0;
mObjsOutstandingVariance = 0;
}
void AddRef(nsrefcnt refcnt) {
mAddRefs++;
if (refcnt == 1) {
mCreates++;
AccountObjs();
}
AccountRefs();
}
void Release(nsrefcnt refcnt) {
mReleases++;
if (refcnt == 0) {
mDestroys++;
AccountObjs();
}
AccountRefs();
}
void AccountRefs() {
PRInt32 cnt = (mAddRefs - mReleases);
// NS_ASSERTION(cnt >= 0, "too many releases");
mRefsOutstandingTotal += cnt;
mRefsOutstandingVariance += cnt * cnt;
}
void AccountObjs() {
PRInt32 cnt = (mAddRefs - mReleases);
// 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);
return HT_ENUMERATE_NEXT;
}
void Dump(PRIntn i, FILE* fp) {
#if 1
double meanRefCnts = mRefsOutstandingTotal / (mAddRefs + mReleases);
double varRefCnts = fabs(mRefsOutstandingVariance /
mRefsOutstandingTotal - meanRefCnts * meanRefCnts);
double meanObjs = mObjsOutstandingTotal / (mCreates + 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,
meanRefCnts,
sqrt(varRefCnts),
(mCreates - mDestroys),
mCreates,
meanObjs,
sqrt(varObjs),
mClassSize,
(mCreates - mDestroys) * mClassSize);
#else
fprintf(fp, "%4d %-20.20s %8d %8d %8d %8d %8d %8d\n",
i, mClassName,
mCreates,
(mCreates - mDestroys),
mAddRefs,
(mAddRefs - mReleases),
mClassSize,
(mCreates - 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;
};
extern "C" void
NS_DumpBloatStatistics(void)
{
// 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);
}
#endif
static void InitTraceLog(void)
{
if (0 == gTraceRefcntLog) {
@ -62,6 +176,15 @@ static void InitTraceLog(void)
#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
}
}
@ -449,8 +572,8 @@ nsTraceRefcnt::Create(void* aPtr,
NS_COM void
nsTraceRefcnt::Destroy(void* aPtr,
const char* aFile,
PRIntn aLine)
const char* aFile,
PRIntn aLine)
{
#ifdef MOZ_TRACE_XPCOM_REFCNT
InitTraceLog();
@ -471,30 +594,68 @@ nsTraceRefcnt::Destroy(void* aPtr,
NS_COM void
nsTraceRefcnt::LogAddRef(void* aPtr,
nsrefcnt aRefCnt,
const char* aClazz)
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)
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)

View File

@ -73,11 +73,13 @@ public:
static NS_COM void LogAddRef(void* aPtr,
nsrefcnt aRefCnt,
const char* aClass);
const char* aClass,
PRUint32 classSize);
static NS_COM void LogRelease(void* aPtr,
nsrefcnt aRefCnt,
const char* aClass);
const char* aClass,
PRUint32 classSize);
#ifdef DEBUG
/**
@ -137,4 +139,9 @@ PR_END_MACRO
#endif /* DEBUG */
#ifdef BLOATY
extern "C" void
NS_DumpBloatStatistics(void);
#endif
#endif /* nsTraceRefcnt_h___ */

View File

@ -16,6 +16,7 @@
* Corporation. Portions created by Netscape are Copyright (C) 1998
* Netscape Communications Corporation. All Rights Reserved.
*/
#include "nsISupports.h"
#include "nsVoidArray.h"
#include "prprf.h"
@ -54,6 +55,119 @@ static PRLock* gTraceLock;
static PRLogModuleInfo* gTraceRefcntLog;
#ifdef BLOATY
#include "plhash.h"
#include <math.h>
PLHashTable* gBloatView;
class BloatEntry {
public:
BloatEntry(const char* className, PRUint32 classSize)
: mClassName(className), mClassSize(classSize) {
Clear();
}
~BloatEntry() {}
void Clear() {
mAddRefs = 0;
mReleases = 0;
mRefsOutstandingTotal = 0;
mRefsOutstandingVariance = 0;
mCreates = 0;
mDestroys = 0;
mObjsOutstandingTotal = 0;
mObjsOutstandingVariance = 0;
}
void AddRef(nsrefcnt refcnt) {
mAddRefs++;
if (refcnt == 1) {
mCreates++;
AccountObjs();
}
AccountRefs();
}
void Release(nsrefcnt refcnt) {
mReleases++;
if (refcnt == 0) {
mDestroys++;
AccountObjs();
}
AccountRefs();
}
void AccountRefs() {
PRInt32 cnt = (mAddRefs - mReleases);
// NS_ASSERTION(cnt >= 0, "too many releases");
mRefsOutstandingTotal += cnt;
mRefsOutstandingVariance += cnt * cnt;
}
void AccountObjs() {
PRInt32 cnt = (mAddRefs - mReleases);
// 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);
return HT_ENUMERATE_NEXT;
}
void Dump(PRIntn i, FILE* fp) {
#if 1
double meanRefCnts = mRefsOutstandingTotal / (mAddRefs + mReleases);
double varRefCnts = fabs(mRefsOutstandingVariance /
mRefsOutstandingTotal - meanRefCnts * meanRefCnts);
double meanObjs = mObjsOutstandingTotal / (mCreates + 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,
meanRefCnts,
sqrt(varRefCnts),
(mCreates - mDestroys),
mCreates,
meanObjs,
sqrt(varObjs),
mClassSize,
(mCreates - mDestroys) * mClassSize);
#else
fprintf(fp, "%4d %-20.20s %8d %8d %8d %8d %8d %8d\n",
i, mClassName,
mCreates,
(mCreates - mDestroys),
mAddRefs,
(mAddRefs - mReleases),
mClassSize,
(mCreates - 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;
};
extern "C" void
NS_DumpBloatStatistics(void)
{
// 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);
}
#endif
static void InitTraceLog(void)
{
if (0 == gTraceRefcntLog) {
@ -62,6 +176,15 @@ static void InitTraceLog(void)
#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
}
}
@ -449,8 +572,8 @@ nsTraceRefcnt::Create(void* aPtr,
NS_COM void
nsTraceRefcnt::Destroy(void* aPtr,
const char* aFile,
PRIntn aLine)
const char* aFile,
PRIntn aLine)
{
#ifdef MOZ_TRACE_XPCOM_REFCNT
InitTraceLog();
@ -471,30 +594,68 @@ nsTraceRefcnt::Destroy(void* aPtr,
NS_COM void
nsTraceRefcnt::LogAddRef(void* aPtr,
nsrefcnt aRefCnt,
const char* aClazz)
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)
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)

View File

@ -73,11 +73,13 @@ public:
static NS_COM void LogAddRef(void* aPtr,
nsrefcnt aRefCnt,
const char* aClass);
const char* aClass,
PRUint32 classSize);
static NS_COM void LogRelease(void* aPtr,
nsrefcnt aRefCnt,
const char* aClass);
const char* aClass,
PRUint32 classSize);
#ifdef DEBUG
/**
@ -137,4 +139,9 @@ PR_END_MACRO
#endif /* DEBUG */
#ifdef BLOATY
extern "C" void
NS_DumpBloatStatistics(void);
#endif
#endif /* nsTraceRefcnt_h___ */

View File

@ -483,6 +483,7 @@ nsresult NS_COM NS_ShutdownXPCOM(nsIServiceManager* servMgr)
// libraries:
NS_RELEASE2(nsComponentManagerImpl::gComponentManager, cnt);
NS_ASSERTION(cnt == 0, "Component Manager being held past XPCOM shutdown.");
#ifdef DEBUG
extern void _FreeAutoLockStatics();
_FreeAutoLockStatics();
@ -490,6 +491,10 @@ nsresult NS_COM NS_ShutdownXPCOM(nsIServiceManager* servMgr)
NS_PurgeAtomTable();
#ifdef BLOATY
NS_DumpBloatStatistics();
#endif
#if defined(DEBUG) && (defined(_WIN32) || defined(XP_UNIX))
if (getenv("MOZ_DUMP_LEAKS")) {
nsTraceRefcnt::DumpLeaks(stderr);

View File

@ -76,6 +76,20 @@ typedef unsigned long nsrefcnt;
typedef PRUint32 nsrefcnt;
#endif
/**
* "Bloaty" is a facility to dump statistics on object allocations and
* refcounting. To turn it on, you must define BLOATY in this file, and
* rebuild the world. (That seems easier than hacking makefiles to ensure
* that environment variables get checked everywhere.)
*/
#ifdef DEBUG_warrenx
#define BLOATY 1
#endif
#ifdef BLOATY
#define MOZ_LOG_REFCNT 1
#endif
#include "nsTraceRefcnt.h"
/**
@ -768,10 +782,10 @@ NS_IMETHODIMP _class::QueryInterface(REFNSIID aIID, void** aInstancePtr) \
#ifdef MOZ_LOG_REFCNT
#define NS_LOG_ADDREF(_ptr, _refcnt, _class) \
nsTraceRefcnt::LogAddRef((_ptr), (_refcnt), (_class))
nsTraceRefcnt::LogAddRef((_ptr), (_refcnt), (_class), (sizeof(*this)))
#define NS_LOG_RELEASE(_ptr, _refcnt, _class) \
nsTraceRefcnt::LogRelease((_ptr), (_refcnt), (_class))
nsTraceRefcnt::LogRelease((_ptr), (_refcnt), (_class), (sizeof(*this)))
#else
#define NS_LOG_ADDREF(_ptr, _refcnt, _class)