1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* ***** BEGIN LICENSE BLOCK *****
3
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
5
* The contents of this file are subject to the Mozilla Public License Version
6
* 1.1 (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
8
* http://www.mozilla.org/MPL/
10
* Software distributed under the License is distributed on an "AS IS" basis,
11
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12
* for the specific language governing rights and limitations under the
15
* The Original Code is Mozilla Communicator client code.
17
* The Initial Developer of the Original Code is
18
* Netscape Communications Corporation.
19
* Portions created by the Initial Developer are Copyright (C) 1998
20
* the Initial Developer. All Rights Reserved.
23
* L. David Baron <dbaron@dbaron.org>
25
* Alternatively, the contents of this file may be used under the terms of
26
* either of the GNU General Public License Version 2 or later (the "GPL"),
27
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28
* in which case the provisions of the GPL or the LGPL are applicable instead
29
* of those above. If you wish to allow use of your version of this file only
30
* under the terms of either the GPL or the LGPL, and not to allow others to
31
* use your version of this file under the terms of the MPL, indicate your
32
* decision by deleting the provisions above and replace them with the notice
33
* and other provisions required by the GPL or the LGPL. If you do not delete
34
* the provisions above, a recipient may use your version of this file under
35
* the terms of any one of the MPL, the GPL or the LGPL.
37
* ***** END LICENSE BLOCK ***** */
39
#include "nsTraceRefcntImpl.h"
41
#include "nsISupports.h"
42
#include "nsVoidArray.h"
53
#elif defined(linux) && defined(__GLIBC__) && (defined(__i386) || defined(PPC))
57
// On glibc 2.1, the Dl_info api defined in <dlfcn.h> is only exposed
58
// if __USE_GNU is defined. I suppose its some kind of standards
61
#if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
72
#if defined(XP_MAC) && !TARGET_CARBON
73
#include "macstdlibextras.h"
76
////////////////////////////////////////////////////////////////////////////////
79
NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
80
double *meanResult, double *stdDevResult)
82
double mean = 0.0, var = 0.0, stdDev = 0.0;
83
if (n > 0.0 && sumOfValues >= 0) {
84
mean = sumOfValues / n;
85
double temp = (n * sumOfSquaredValues) - (sumOfValues * sumOfValues);
86
if (temp < 0.0 || n <= 1)
89
var = temp / (n * (n - 1));
90
// for some reason, Windows says sqrt(0.0) is "-1.#J" (?!) so do this:
91
stdDev = var != 0.0 ? sqrt(var) : 0.0;
94
*stdDevResult = stdDev;
97
////////////////////////////////////////////////////////////////////////////////
99
#ifdef NS_BUILD_REFCNT_LOGGING
105
static PRLock* gTraceLock;
107
#define LOCK_TRACELOG() PR_Lock(gTraceLock)
108
#define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
110
static PLHashTable* gBloatView;
111
static PLHashTable* gTypesToLog;
112
static PLHashTable* gObjectsToLog;
113
static PLHashTable* gSerialNumbers;
114
static PRInt32 gNextSerialNumber;
116
static PRBool gLogging;
117
static PRBool gLogToLeaky;
118
static PRBool gLogLeaksOnly;
120
static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
121
static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
123
static PRBool gInitialized = PR_FALSE;
124
static FILE *gBloatLog = nsnull;
125
static FILE *gRefcntsLog = nsnull;
126
static FILE *gAllocLog = nsnull;
127
static FILE *gLeakyLog = nsnull;
128
static FILE *gCOMPtrLog = nsnull;
129
static PRBool gActivityIsLegal = PR_FALSE;
131
struct serialNumberRecord {
132
PRInt32 serialNumber;
137
struct nsTraceRefcntStats {
142
double mRefsOutstandingTotal;
143
double mRefsOutstandingSquared;
144
double mObjsOutstandingTotal;
145
double mObjsOutstandingSquared;
148
#ifdef DEBUG_dbaron_off
149
// I hope to turn this on for everybody once we hit it a little less.
150
#define ASSERT_ACTIVITY_IS_LEGAL \
151
NS_WARN_IF_FALSE(gActivityIsLegal, \
152
"XPCOM objects created/destroyed from static ctor/dtor")
154
#define ASSERT_ACTIVITY_IS_LEGAL
158
// These functions are copied from nsprpub/lib/ds/plhash.c, with changes
159
// to the functions not called Default* to free the serialNumberRecord or
162
static void * PR_CALLBACK
163
DefaultAllocTable(void *pool, PRSize size)
166
#pragma unused (pool)
169
return PR_MALLOC(size);
172
static void PR_CALLBACK
173
DefaultFreeTable(void *pool, void *item)
176
#pragma unused (pool)
182
static PLHashEntry * PR_CALLBACK
183
DefaultAllocEntry(void *pool, const void *key)
186
#pragma unused (pool,key)
189
return PR_NEW(PLHashEntry);
192
static void PR_CALLBACK
193
SerialNumberFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
196
#pragma unused (pool)
199
if (flag == HT_FREE_ENTRY) {
200
PR_Free(NS_REINTERPRET_CAST(serialNumberRecord*,he->value));
205
static void PR_CALLBACK
206
TypesToLogFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
209
#pragma unused (pool)
212
if (flag == HT_FREE_ENTRY) {
213
nsCRT::free(NS_CONST_CAST(char*,
214
NS_REINTERPRET_CAST(const char*, he->key)));
219
static const PLHashAllocOps serialNumberHashAllocOps = {
220
DefaultAllocTable, DefaultFreeTable,
221
DefaultAllocEntry, SerialNumberFreeEntry
224
static const PLHashAllocOps typesToLogHashAllocOps = {
225
DefaultAllocTable, DefaultFreeTable,
226
DefaultAllocEntry, TypesToLogFreeEntry
229
////////////////////////////////////////////////////////////////////////////////
233
BloatEntry(const char* className, PRUint32 classSize)
234
: mClassSize(classSize) {
235
mClassName = PL_strdup(className);
242
PL_strfree(mClassName);
245
PRUint32 GetClassSize() { return (PRUint32)mClassSize; }
246
const char* GetClassName() { return mClassName; }
248
static void Clear(nsTraceRefcntStats* stats) {
250
stats->mReleases = 0;
252
stats->mDestroys = 0;
253
stats->mRefsOutstandingTotal = 0;
254
stats->mRefsOutstandingSquared = 0;
255
stats->mObjsOutstandingTotal = 0;
256
stats->mObjsOutstandingSquared = 0;
260
mAllStats.mAddRefs += mNewStats.mAddRefs;
261
mAllStats.mReleases += mNewStats.mReleases;
262
mAllStats.mCreates += mNewStats.mCreates;
263
mAllStats.mDestroys += mNewStats.mDestroys;
264
mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal;
265
mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared;
266
mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal;
267
mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared;
271
void AddRef(nsrefcnt refcnt) {
272
mNewStats.mAddRefs++;
279
void Release(nsrefcnt refcnt) {
280
mNewStats.mReleases++;
288
mNewStats.mCreates++;
293
mNewStats.mDestroys++;
298
PRInt32 cnt = (mNewStats.mAddRefs - mNewStats.mReleases);
299
mNewStats.mRefsOutstandingTotal += cnt;
300
mNewStats.mRefsOutstandingSquared += cnt * cnt;
304
PRInt32 cnt = (mNewStats.mCreates - mNewStats.mDestroys);
305
mNewStats.mObjsOutstandingTotal += cnt;
306
mNewStats.mObjsOutstandingSquared += cnt * cnt;
309
static PRIntn PR_CALLBACK DumpEntry(PLHashEntry *he, PRIntn i, void *arg) {
310
BloatEntry* entry = (BloatEntry*)he->value;
313
NS_STATIC_CAST(nsVoidArray*, arg)->AppendElement(entry);
315
return HT_ENUMERATE_NEXT;
318
static PRIntn PR_CALLBACK TotalEntries(PLHashEntry *he, PRIntn i, void *arg) {
319
BloatEntry* entry = (BloatEntry*)he->value;
320
if (entry && nsCRT::strcmp(entry->mClassName, "TOTAL") != 0) {
321
entry->Total((BloatEntry*)arg);
323
return HT_ENUMERATE_NEXT;
326
void Total(BloatEntry* total) {
327
total->mAllStats.mAddRefs += mNewStats.mAddRefs + mAllStats.mAddRefs;
328
total->mAllStats.mReleases += mNewStats.mReleases + mAllStats.mReleases;
329
total->mAllStats.mCreates += mNewStats.mCreates + mAllStats.mCreates;
330
total->mAllStats.mDestroys += mNewStats.mDestroys + mAllStats.mDestroys;
331
total->mAllStats.mRefsOutstandingTotal += mNewStats.mRefsOutstandingTotal + mAllStats.mRefsOutstandingTotal;
332
total->mAllStats.mRefsOutstandingSquared += mNewStats.mRefsOutstandingSquared + mAllStats.mRefsOutstandingSquared;
333
total->mAllStats.mObjsOutstandingTotal += mNewStats.mObjsOutstandingTotal + mAllStats.mObjsOutstandingTotal;
334
total->mAllStats.mObjsOutstandingSquared += mNewStats.mObjsOutstandingSquared + mAllStats.mObjsOutstandingSquared;
335
PRInt32 count = (mNewStats.mCreates + mAllStats.mCreates);
336
total->mClassSize += mClassSize * count; // adjust for average in DumpTotal
337
total->mTotalLeaked += (PRInt32)(mClassSize *
338
((mNewStats.mCreates + mAllStats.mCreates)
339
-(mNewStats.mDestroys + mAllStats.mDestroys)));
342
nsresult DumpTotal(PRUint32 nClasses, FILE* out) {
343
mClassSize /= mAllStats.mCreates;
344
return Dump(-1, out, nsTraceRefcntImpl::ALL_STATS);
347
static PRBool HaveLeaks(nsTraceRefcntStats* stats) {
348
return ((stats->mAddRefs != stats->mReleases) ||
349
(stats->mCreates != stats->mDestroys));
352
static nsresult PrintDumpHeader(FILE* out, const char* msg) {
353
fprintf(out, "\n== BloatView: %s\n\n", msg);
355
" |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n");
357
" Per-Inst Leaked Total Rem Mean StdDev Total Rem Mean StdDev\n");
361
nsresult Dump(PRIntn i, FILE* out, nsTraceRefcntImpl::StatisticsType type) {
362
nsTraceRefcntStats* stats = (type == nsTraceRefcntImpl::NEW_STATS) ? &mNewStats : &mAllStats;
363
if (gLogLeaksOnly && !HaveLeaks(stats)) {
367
double meanRefs, stddevRefs;
368
NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases,
369
stats->mRefsOutstandingTotal,
370
stats->mRefsOutstandingSquared,
371
&meanRefs, &stddevRefs);
373
double meanObjs, stddevObjs;
374
NS_MeanAndStdDev(stats->mCreates + stats->mDestroys,
375
stats->mObjsOutstandingTotal,
376
stats->mObjsOutstandingSquared,
377
&meanObjs, &stddevObjs);
379
if ((stats->mAddRefs - stats->mReleases) != 0 ||
380
stats->mAddRefs != 0 ||
383
(stats->mCreates - stats->mDestroys) != 0 ||
384
stats->mCreates != 0 ||
387
fprintf(out, "%4d %-40.40s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
390
(nsCRT::strcmp(mClassName, "TOTAL"))
391
?(PRInt32)((stats->mCreates - stats->mDestroys) * mClassSize)
394
(stats->mCreates - stats->mDestroys),
398
(stats->mAddRefs - stats->mReleases),
407
double mClassSize; // this is stored as a double because of the way we compute the avg class size for total bloat
408
PRInt32 mTotalLeaked; // used only for TOTAL entry
409
nsTraceRefcntStats mNewStats;
410
nsTraceRefcntStats mAllStats;
413
static void PR_CALLBACK
414
BloatViewFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
417
#pragma unused (pool)
420
if (flag == HT_FREE_ENTRY) {
421
BloatEntry* entry = NS_REINTERPRET_CAST(BloatEntry*,he->value);
427
const static PLHashAllocOps bloatViewHashAllocOps = {
428
DefaultAllocTable, DefaultFreeTable,
429
DefaultAllocEntry, BloatViewFreeEntry
435
gBloatView = PL_NewHashTable(256,
439
&bloatViewHashAllocOps, NULL);
443
GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize)
448
BloatEntry* entry = NULL;
450
entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
451
if (entry == NULL && aInstanceSize > 0) {
453
entry = new BloatEntry(aTypeName, aInstanceSize);
454
PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
460
NS_ASSERTION(aInstanceSize == 0 ||
461
entry->GetClassSize() == aInstanceSize,
462
"bad size recorded");
468
static PRIntn PR_CALLBACK DumpSerialNumbers(PLHashEntry* aHashEntry, PRIntn aIndex, void* aClosure)
470
serialNumberRecord* record = NS_REINTERPRET_CAST(serialNumberRecord *,aHashEntry->value);
471
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
472
fprintf((FILE*) aClosure, "%d (%d references; %d from COMPtrs)\n",
473
record->serialNumber,
475
record->COMPtrCount);
477
fprintf((FILE*) aClosure, "%d (%d references)\n",
478
record->serialNumber,
481
return HT_ENUMERATE_NEXT;
485
#endif /* NS_BUILD_REFCNT_LOGGING */
488
nsTraceRefcntImpl::DumpStatistics(StatisticsType type, FILE* out)
491
#ifdef NS_BUILD_REFCNT_LOGGING
492
if (gBloatLog == nsnull || gBloatView == nsnull) {
493
return NS_ERROR_FAILURE;
501
PRBool wasLogging = gLogging;
502
gLogging = PR_FALSE; // turn off logging for this method
505
if (type == NEW_STATS) {
507
msg = "NEW (incremental) LEAK STATISTICS";
509
msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
513
msg = "ALL (cumulative) LEAK STATISTICS";
515
msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
517
rv = BloatEntry::PrintDumpHeader(out, msg);
518
if (NS_FAILED(rv)) goto done;
521
BloatEntry total("TOTAL", 0);
522
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
523
total.DumpTotal(gBloatView->nentries, out);
526
PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
528
fprintf(stdout, "nsTraceRefcntImpl::DumpStatistics: %d entries\n",
531
// Sort the entries alphabetically by classname.
533
for (i = entries.Count() - 1; i >= 1; --i) {
534
for (j = i - 1; j >= 0; --j) {
535
BloatEntry* left = NS_STATIC_CAST(BloatEntry*, entries[i]);
536
BloatEntry* right = NS_STATIC_CAST(BloatEntry*, entries[j]);
538
if (PL_strcmp(left->GetClassName(), right->GetClassName()) < 0) {
539
entries.ReplaceElementAt(right, i);
540
entries.ReplaceElementAt(left, j);
545
// Enumerate from back-to-front, so things come out in alpha order
546
for (i = 0; i < entries.Count(); ++i) {
547
BloatEntry* entry = NS_STATIC_CAST(BloatEntry*, entries[i]);
548
entry->Dump(i, out, type);
552
if (gSerialNumbers) {
553
fprintf(out, "\n\nSerial Numbers of Leaked Objects:\n");
554
PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out);
558
gLogging = wasLogging;
565
nsTraceRefcntImpl::ResetStatistics()
567
#ifdef NS_BUILD_REFCNT_LOGGING
570
PL_HashTableDestroy(gBloatView);
577
#ifdef NS_BUILD_REFCNT_LOGGING
578
static PRBool LogThisType(const char* aTypeName)
580
void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
584
static PRInt32 GetSerialNumber(void* aPtr, PRBool aCreate)
586
#ifdef GC_LEAK_DETECTOR
587
// need to disguise this pointer, so the table won't keep the object alive.
588
aPtr = (void*) ~PLHashNumber(aPtr);
590
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
592
return PRInt32((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->serialNumber);
595
serialNumberRecord *record = PR_NEW(serialNumberRecord);
596
record->serialNumber = ++gNextSerialNumber;
597
record->refCount = 0;
598
record->COMPtrCount = 0;
599
PL_HashTableRawAdd(gSerialNumbers, hep, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr, NS_REINTERPRET_CAST(void*,record));
600
return gNextSerialNumber;
607
static PRInt32* GetRefCount(void* aPtr)
609
#ifdef GC_LEAK_DETECTOR
610
// need to disguise this pointer, so the table won't keep the object alive.
611
aPtr = (void*) ~PLHashNumber(aPtr);
613
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
615
return &((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->refCount);
621
static PRInt32* GetCOMPtrCount(void* aPtr)
623
#ifdef GC_LEAK_DETECTOR
624
// need to disguise this pointer, so the table won't keep the object alive.
625
aPtr = (void*) ~PLHashNumber(aPtr);
627
PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
629
return &((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->COMPtrCount);
635
static void RecycleSerialNumberPtr(void* aPtr)
637
#ifdef GC_LEAK_DETECTOR
638
// need to disguise this pointer, so the table won't keep the object alive.
639
aPtr = (void*) ~PLHashNumber(aPtr);
641
PL_HashTableRemove(gSerialNumbers, aPtr);
644
static PRBool LogThisObj(PRInt32 aSerialNumber)
646
return nsnull != PL_HashTableLookup(gObjectsToLog, (const void*)(aSerialNumber));
649
static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
651
const char* value = getenv(envVar);
653
if (nsCRT::strcmp(value, "1") == 0) {
655
fprintf(stdout, "### %s defined -- logging %s to stdout\n",
659
else if (nsCRT::strcmp(value, "2") == 0) {
661
fprintf(stdout, "### %s defined -- logging %s to stderr\n",
666
FILE *stream = ::fopen(value, "w");
667
if (stream != NULL) {
669
fprintf(stdout, "### %s defined -- logging %s to %s\n",
674
fprintf(stdout, "### %s defined -- unable to log %s to %s\n",
684
static PLHashNumber PR_CALLBACK HashNumber(const void* aKey)
686
return PLHashNumber(NS_PTR_TO_INT32(aKey));
689
static void InitTraceLog(void)
691
if (gInitialized) return;
692
gInitialized = PR_TRUE;
694
#if defined(XP_MAC) && !TARGET_CARBON
695
// this can get called before Toolbox has been initialized.
696
InitializeMacToolbox();
700
defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
702
gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
703
if (defined || gLogLeaksOnly) {
706
NS_WARNING("out of memory");
708
gLogLeaksOnly = PR_FALSE;
712
(void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
714
(void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
716
defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
718
gLogToLeaky = PR_TRUE;
722
p = dlsym(0, "__log_addref");
723
q = dlsym(0, "__log_release");
726
leakyLogAddRef = (void (*)(void*,int,int)) p;
727
leakyLogRelease = (void (*)(void*,int,int)) q;
730
gLogToLeaky = PR_FALSE;
731
fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
736
const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
738
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
740
(void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
742
if (getenv("XPCOM_MEM_COMPTR_LOG")) {
743
fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
747
const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
749
fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
754
// if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
755
// as a list of class names to track
756
gTypesToLog = PL_NewHashTable(256,
760
&typesToLogHashAllocOps, NULL);
762
NS_WARNING("out of memory");
763
fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
766
fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
767
const char* cp = classes;
769
char* cm = (char*) strchr(cp, ',');
773
PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
774
fprintf(stdout, "%s ", cp);
779
fprintf(stdout, "\n");
782
gSerialNumbers = PL_NewHashTable(256,
786
&serialNumberHashAllocOps, NULL);
791
const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
793
gObjectsToLog = PL_NewHashTable(256,
799
if (!gObjectsToLog) {
800
NS_WARNING("out of memory");
801
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
803
else if (! (gRefcntsLog || gAllocLog || gCOMPtrLog)) {
804
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
807
fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
808
const char* cp = objects;
810
char* cm = (char*) strchr(cp, ',');
829
for(PRInt32 serialno = bottom; serialno <= top; serialno++) {
830
PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
831
fprintf(stdout, "%d ", serialno);
837
fprintf(stdout, "\n");
842
if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
846
gTraceLock = PR_NewLock();
851
#if defined(_WIN32) && defined(_M_IX86) // WIN32 x86 stack walking code
852
#include "nsStackFrameWin.h"
854
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
856
DumpStackToFile(aStream);
859
// WIN32 x86 stack walking code
860
// i386 or PPC Linux stackwalking code or Solaris
861
#elif (defined(linux) && defined(__GLIBC__) && (defined(__i386) || defined(PPC))) || (defined(__sun) && (defined(__sparc) || defined(sparc) || defined(__i386) || defined(i386)))
862
#include "nsStackFrameUnix.h"
864
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
866
DumpStackToFile(aStream);
869
#elif defined(XP_MAC)
872
* Stack walking code for the Mac OS.
875
#include "gc_fragments.h"
880
void MWUnmangle(const char *mangled_name, char *unmangled_name, size_t buffersize);
883
struct traceback_table {
892
static char* pc2name(long* pc, char name[], long size)
896
// make sure pc is instruction aligned (at least).
897
if (UInt32(pc) == (UInt32(pc) & 0xFFFFFFFC)) {
898
long instructionsToLook = 4096;
899
long* instruction = (long*)pc;
901
// look for the traceback table.
902
while (instructionsToLook--) {
903
if (instruction[0] == 0x4E800020 && instruction[1] == 0x00000000) {
904
traceback_table* tb = (traceback_table*)&instruction[1];
905
memcpy(name, tb->name + 1, --nameLength);
906
name[nameLength] = '\0';
917
stack_frame* next; // savedSP
925
static asm stack_frame* getStackFrame()
932
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
934
stack_frame* currentFrame = getStackFrame(); // WalkTheStack's frame.
935
currentFrame = currentFrame->next; // WalkTheStack's caller's frame.
936
currentFrame = currentFrame->next; // WalkTheStack's caller's caller's frame.
939
// LR saved at 8(SP) in each frame. subtract 4 to get address of calling instruction.
940
void* pc = currentFrame->savedLR;
942
// convert PC to name, unmangle it, and generate source location, if possible.
943
static char symbol_name[1024], unmangled_name[1024], file_name[256]; UInt32 file_offset;
945
if (GC_address_to_source((char*)pc, symbol_name, file_name, &file_offset)) {
946
MWUnmangle(symbol_name, unmangled_name, sizeof(unmangled_name));
947
fprintf(aStream, "%s[%s,%ld]\n", unmangled_name, file_name, file_offset);
949
pc2name((long*)pc, symbol_name, sizeof(symbol_name));
950
MWUnmangle(symbol_name, unmangled_name, sizeof(unmangled_name));
951
fprintf(aStream, "%s(0x%08X)\n", unmangled_name, pc);
954
currentFrame = currentFrame->next;
955
// the bottom-most frame is marked as pointing to NULL, or is ODD if a 68K transition frame.
956
if (currentFrame == NULL || UInt32(currentFrame) & 0x1)
961
#else // unsupported platform.
964
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
966
fprintf(aStream, "write me, dammit!\n");
971
//----------------------------------------------------------------------
973
// This thing is exported by libstdc++
974
// Yes, this is a gcc only hack
975
#if defined(MOZ_DEMANGLE_SYMBOLS)
977
#include <stdlib.h> // for free()
978
#endif // MOZ_DEMANGLE_SYMBOLS
981
nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol,
985
NS_ASSERTION(nsnull != aSymbol,"null symbol");
986
NS_ASSERTION(nsnull != aBuffer,"null buffer");
987
NS_ASSERTION(aBufLen >= 32 ,"pulled 32 out of you know where");
991
#if defined(MOZ_DEMANGLE_SYMBOLS)
992
/* See demangle.h in the gcc source for the voodoo */
993
char * demangled = abi::__cxa_demangle(aSymbol,0,0,0);
997
strncpy(aBuffer,demangled,aBufLen);
1000
#endif // MOZ_DEMANGLE_SYMBOLS
1004
//----------------------------------------------------------------------
1007
nsTraceRefcntImpl::LoadLibrarySymbols(const char* aLibraryName,
1008
void* aLibrayHandle)
1010
#ifdef NS_BUILD_REFCNT_LOGGING
1011
#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */
1015
if (gAllocLog || gRefcntsLog) {
1016
fprintf(stdout, "### Loading symbols for %s\n", aLibraryName);
1019
HANDLE myProcess = ::GetCurrentProcess();
1020
BOOL ok = EnsureSymInitialized();
1022
const char* baseName = aLibraryName;
1023
// just get the base name of the library if a full path was given:
1024
PRInt32 len = strlen(aLibraryName);
1025
for (PRInt32 i = len - 1; i >= 0; i--) {
1026
if (aLibraryName[i] == '\\') {
1027
baseName = &aLibraryName[i + 1];
1031
DWORD baseAddr = _SymLoadModule(myProcess,
1037
ok = (baseAddr != nsnull);
1042
FORMAT_MESSAGE_ALLOCATE_BUFFER |
1043
FORMAT_MESSAGE_FROM_SYSTEM |
1044
FORMAT_MESSAGE_IGNORE_INSERTS,
1047
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1052
fprintf(stdout, "### ERROR: LoadLibrarySymbols for %s: %s\n",
1053
aLibraryName, lpMsgBuf);
1055
LocalFree( lpMsgBuf );
1062
//----------------------------------------------------------------------
1069
// don't use the logging ones. :-)
1070
NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::AddRef(void)
1072
NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
1077
NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::Release(void)
1079
NS_PRECONDITION(0 != mRefCnt, "dup release");
1082
mRefCnt = 1; /* stabilize */
1089
NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl, nsITraceRefcnt)
1091
nsTraceRefcntImpl::nsTraceRefcntImpl()
1093
/* member initializers and constructor code */
1097
nsTraceRefcntImpl::LogAddRef(void* aPtr,
1102
#ifdef NS_BUILD_REFCNT_LOGGING
1103
ASSERT_ACTIVITY_IS_LEGAL;
1110
BloatEntry* entry = GetBloatEntry(aClazz, classSize);
1112
entry->AddRef(aRefcnt);
1116
// Here's the case where neither NS_NEWXPCOM nor MOZ_COUNT_CTOR were used,
1117
// yet we still want to see creation information:
1119
PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
1120
PRInt32 serialno = 0;
1121
if (gSerialNumbers && loggingThisType) {
1122
serialno = GetSerialNumber(aPtr, aRefcnt == 1);
1123
PRInt32* count = GetRefCount(aPtr);
1129
PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1130
if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
1131
fprintf(gAllocLog, "\n<%s> 0x%08X %d Create\n",
1132
aClazz, NS_PTR_TO_INT32(aPtr), serialno);
1133
WalkTheStack(gAllocLog);
1136
if (gRefcntsLog && loggingThisType && loggingThisObject) {
1138
(*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
1141
// Can't use PR_LOG(), b/c it truncates the line
1142
fprintf(gRefcntsLog,
1143
"\n<%s> 0x%08X %d AddRef %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
1144
WalkTheStack(gRefcntsLog);
1145
fflush(gRefcntsLog);
1155
nsTraceRefcntImpl::LogRelease(void* aPtr,
1159
#ifdef NS_BUILD_REFCNT_LOGGING
1160
ASSERT_ACTIVITY_IS_LEGAL;
1167
BloatEntry* entry = GetBloatEntry(aClazz, 0);
1169
entry->Release(aRefcnt);
1173
PRBool loggingThisType = (!gTypesToLog || LogThisType(aClazz));
1174
PRInt32 serialno = 0;
1175
if (gSerialNumbers && loggingThisType) {
1176
serialno = GetSerialNumber(aPtr, PR_FALSE);
1177
PRInt32* count = GetRefCount(aPtr);
1183
PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1184
if (gRefcntsLog && loggingThisType && loggingThisObject) {
1186
(*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
1189
// Can't use PR_LOG(), b/c it truncates the line
1190
fprintf(gRefcntsLog,
1191
"\n<%s> 0x%08X %d Release %d\n", aClazz, NS_PTR_TO_INT32(aPtr), serialno, aRefcnt);
1192
WalkTheStack(gRefcntsLog);
1193
fflush(gRefcntsLog);
1197
// Here's the case where neither NS_DELETEXPCOM nor MOZ_COUNT_DTOR were used,
1198
// yet we still want to see deletion information:
1200
if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
1202
"\n<%s> 0x%08X %d Destroy\n",
1203
aClazz, NS_PTR_TO_INT32(aPtr), serialno);
1204
WalkTheStack(gAllocLog);
1207
if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
1208
RecycleSerialNumberPtr(aPtr);
1218
nsTraceRefcntImpl::LogCtor(void* aPtr,
1220
PRUint32 aInstanceSize)
1222
#ifdef NS_BUILD_REFCNT_LOGGING
1223
ASSERT_ACTIVITY_IS_LEGAL;
1231
BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1237
PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
1238
PRInt32 serialno = 0;
1239
if (gSerialNumbers && loggingThisType) {
1240
serialno = GetSerialNumber(aPtr, PR_TRUE);
1243
PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1244
if (gAllocLog && loggingThisType && loggingThisObject) {
1245
fprintf(gAllocLog, "\n<%s> 0x%08X %d Ctor (%d)\n",
1246
aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1247
WalkTheStack(gAllocLog);
1258
nsTraceRefcntImpl::LogDtor(void* aPtr,
1260
PRUint32 aInstanceSize)
1262
#ifdef NS_BUILD_REFCNT_LOGGING
1263
ASSERT_ACTIVITY_IS_LEGAL;
1271
BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1277
PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
1278
PRInt32 serialno = 0;
1279
if (gSerialNumbers && loggingThisType) {
1280
serialno = GetSerialNumber(aPtr, PR_FALSE);
1281
RecycleSerialNumberPtr(aPtr);
1284
PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1286
// (If we're on a losing architecture, don't do this because we'll be
1287
// using LogDeleteXPCOM instead to get file and line numbers.)
1288
if (gAllocLog && loggingThisType && loggingThisObject) {
1289
fprintf(gAllocLog, "\n<%s> 0x%08X %d Dtor (%d)\n",
1290
aType, NS_PTR_TO_INT32(aPtr), serialno, aInstanceSize);
1291
WalkTheStack(gAllocLog);
1302
nsTraceRefcntImpl::LogAddCOMPtr(void* aCOMPtr,
1303
nsISupports* aObject)
1305
#if defined(NS_BUILD_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1306
// Get the most-derived object.
1307
void *object = dynamic_cast<void *>(aObject);
1309
// This is a very indirect way of finding out what the class is
1310
// of the object being logged. If we're logging a specific type,
1312
if (!gTypesToLog || !gSerialNumbers) {
1315
PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
1316
if (serialno == 0) {
1325
PRInt32* count = GetCOMPtrCount(object);
1329
PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1331
if (gCOMPtrLog && loggingThisObject) {
1332
fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrAddRef %d 0x%08X\n",
1333
NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1334
WalkTheStack(gCOMPtrLog);
1345
nsTraceRefcntImpl::LogReleaseCOMPtr(void* aCOMPtr,
1346
nsISupports* aObject)
1348
#if defined(NS_BUILD_REFCNT_LOGGING) && defined(HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR)
1349
// Get the most-derived object.
1350
void *object = dynamic_cast<void *>(aObject);
1352
// This is a very indirect way of finding out what the class is
1353
// of the object being logged. If we're logging a specific type,
1355
if (!gTypesToLog || !gSerialNumbers) {
1358
PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
1359
if (serialno == 0) {
1368
PRInt32* count = GetCOMPtrCount(object);
1372
PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1374
if (gCOMPtrLog && loggingThisObject) {
1375
fprintf(gCOMPtrLog, "\n<?> 0x%08X %d nsCOMPtrRelease %d 0x%08X\n",
1376
NS_PTR_TO_INT32(object), serialno, count?(*count):-1, NS_PTR_TO_INT32(aCOMPtr));
1377
WalkTheStack(gCOMPtrLog);
1387
nsTraceRefcntImpl::Startup()
1389
#ifdef NS_BUILD_REFCNT_LOGGING
1390
SetActivityIsLegal(PR_TRUE);
1395
nsTraceRefcntImpl::Shutdown()
1397
#ifdef NS_BUILD_REFCNT_LOGGING
1400
PL_HashTableDestroy(gBloatView);
1401
gBloatView = nsnull;
1404
PL_HashTableDestroy(gTypesToLog);
1405
gTypesToLog = nsnull;
1407
if (gObjectsToLog) {
1408
PL_HashTableDestroy(gObjectsToLog);
1409
gObjectsToLog = nsnull;
1411
if (gSerialNumbers) {
1412
PL_HashTableDestroy(gSerialNumbers);
1413
gSerialNumbers = nsnull;
1416
SetActivityIsLegal(PR_FALSE);
1422
nsTraceRefcntImpl::SetActivityIsLegal(PRBool aLegal)
1424
#ifdef NS_BUILD_REFCNT_LOGGING
1425
gActivityIsLegal = aLegal;
1431
nsTraceRefcntImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
1433
*aInstancePtr = nsnull;
1434
nsITraceRefcnt* tracer = new nsTraceRefcntImpl();
1436
return NS_ERROR_OUT_OF_MEMORY;
1438
nsresult rv = tracer->QueryInterface(aIID, aInstancePtr);
1439
if (NS_FAILED(rv)) {