1998-09-12 19:25:06 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Netscape Public License
|
|
|
|
* Version 1.0 (the "License"); you may not use this file except in
|
|
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/NPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS"
|
|
|
|
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
|
|
|
* the License for the specific language governing rights and limitations
|
|
|
|
* under the License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla Communicator client code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
|
|
* Corporation. Portions created by Netscape are Copyright (C) 1998
|
|
|
|
* Netscape Communications Corporation. All Rights Reserved.
|
|
|
|
*/
|
1999-10-07 21:50:20 +00:00
|
|
|
|
1998-09-12 19:25:06 +00:00
|
|
|
#include "nsISupports.h"
|
1999-10-04 23:36:18 +00:00
|
|
|
#include "nsVoidArray.h"
|
1998-09-12 19:25:06 +00:00
|
|
|
#include "prprf.h"
|
|
|
|
#include "prlog.h"
|
1999-10-04 23:36:18 +00:00
|
|
|
#include "plstr.h"
|
1998-09-12 19:25:06 +00:00
|
|
|
|
|
|
|
#if defined(_WIN32)
|
|
|
|
#include <windows.h>
|
1999-06-21 02:29:17 +00:00
|
|
|
#elif defined(linux) && defined(__GLIBC__) && defined(__i386)
|
1999-06-16 02:05:32 +00:00
|
|
|
#include <setjmp.h>
|
1999-06-16 04:55:36 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
|
|
|
|
// if __USE_GNU is defined. I suppose its some kind of standards
|
|
|
|
// adherence thing.
|
|
|
|
//
|
1999-06-21 02:29:17 +00:00
|
|
|
#if (__GLIBC_MINOR__ >= 1)
|
1999-06-16 04:55:36 +00:00
|
|
|
#define __USE_GNU
|
|
|
|
#endif
|
|
|
|
|
1999-06-16 02:05:32 +00:00
|
|
|
#include <dlfcn.h>
|
1999-06-16 04:55:36 +00:00
|
|
|
|
1998-09-12 19:25:06 +00:00
|
|
|
#endif
|
|
|
|
|
1998-09-19 06:56:22 +00:00
|
|
|
#if defined(NS_MT_SUPPORTED)
|
|
|
|
#include "prlock.h"
|
|
|
|
|
|
|
|
static PRLock* gTraceLock;
|
|
|
|
|
|
|
|
#define LOCK_TRACELOG() PR_Lock(gTraceLock)
|
|
|
|
#define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
|
|
|
|
#else /* ! NT_MT_SUPPORTED */
|
|
|
|
#define LOCK_TRACELOG()
|
|
|
|
#define UNLOCK_TRACELOG()
|
|
|
|
#endif /* ! NS_MT_SUPPORTED */
|
|
|
|
|
1999-04-10 08:50:50 +00:00
|
|
|
static PRLogModuleInfo* gTraceRefcntLog;
|
|
|
|
|
1999-10-07 21:50:20 +00:00
|
|
|
#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() {
|
1999-10-08 04:39:24 +00:00
|
|
|
PRInt32 cnt = (mCreates - mDestroys);
|
1999-10-07 21:50:20 +00:00
|
|
|
// 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
|
|
|
|
|
1998-09-12 19:25:06 +00:00
|
|
|
static void InitTraceLog(void)
|
|
|
|
{
|
|
|
|
if (0 == gTraceRefcntLog) {
|
|
|
|
gTraceRefcntLog = PR_NewLogModule("xpcomrefcnt");
|
1998-09-19 06:56:22 +00:00
|
|
|
|
|
|
|
#if defined(NS_MT_SUPPORTED)
|
|
|
|
gTraceLock = PR_NewLock();
|
|
|
|
#endif /* NS_MT_SUPPORTED */
|
1999-10-07 21:50:20 +00:00
|
|
|
|
|
|
|
#ifdef BLOATY
|
|
|
|
gBloatView = PL_NewHashTable(256,
|
|
|
|
PL_HashString,
|
|
|
|
PL_CompareStrings,
|
|
|
|
PL_CompareValues,
|
|
|
|
NULL, NULL);
|
|
|
|
NS_ASSERTION(gBloatView, "out of memory");
|
|
|
|
#endif
|
1998-09-12 19:25:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-06-16 02:05:32 +00:00
|
|
|
|
|
|
|
int nsIToA16(PRUint32 aNumber, char* aBuffer)
|
|
|
|
{
|
|
|
|
static char kHex[] = "0123456789abcdef";
|
|
|
|
|
|
|
|
if (aNumber == 0) {
|
|
|
|
*aBuffer = '0';
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char buf[8];
|
|
|
|
PRInt32 count = 0;
|
|
|
|
while (aNumber != 0) {
|
|
|
|
PRUint32 nibble = aNumber & 0xf;
|
|
|
|
buf[count++] = kHex[nibble];
|
|
|
|
aNumber >>= 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (PRInt32 i = count - 1; i >= 0; --i)
|
|
|
|
*aBuffer++ = buf[i];
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
1999-07-24 22:00:00 +00:00
|
|
|
#if defined(_WIN32) && defined(_M_IX86) // WIN32 x86 stack walking code
|
1998-09-12 19:25:06 +00:00
|
|
|
#include "imagehlp.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
|
1999-06-15 03:25:27 +00:00
|
|
|
// Define these as static pointers so that we can load the DLL on the
|
|
|
|
// fly (and not introduce a link-time dependency on it). Tip o' the
|
|
|
|
// hat to Matt Pietrick for this idea. See:
|
|
|
|
//
|
|
|
|
// http://msdn.microsoft.com/library/periodic/period97/F1/D3/S245C6.htm
|
|
|
|
//
|
|
|
|
typedef BOOL (__stdcall *SYMINITIALIZEPROC)(HANDLE, LPSTR, BOOL);
|
|
|
|
static SYMINITIALIZEPROC _SymInitialize;
|
|
|
|
|
|
|
|
typedef BOOL (__stdcall *SYMCLEANUPPROC)(HANDLE);
|
|
|
|
static SYMCLEANUPPROC _SymCleanup;
|
|
|
|
|
|
|
|
typedef BOOL (__stdcall *STACKWALKPROC)(DWORD,
|
|
|
|
HANDLE,
|
|
|
|
HANDLE,
|
|
|
|
LPSTACKFRAME,
|
|
|
|
LPVOID,
|
|
|
|
PREAD_PROCESS_MEMORY_ROUTINE,
|
|
|
|
PFUNCTION_TABLE_ACCESS_ROUTINE,
|
|
|
|
PGET_MODULE_BASE_ROUTINE,
|
|
|
|
PTRANSLATE_ADDRESS_ROUTINE);
|
|
|
|
static STACKWALKPROC _StackWalk;
|
|
|
|
|
|
|
|
typedef LPVOID (__stdcall *SYMFUNCTIONTABLEACCESSPROC)(HANDLE, DWORD);
|
|
|
|
static SYMFUNCTIONTABLEACCESSPROC _SymFunctionTableAccess;
|
|
|
|
|
|
|
|
typedef DWORD (__stdcall *SYMGETMODULEBASEPROC)(HANDLE, DWORD);
|
|
|
|
static SYMGETMODULEBASEPROC _SymGetModuleBase;
|
|
|
|
|
|
|
|
typedef BOOL (__stdcall *SYMGETSYMFROMADDRPROC)(HANDLE, DWORD, PDWORD, PIMAGEHLP_SYMBOL);
|
|
|
|
static SYMGETSYMFROMADDRPROC _SymGetSymFromAddr;
|
|
|
|
|
|
|
|
|
|
|
|
static PRBool
|
|
|
|
EnsureSymInitialized()
|
1998-09-12 19:25:06 +00:00
|
|
|
{
|
1999-06-15 03:25:27 +00:00
|
|
|
PRBool gInitialized = PR_FALSE;
|
1998-09-12 19:25:06 +00:00
|
|
|
|
1999-06-15 03:25:27 +00:00
|
|
|
if (! gInitialized) {
|
|
|
|
HMODULE module = ::LoadLibrary("IMAGEHLP.DLL");
|
|
|
|
if (!module) return PR_FALSE;
|
|
|
|
|
|
|
|
_SymInitialize = (SYMINITIALIZEPROC) ::GetProcAddress(module, "SymInitialize");
|
|
|
|
if (!_SymInitialize) return PR_FALSE;
|
|
|
|
|
|
|
|
_SymCleanup = (SYMCLEANUPPROC)GetProcAddress(module, "SymCleanup");
|
|
|
|
if (!_SymCleanup) return PR_FALSE;
|
|
|
|
|
|
|
|
_StackWalk = (STACKWALKPROC)GetProcAddress(module, "StackWalk");
|
|
|
|
if (!_StackWalk) return PR_FALSE;
|
1998-09-12 19:25:06 +00:00
|
|
|
|
1999-06-15 03:25:27 +00:00
|
|
|
_SymFunctionTableAccess = (SYMFUNCTIONTABLEACCESSPROC) GetProcAddress(module, "SymFunctionTableAccess");
|
|
|
|
if (!_SymFunctionTableAccess) return PR_FALSE;
|
|
|
|
|
|
|
|
_SymGetModuleBase = (SYMGETMODULEBASEPROC)GetProcAddress(module, "SymGetModuleBase");
|
|
|
|
if (!_SymGetModuleBase) return PR_FALSE;
|
|
|
|
|
|
|
|
_SymGetSymFromAddr = (SYMGETSYMFROMADDRPROC)GetProcAddress(module, "SymGetSymFromAddr");
|
|
|
|
if (!_SymGetSymFromAddr) return PR_FALSE;
|
|
|
|
|
|
|
|
gInitialized = _SymInitialize(GetCurrentProcess(), 0, TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return gInitialized;
|
1998-09-12 19:25:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Walk the stack, translating PC's found into strings and recording the
|
|
|
|
* chain in aBuffer. For this to work properly, the dll's must be rebased
|
|
|
|
* so that the address in the file agrees with the address in memory.
|
|
|
|
* Otherwise StackWalk will return FALSE when it hits a frame in a dll's
|
|
|
|
* whose in memory address doesn't match it's in-file address.
|
|
|
|
*
|
|
|
|
* Fortunately, there is a handy dandy routine in IMAGEHLP.DLL that does
|
|
|
|
* the rebasing and accordingly I've made a tool to use it to rebase the
|
|
|
|
* DLL's in one fell swoop (see xpcom/tools/windows/rebasedlls.cpp).
|
|
|
|
*/
|
1998-11-26 18:03:20 +00:00
|
|
|
void
|
|
|
|
nsTraceRefcnt::WalkTheStack(char* aBuffer, int aBufLen)
|
1998-09-12 19:25:06 +00:00
|
|
|
{
|
|
|
|
aBuffer[0] = '\0';
|
|
|
|
aBufLen--; // leave room for nul
|
|
|
|
|
|
|
|
HANDLE myProcess = ::GetCurrentProcess();
|
|
|
|
HANDLE myThread = ::GetCurrentThread();
|
|
|
|
|
1999-06-15 03:25:27 +00:00
|
|
|
BOOL ok;
|
|
|
|
|
|
|
|
ok = EnsureSymInitialized();
|
|
|
|
if (! ok)
|
|
|
|
return;
|
|
|
|
|
1998-09-12 19:25:06 +00:00
|
|
|
// Get the context information for this thread. That way we will
|
|
|
|
// know where our sp, fp, pc, etc. are and can fill in the
|
|
|
|
// STACKFRAME with the initial values.
|
1999-06-15 03:25:27 +00:00
|
|
|
CONTEXT context;
|
1998-09-12 19:25:06 +00:00
|
|
|
context.ContextFlags = CONTEXT_FULL;
|
1999-06-15 03:25:27 +00:00
|
|
|
ok = GetThreadContext(myThread, &context);
|
|
|
|
if (! ok)
|
1998-09-12 19:25:06 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Setup initial stack frame to walk from
|
1999-06-15 03:25:27 +00:00
|
|
|
STACKFRAME frame;
|
1998-09-12 19:25:06 +00:00
|
|
|
memset(&frame, 0, sizeof(frame));
|
1999-06-15 03:25:27 +00:00
|
|
|
frame.AddrPC.Offset = context.Eip;
|
|
|
|
frame.AddrPC.Mode = AddrModeFlat;
|
1998-09-12 19:25:06 +00:00
|
|
|
frame.AddrStack.Offset = context.Esp;
|
1999-06-15 03:25:27 +00:00
|
|
|
frame.AddrStack.Mode = AddrModeFlat;
|
|
|
|
frame.AddrFrame.Offset = context.Ebp;
|
|
|
|
frame.AddrFrame.Mode = AddrModeFlat;
|
1998-09-12 19:25:06 +00:00
|
|
|
|
|
|
|
// Now walk the stack and map the pc's to symbol names that we stuff
|
|
|
|
// append to *cp.
|
1999-06-15 03:25:27 +00:00
|
|
|
char* cp = aBuffer;
|
1998-09-12 19:25:06 +00:00
|
|
|
|
|
|
|
int skip = 2;
|
|
|
|
while (aBufLen > 0) {
|
1999-06-15 03:25:27 +00:00
|
|
|
ok = _StackWalk(IMAGE_FILE_MACHINE_I386,
|
|
|
|
myProcess,
|
|
|
|
myThread,
|
|
|
|
&frame,
|
|
|
|
&context,
|
|
|
|
0, // read process memory routine
|
|
|
|
_SymFunctionTableAccess, // function table access routine
|
|
|
|
_SymGetModuleBase, // module base routine
|
|
|
|
0); // translate address routine
|
|
|
|
|
|
|
|
if (!ok || frame.AddrPC.Offset == 0)
|
1998-09-12 19:25:06 +00:00
|
|
|
break;
|
1999-06-15 03:25:27 +00:00
|
|
|
|
|
|
|
if (skip-- > 0)
|
1998-09-12 19:25:06 +00:00
|
|
|
continue;
|
1999-06-15 03:25:27 +00:00
|
|
|
|
|
|
|
char buf[sizeof(IMAGEHLP_SYMBOL) + 512];
|
|
|
|
PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL) buf;
|
|
|
|
symbol->SizeOfStruct = sizeof(buf);
|
|
|
|
symbol->MaxNameLength = 512;
|
|
|
|
|
|
|
|
DWORD displacement;
|
|
|
|
ok = _SymGetSymFromAddr(myProcess,
|
|
|
|
frame.AddrPC.Offset,
|
|
|
|
&displacement,
|
|
|
|
symbol);
|
|
|
|
|
|
|
|
if (ok) {
|
|
|
|
int nameLen = strlen(symbol->Name);
|
1999-06-15 05:27:09 +00:00
|
|
|
if (nameLen + 12 > aBufLen) { // 12 == strlen("+0x12345678 ")
|
1998-09-12 19:25:06 +00:00
|
|
|
break;
|
|
|
|
}
|
1999-06-15 05:27:09 +00:00
|
|
|
char* cp2 = symbol->Name;
|
|
|
|
while (*cp2) {
|
|
|
|
if (*cp2 == ' ') *cp2 = '_'; // replace spaces with underscores
|
|
|
|
*cp++ = *cp2++;
|
|
|
|
}
|
|
|
|
aBufLen -= nameLen;
|
1999-06-16 02:05:32 +00:00
|
|
|
*cp++ = '+';
|
|
|
|
*cp++ = '0';
|
|
|
|
*cp++ = 'x';
|
|
|
|
PRInt32 len = nsIToA16(displacement, cp);
|
|
|
|
cp += len;
|
|
|
|
*cp++ = ' ';
|
|
|
|
|
|
|
|
aBufLen -= nameLen + len + 4;
|
1998-09-12 19:25:06 +00:00
|
|
|
}
|
|
|
|
else {
|
1999-06-15 05:27:09 +00:00
|
|
|
if (11 > aBufLen) { // 11 == strlen("0x12345678 ")
|
1998-09-12 19:25:06 +00:00
|
|
|
break;
|
|
|
|
}
|
1999-06-16 02:05:32 +00:00
|
|
|
*cp++ = '0';
|
|
|
|
*cp++ = 'x';
|
|
|
|
PRInt32 len = nsIToA16(frame.AddrPC.Offset, cp);
|
|
|
|
cp += len;
|
|
|
|
*cp++ = ' ';
|
|
|
|
aBufLen -= len + 3;
|
1998-09-12 19:25:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*cp = 0;
|
|
|
|
}
|
1999-06-16 02:05:32 +00:00
|
|
|
/* _WIN32 */
|
1999-06-21 02:29:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
#elif defined(linux) && defined(__GLIBC__) && defined(__i386) // i386 Linux stackwalking code
|
1999-06-16 02:05:32 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
nsTraceRefcnt::WalkTheStack(char* aBuffer, int aBufLen)
|
|
|
|
{
|
|
|
|
aBuffer[0] = '\0';
|
|
|
|
aBufLen--; // leave room for nul
|
|
|
|
|
|
|
|
char* cp = aBuffer;
|
|
|
|
|
|
|
|
jmp_buf jb;
|
|
|
|
setjmp(jb);
|
|
|
|
|
|
|
|
// Stack walking code courtesy Kipp's "leaky".
|
|
|
|
u_long* bp = (u_long*) (jb[0].__jmpbuf[JB_BP]);
|
|
|
|
int skip = 2;
|
|
|
|
for (;;) {
|
|
|
|
u_long* nextbp = (u_long*) *bp++;
|
|
|
|
u_long pc = *bp;
|
|
|
|
if ((pc < 0x08000000) || (pc > 0x7fffffff) || (nextbp < bp)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (--skip <= 0) {
|
|
|
|
Dl_info info;
|
|
|
|
int ok = dladdr((void*) pc, &info);
|
|
|
|
if (ok < 0)
|
|
|
|
break;
|
|
|
|
|
1999-09-14 13:24:25 +00:00
|
|
|
const char * symbol = info.dli_sname;
|
|
|
|
|
|
|
|
int len = strlen(symbol);
|
1999-06-16 02:05:32 +00:00
|
|
|
if (! len)
|
|
|
|
break; // XXX Lazy. We could look at the filename or something.
|
|
|
|
|
1999-09-14 13:24:25 +00:00
|
|
|
char demangled[4096] = "\0";
|
|
|
|
|
|
|
|
DemangleSymbol(symbol,demangled,sizeof(demangled));
|
|
|
|
|
|
|
|
if (demangled && strlen(demangled))
|
|
|
|
{
|
|
|
|
symbol = demangled;
|
|
|
|
len = strlen(symbol);
|
|
|
|
}
|
|
|
|
|
1999-06-16 02:05:32 +00:00
|
|
|
if (len + 12 >= aBufLen) // 12 == strlen("+0x12345678 ")
|
|
|
|
break;
|
|
|
|
|
1999-09-14 13:24:25 +00:00
|
|
|
strcpy(cp, symbol);
|
1999-06-16 02:05:32 +00:00
|
|
|
cp += len;
|
|
|
|
|
|
|
|
*cp++ = '+';
|
|
|
|
*cp++ = '0';
|
|
|
|
*cp++ = 'x';
|
|
|
|
|
|
|
|
PRUint32 off = (char*)pc - (char*)info.dli_saddr;
|
|
|
|
PRInt32 addrStrLen = nsIToA16(off, cp);
|
|
|
|
cp += addrStrLen;
|
|
|
|
|
1999-09-20 03:45:21 +00:00
|
|
|
*cp++ = '\t';
|
1999-06-16 02:05:32 +00:00
|
|
|
|
|
|
|
aBufLen -= addrStrLen + 4;
|
|
|
|
}
|
|
|
|
bp = nextbp;
|
|
|
|
}
|
|
|
|
*cp = '\0';
|
|
|
|
}
|
1998-09-12 19:25:06 +00:00
|
|
|
|
1999-06-16 02:05:32 +00:00
|
|
|
#else // unsupported platform.
|
1999-02-26 17:39:55 +00:00
|
|
|
|
1999-06-15 03:25:27 +00:00
|
|
|
NS_COM void
|
1998-11-26 18:03:20 +00:00
|
|
|
nsTraceRefcnt::WalkTheStack(char* aBuffer, int aBufLen)
|
1998-09-12 19:25:06 +00:00
|
|
|
{
|
1999-06-15 03:25:27 +00:00
|
|
|
// Write me!!!
|
|
|
|
*aBuffer = '\0';
|
1998-09-12 19:25:06 +00:00
|
|
|
}
|
1999-06-15 03:25:27 +00:00
|
|
|
|
|
|
|
#endif
|
1998-09-12 19:25:06 +00:00
|
|
|
|
|
|
|
NS_COM void
|
|
|
|
nsTraceRefcnt::LoadLibrarySymbols(const char* aLibraryName,
|
|
|
|
void* aLibrayHandle)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_TRACE_XPCOM_REFCNT
|
1999-07-24 22:00:00 +00:00
|
|
|
#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */
|
1998-09-15 00:13:35 +00:00
|
|
|
InitTraceLog();
|
|
|
|
if (PR_LOG_TEST(gTraceRefcntLog,PR_LOG_DEBUG)) {
|
|
|
|
HANDLE myProcess = ::GetCurrentProcess();
|
1998-09-12 19:25:06 +00:00
|
|
|
|
1998-09-15 00:13:35 +00:00
|
|
|
if (!SymInitialize(myProcess, ".;..\\lib", TRUE)) {
|
|
|
|
return;
|
|
|
|
}
|
1998-09-12 19:25:06 +00:00
|
|
|
|
1998-09-15 00:13:35 +00:00
|
|
|
BOOL b = ::SymLoadModule(myProcess,
|
|
|
|
NULL,
|
|
|
|
(char*)aLibraryName,
|
|
|
|
(char*)aLibraryName,
|
|
|
|
0,
|
|
|
|
0);
|
1998-09-12 19:25:06 +00:00
|
|
|
// DWORD lastError = 0;
|
|
|
|
// if (!b) lastError = ::GetLastError();
|
|
|
|
// printf("loading symbols for library %s => %s [%d]\n", aLibraryName,
|
|
|
|
// b ? "true" : "false", lastError);
|
1998-09-15 00:13:35 +00:00
|
|
|
}
|
1998-09-12 19:25:06 +00:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_COM unsigned long
|
|
|
|
nsTraceRefcnt::AddRef(void* aPtr,
|
|
|
|
unsigned long aNewRefcnt,
|
|
|
|
const char* aFile,
|
|
|
|
PRIntn aLine)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_TRACE_XPCOM_REFCNT
|
|
|
|
InitTraceLog();
|
1998-09-19 06:56:22 +00:00
|
|
|
|
|
|
|
LOCK_TRACELOG();
|
1998-09-12 19:25:06 +00:00
|
|
|
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));
|
|
|
|
}
|
1998-09-19 06:56:22 +00:00
|
|
|
UNLOCK_TRACELOG();
|
1998-09-12 19:25:06 +00:00
|
|
|
#endif
|
|
|
|
return aNewRefcnt;
|
1998-09-19 06:56:22 +00:00
|
|
|
|
1998-09-12 19:25:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_COM unsigned long
|
|
|
|
nsTraceRefcnt::Release(void* aPtr,
|
|
|
|
unsigned long aNewRefcnt,
|
|
|
|
const char* aFile,
|
|
|
|
PRIntn aLine)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_TRACE_XPCOM_REFCNT
|
|
|
|
InitTraceLog();
|
1998-09-19 06:56:22 +00:00
|
|
|
|
|
|
|
LOCK_TRACELOG();
|
1998-09-12 19:25:06 +00:00
|
|
|
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));
|
|
|
|
}
|
1998-09-19 06:56:22 +00:00
|
|
|
UNLOCK_TRACELOG();
|
1998-09-12 19:25:06 +00:00
|
|
|
#endif
|
|
|
|
return aNewRefcnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_COM void
|
|
|
|
nsTraceRefcnt::Create(void* aPtr,
|
|
|
|
const char* aType,
|
|
|
|
const char* aFile,
|
|
|
|
PRIntn aLine)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_TRACE_XPCOM_REFCNT
|
|
|
|
InitTraceLog();
|
1998-09-19 06:56:22 +00:00
|
|
|
|
|
|
|
LOCK_TRACELOG();
|
1998-09-12 19:25:06 +00:00
|
|
|
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));
|
|
|
|
}
|
1998-09-19 06:56:22 +00:00
|
|
|
UNLOCK_TRACELOG();
|
1998-09-12 19:25:06 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_COM void
|
|
|
|
nsTraceRefcnt::Destroy(void* aPtr,
|
1999-10-07 21:50:20 +00:00
|
|
|
const char* aFile,
|
|
|
|
PRIntn aLine)
|
1998-09-12 19:25:06 +00:00
|
|
|
{
|
|
|
|
#ifdef MOZ_TRACE_XPCOM_REFCNT
|
|
|
|
InitTraceLog();
|
1998-09-19 06:56:22 +00:00
|
|
|
|
|
|
|
LOCK_TRACELOG();
|
1998-09-12 19:25:06 +00:00
|
|
|
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));
|
|
|
|
}
|
1998-09-19 06:56:22 +00:00
|
|
|
UNLOCK_TRACELOG();
|
1998-09-12 19:25:06 +00:00
|
|
|
#endif
|
|
|
|
}
|
1999-06-15 03:25:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
NS_COM void
|
|
|
|
nsTraceRefcnt::LogAddRef(void* aPtr,
|
|
|
|
nsrefcnt aRefCnt,
|
1999-10-07 21:50:20 +00:00
|
|
|
const char* aClazz,
|
|
|
|
PRUint32 classSize)
|
1999-06-15 03:25:27 +00:00
|
|
|
{
|
|
|
|
InitTraceLog();
|
1999-10-07 21:50:20 +00:00
|
|
|
#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
|
1999-06-15 03:25:27 +00:00
|
|
|
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
|
1999-10-05 00:07:54 +00:00
|
|
|
printf("%s\t%p\tAddRef\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb);
|
1999-06-15 03:25:27 +00:00
|
|
|
}
|
1999-10-07 21:50:20 +00:00
|
|
|
#endif
|
1999-06-15 03:25:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NS_COM void
|
|
|
|
nsTraceRefcnt::LogRelease(void* aPtr,
|
1999-10-07 21:50:20 +00:00
|
|
|
nsrefcnt aRefCnt,
|
|
|
|
const char* aClazz,
|
|
|
|
PRUint32 classSize)
|
1999-06-15 03:25:27 +00:00
|
|
|
{
|
|
|
|
InitTraceLog();
|
1999-10-07 21:50:20 +00:00
|
|
|
#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
|
1999-06-15 03:25:27 +00:00
|
|
|
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
|
1999-10-05 00:07:54 +00:00
|
|
|
printf("%s\t%p\tRelease\t%d\t%s\n", aClazz, aPtr, aRefCnt, sb);
|
1999-06-15 03:25:27 +00:00
|
|
|
}
|
1999-10-07 21:50:20 +00:00
|
|
|
#endif
|
1999-06-15 03:25:27 +00:00
|
|
|
}
|
1999-09-14 13:24:25 +00:00
|
|
|
|
|
|
|
// This thing is exported by libiberty.a (-liberty)
|
|
|
|
// Yes, this is a gcc only hack
|
1999-09-14 13:30:12 +00:00
|
|
|
#if defined(MOZ_DEMANGLE_SYMBOLS)
|
1999-09-14 13:24:25 +00:00
|
|
|
extern "C" char * cplus_demangle(const char *,int);
|
|
|
|
#include <stdlib.h> // for free()
|
1999-09-14 13:30:12 +00:00
|
|
|
#endif // MOZ_DEMANGLE_SYMBOLS
|
1999-09-14 13:24:25 +00:00
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
NS_COM void
|
|
|
|
nsTraceRefcnt::DemangleSymbol(const char * aSymbol,
|
|
|
|
char * aBuffer,
|
|
|
|
int aBufLen)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(nsnull != aSymbol,"null symbol");
|
|
|
|
NS_ASSERTION(nsnull != aBuffer,"null buffer");
|
|
|
|
NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
|
|
|
|
|
|
|
|
aBuffer[0] = '\0';
|
|
|
|
|
1999-09-14 13:30:12 +00:00
|
|
|
#if defined(MOZ_DEMANGLE_SYMBOLS)
|
1999-09-14 13:24:25 +00:00
|
|
|
/* See demangle.h in the gcc source for the voodoo */
|
|
|
|
char * demangled = cplus_demangle(aSymbol,3);
|
|
|
|
|
|
|
|
if (demangled)
|
|
|
|
{
|
|
|
|
strncpy(aBuffer,demangled,aBufLen);
|
|
|
|
|
|
|
|
free(demangled);
|
|
|
|
}
|
1999-09-14 13:30:12 +00:00
|
|
|
#endif // MOZ_DEMANGLE_SYMBOLS
|
1999-09-14 13:24:25 +00:00
|
|
|
}
|
|
|
|
#else // __linux__
|
|
|
|
NS_COM void
|
|
|
|
nsTraceRefcnt::DemangleSymbol(const char * aSymbol,
|
|
|
|
char * aBuffer,
|
|
|
|
int aBufLen)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(nsnull != aSymbol,"null symbol");
|
1999-09-14 13:44:20 +00:00
|
|
|
NS_ASSERTION(nsnull != aBuffer,"null buffer");
|
1999-09-14 13:24:25 +00:00
|
|
|
|
|
|
|
// lose
|
1999-09-14 13:44:20 +00:00
|
|
|
aBuffer[0] = '\0';
|
1999-09-14 13:24:25 +00:00
|
|
|
}
|
|
|
|
#endif // __linux__
|
|
|
|
|
1999-10-04 23:36:18 +00:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
struct CtorEntry {
|
|
|
|
const char* type;
|
|
|
|
mozCtorDtorCounter* counter;
|
|
|
|
};
|
|
|
|
|
|
|
|
nsVoidArray* nsTraceRefcnt::mCtors;
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTraceRefcnt::RegisterCtor(const char* aType,
|
|
|
|
mozCtorDtorCounter* aCounterAddr)
|
|
|
|
{
|
|
|
|
if (!mCtors) {
|
|
|
|
mCtors = new nsVoidArray();
|
|
|
|
if (!mCtors) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CtorEntry* e = new CtorEntry();
|
|
|
|
if (!e) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
e->type = aType;
|
|
|
|
e->counter = aCounterAddr;
|
|
|
|
mCtors->AppendElement(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTraceRefcnt::UnregisterCtor(const char* aType)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsTraceRefcnt::FlushCtorRegistry(void)
|
|
|
|
{
|
|
|
|
if (mCtors) {
|
|
|
|
PRInt32 i, n = mCtors->Count();
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
CtorEntry* e = (CtorEntry*) mCtors->ElementAt(i);
|
|
|
|
delete e;
|
|
|
|
}
|
|
|
|
delete mCtors;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* DEBUG */
|