~ubuntu-branches/ubuntu/trusty/virtualbox-lts-xenial/trusty-updates

« back to all changes in this revision

Viewing changes to src/libs/xpcom18a4/xpcom/base/nsTraceRefcntImpl.cpp

  • Committer: Package Import Robot
  • Author(s): Gianfranco Costamagna
  • Date: 2016-02-23 14:28:26 UTC
  • Revision ID: package-import@ubuntu.com-20160223142826-bdu69el2z6wa2a44
Tags: upstream-4.3.36-dfsg
ImportĀ upstreamĀ versionĀ 4.3.36-dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
4
 *
 
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/
 
9
 *
 
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
 
13
 * License.
 
14
 *
 
15
 * The Original Code is Mozilla Communicator client code.
 
16
 *
 
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.
 
21
 *
 
22
 * Contributor(s):
 
23
 *   L. David Baron <dbaron@dbaron.org>
 
24
 *
 
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.
 
36
 *
 
37
 * ***** END LICENSE BLOCK ***** */
 
38
 
 
39
#include "nsTraceRefcntImpl.h"
 
40
#include "nscore.h"
 
41
#include "nsISupports.h"
 
42
#include "nsVoidArray.h"
 
43
#include "prprf.h"
 
44
#include "prlog.h"
 
45
#include "plstr.h"
 
46
#include <stdlib.h>
 
47
#include "nsCOMPtr.h"
 
48
#include "nsCRT.h"
 
49
#include <math.h>
 
50
 
 
51
#if defined(_WIN32)
 
52
#include <windows.h>
 
53
#elif defined(linux) && defined(__GLIBC__) && (defined(__i386) || defined(PPC))
 
54
#include <setjmp.h>
 
55
 
 
56
//
 
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
 
59
// adherence thing.
 
60
//
 
61
#if (__GLIBC_MINOR__ >= 1) && !defined(__USE_GNU)
 
62
#define __USE_GNU
 
63
#endif
 
64
 
 
65
#include <dlfcn.h>
 
66
#endif
 
67
 
 
68
#ifdef HAVE_LIBDL
 
69
#include <dlfcn.h>
 
70
#endif
 
71
 
 
72
#if defined(XP_MAC) && !TARGET_CARBON
 
73
#include "macstdlibextras.h"
 
74
#endif
 
75
 
 
76
////////////////////////////////////////////////////////////////////////////////
 
77
 
 
78
NS_COM void 
 
79
NS_MeanAndStdDev(double n, double sumOfValues, double sumOfSquaredValues,
 
80
                 double *meanResult, double *stdDevResult)
 
81
{
 
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)
 
87
      var = 0.0;
 
88
    else
 
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;
 
92
  }
 
93
  *meanResult = mean;
 
94
  *stdDevResult = stdDev;
 
95
}
 
96
 
 
97
////////////////////////////////////////////////////////////////////////////////
 
98
 
 
99
#ifdef NS_BUILD_REFCNT_LOGGING
 
100
#include "plhash.h"
 
101
#include "prmem.h"
 
102
 
 
103
#include "prlock.h"
 
104
 
 
105
static PRLock* gTraceLock;
 
106
 
 
107
#define LOCK_TRACELOG()   PR_Lock(gTraceLock)
 
108
#define UNLOCK_TRACELOG() PR_Unlock(gTraceLock)
 
109
 
 
110
static PLHashTable* gBloatView;
 
111
static PLHashTable* gTypesToLog;
 
112
static PLHashTable* gObjectsToLog;
 
113
static PLHashTable* gSerialNumbers;
 
114
static PRInt32 gNextSerialNumber;
 
115
 
 
116
static PRBool gLogging;
 
117
static PRBool gLogToLeaky;
 
118
static PRBool gLogLeaksOnly;
 
119
 
 
120
static void (*leakyLogAddRef)(void* p, int oldrc, int newrc);
 
121
static void (*leakyLogRelease)(void* p, int oldrc, int newrc);
 
122
 
 
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;
 
130
 
 
131
struct serialNumberRecord {
 
132
  PRInt32 serialNumber;
 
133
  PRInt32 refCount;
 
134
  PRInt32 COMPtrCount;
 
135
};
 
136
 
 
137
struct nsTraceRefcntStats {
 
138
  nsrefcnt mAddRefs;
 
139
  nsrefcnt mReleases;
 
140
  nsrefcnt mCreates;
 
141
  nsrefcnt mDestroys;
 
142
  double mRefsOutstandingTotal;
 
143
  double mRefsOutstandingSquared;
 
144
  double mObjsOutstandingTotal;
 
145
  double mObjsOutstandingSquared;
 
146
};
 
147
 
 
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")
 
153
#else
 
154
#define ASSERT_ACTIVITY_IS_LEGAL
 
155
#endif
 
156
 
 
157
 
 
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
 
160
// the BloatEntry.
 
161
 
 
162
static void * PR_CALLBACK
 
163
DefaultAllocTable(void *pool, PRSize size)
 
164
{
 
165
#if defined(XP_MAC)
 
166
#pragma unused (pool)
 
167
#endif
 
168
 
 
169
    return PR_MALLOC(size);
 
170
}
 
171
 
 
172
static void PR_CALLBACK
 
173
DefaultFreeTable(void *pool, void *item)
 
174
{
 
175
#if defined(XP_MAC)
 
176
#pragma unused (pool)
 
177
#endif
 
178
 
 
179
    PR_Free(item);
 
180
}
 
181
 
 
182
static PLHashEntry * PR_CALLBACK
 
183
DefaultAllocEntry(void *pool, const void *key)
 
184
{
 
185
#if defined(XP_MAC)
 
186
#pragma unused (pool,key)
 
187
#endif
 
188
 
 
189
    return PR_NEW(PLHashEntry);
 
190
}
 
191
 
 
192
static void PR_CALLBACK
 
193
SerialNumberFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
 
194
{
 
195
#if defined(XP_MAC)
 
196
#pragma unused (pool)
 
197
#endif
 
198
 
 
199
    if (flag == HT_FREE_ENTRY) {
 
200
        PR_Free(NS_REINTERPRET_CAST(serialNumberRecord*,he->value));
 
201
        PR_Free(he);
 
202
    }
 
203
}
 
204
 
 
205
static void PR_CALLBACK
 
206
TypesToLogFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
 
207
{
 
208
#if defined(XP_MAC)
 
209
#pragma unused (pool)
 
210
#endif
 
211
 
 
212
    if (flag == HT_FREE_ENTRY) {
 
213
        nsCRT::free(NS_CONST_CAST(char*,
 
214
                     NS_REINTERPRET_CAST(const char*, he->key)));
 
215
        PR_Free(he);
 
216
    }
 
217
}
 
218
 
 
219
static const PLHashAllocOps serialNumberHashAllocOps = {
 
220
    DefaultAllocTable, DefaultFreeTable,
 
221
    DefaultAllocEntry, SerialNumberFreeEntry
 
222
};
 
223
 
 
224
static const PLHashAllocOps typesToLogHashAllocOps = {
 
225
    DefaultAllocTable, DefaultFreeTable,
 
226
    DefaultAllocEntry, TypesToLogFreeEntry
 
227
};
 
228
 
 
229
////////////////////////////////////////////////////////////////////////////////
 
230
 
 
231
class BloatEntry {
 
232
public:
 
233
  BloatEntry(const char* className, PRUint32 classSize)
 
234
    : mClassSize(classSize) { 
 
235
    mClassName = PL_strdup(className);
 
236
    Clear(&mNewStats);
 
237
    Clear(&mAllStats);
 
238
    mTotalLeaked = 0;
 
239
  }
 
240
 
 
241
  ~BloatEntry() {
 
242
    PL_strfree(mClassName);
 
243
  }
 
244
 
 
245
  PRUint32 GetClassSize() { return (PRUint32)mClassSize; }
 
246
  const char* GetClassName() { return mClassName; }
 
247
 
 
248
  static void Clear(nsTraceRefcntStats* stats) {
 
249
    stats->mAddRefs = 0;
 
250
    stats->mReleases = 0;
 
251
    stats->mCreates = 0;
 
252
    stats->mDestroys = 0;
 
253
    stats->mRefsOutstandingTotal = 0;
 
254
    stats->mRefsOutstandingSquared = 0;
 
255
    stats->mObjsOutstandingTotal = 0;
 
256
    stats->mObjsOutstandingSquared = 0;
 
257
  }
 
258
 
 
259
  void Accumulate() {
 
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;
 
268
      Clear(&mNewStats);
 
269
  }
 
270
 
 
271
  void AddRef(nsrefcnt refcnt) {
 
272
    mNewStats.mAddRefs++;
 
273
    if (refcnt == 1) {
 
274
      Ctor();
 
275
    }
 
276
    AccountRefs();
 
277
  }
 
278
 
 
279
  void Release(nsrefcnt refcnt) {
 
280
    mNewStats.mReleases++;
 
281
    if (refcnt == 0) {
 
282
      Dtor();
 
283
    }
 
284
    AccountRefs();
 
285
  }
 
286
 
 
287
  void Ctor() {
 
288
    mNewStats.mCreates++;
 
289
    AccountObjs();
 
290
  }
 
291
 
 
292
  void Dtor() {
 
293
    mNewStats.mDestroys++;
 
294
    AccountObjs();
 
295
  }
 
296
 
 
297
  void AccountRefs() {
 
298
    PRInt32 cnt = (mNewStats.mAddRefs - mNewStats.mReleases);
 
299
    mNewStats.mRefsOutstandingTotal += cnt;
 
300
    mNewStats.mRefsOutstandingSquared += cnt * cnt;
 
301
  }
 
302
 
 
303
  void AccountObjs() {
 
304
    PRInt32 cnt = (mNewStats.mCreates - mNewStats.mDestroys);
 
305
    mNewStats.mObjsOutstandingTotal += cnt;
 
306
    mNewStats.mObjsOutstandingSquared += cnt * cnt;
 
307
  }
 
308
 
 
309
  static PRIntn PR_CALLBACK DumpEntry(PLHashEntry *he, PRIntn i, void *arg) {
 
310
    BloatEntry* entry = (BloatEntry*)he->value;
 
311
    if (entry) {
 
312
      entry->Accumulate();
 
313
      NS_STATIC_CAST(nsVoidArray*, arg)->AppendElement(entry);
 
314
    }
 
315
    return HT_ENUMERATE_NEXT;
 
316
  }
 
317
 
 
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);
 
322
    }
 
323
    return HT_ENUMERATE_NEXT;
 
324
  }
 
325
 
 
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)));
 
340
  }
 
341
 
 
342
  nsresult DumpTotal(PRUint32 nClasses, FILE* out) {
 
343
    mClassSize /= mAllStats.mCreates;
 
344
    return Dump(-1, out, nsTraceRefcntImpl::ALL_STATS);
 
345
  }
 
346
 
 
347
  static PRBool HaveLeaks(nsTraceRefcntStats* stats) {
 
348
    return ((stats->mAddRefs != stats->mReleases) ||
 
349
            (stats->mCreates != stats->mDestroys));
 
350
  }
 
351
 
 
352
  static nsresult PrintDumpHeader(FILE* out, const char* msg) {
 
353
    fprintf(out, "\n== BloatView: %s\n\n", msg);
 
354
    fprintf(out, 
 
355
        "     |<----------------Class--------------->|<-----Bytes------>|<----------------Objects---------------->|<--------------References-------------->|\n");
 
356
    fprintf(out,
 
357
        "                                              Per-Inst   Leaked    Total      Rem      Mean       StdDev     Total      Rem      Mean       StdDev\n");
 
358
    return NS_OK;
 
359
  }
 
360
 
 
361
  nsresult Dump(PRIntn i, FILE* out, nsTraceRefcntImpl::StatisticsType type) {
 
362
    nsTraceRefcntStats* stats = (type == nsTraceRefcntImpl::NEW_STATS) ? &mNewStats : &mAllStats;
 
363
    if (gLogLeaksOnly && !HaveLeaks(stats)) {
 
364
      return NS_OK;
 
365
    }
 
366
 
 
367
    double meanRefs, stddevRefs;
 
368
    NS_MeanAndStdDev(stats->mAddRefs + stats->mReleases, 
 
369
                     stats->mRefsOutstandingTotal, 
 
370
                     stats->mRefsOutstandingSquared,
 
371
                     &meanRefs, &stddevRefs);
 
372
 
 
373
    double meanObjs, stddevObjs;
 
374
    NS_MeanAndStdDev(stats->mCreates + stats->mDestroys,
 
375
                     stats->mObjsOutstandingTotal, 
 
376
                     stats->mObjsOutstandingSquared,
 
377
                     &meanObjs, &stddevObjs);
 
378
 
 
379
    if ((stats->mAddRefs - stats->mReleases) != 0 ||
 
380
        stats->mAddRefs != 0 ||
 
381
        meanRefs != 0 ||
 
382
        stddevRefs != 0 ||
 
383
        (stats->mCreates - stats->mDestroys) != 0 ||
 
384
        stats->mCreates != 0 ||
 
385
        meanObjs != 0 ||
 
386
        stddevObjs != 0) {
 
387
      fprintf(out, "%4d %-40.40s %8d %8d %8d %8d (%8.2f +/- %8.2f) %8d %8d (%8.2f +/- %8.2f)\n",
 
388
              i+1, mClassName,
 
389
              (PRInt32)mClassSize,
 
390
              (nsCRT::strcmp(mClassName, "TOTAL"))
 
391
                  ?(PRInt32)((stats->mCreates - stats->mDestroys) * mClassSize)
 
392
                  :mTotalLeaked,
 
393
              stats->mCreates,
 
394
              (stats->mCreates - stats->mDestroys),
 
395
              meanObjs,
 
396
              stddevObjs, 
 
397
              stats->mAddRefs,
 
398
              (stats->mAddRefs - stats->mReleases),
 
399
              meanRefs,
 
400
              stddevRefs);
 
401
    }
 
402
    return NS_OK;
 
403
  }
 
404
 
 
405
protected:
 
406
  char*         mClassName;
 
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;
 
411
};
 
412
 
 
413
static void PR_CALLBACK
 
414
BloatViewFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
 
415
{
 
416
#if defined(XP_MAC)
 
417
#pragma unused (pool)
 
418
#endif
 
419
 
 
420
    if (flag == HT_FREE_ENTRY) {
 
421
        BloatEntry* entry = NS_REINTERPRET_CAST(BloatEntry*,he->value);
 
422
        delete entry;
 
423
        PR_Free(he);
 
424
    }
 
425
}
 
426
 
 
427
const static PLHashAllocOps bloatViewHashAllocOps = {
 
428
    DefaultAllocTable, DefaultFreeTable,
 
429
    DefaultAllocEntry, BloatViewFreeEntry
 
430
};
 
431
 
 
432
static void
 
433
RecreateBloatView()
 
434
{
 
435
  gBloatView = PL_NewHashTable(256, 
 
436
                               PL_HashString,
 
437
                               PL_CompareStrings,
 
438
                               PL_CompareValues,
 
439
                               &bloatViewHashAllocOps, NULL);
 
440
}
 
441
 
 
442
static BloatEntry*
 
443
GetBloatEntry(const char* aTypeName, PRUint32 aInstanceSize)
 
444
{
 
445
  if (!gBloatView) {
 
446
    RecreateBloatView();
 
447
  }
 
448
  BloatEntry* entry = NULL;
 
449
  if (gBloatView) {
 
450
    entry = (BloatEntry*)PL_HashTableLookup(gBloatView, aTypeName);
 
451
    if (entry == NULL && aInstanceSize > 0) {
 
452
 
 
453
      entry = new BloatEntry(aTypeName, aInstanceSize);
 
454
      PLHashEntry* e = PL_HashTableAdd(gBloatView, aTypeName, entry);
 
455
      if (e == NULL) {
 
456
        delete entry;
 
457
        entry = NULL;
 
458
      }
 
459
    } else {
 
460
      NS_ASSERTION(aInstanceSize == 0 || 
 
461
                   entry->GetClassSize() == aInstanceSize, 
 
462
                   "bad size recorded");
 
463
    }
 
464
  }
 
465
  return entry;
 
466
}
 
467
 
 
468
static PRIntn PR_CALLBACK DumpSerialNumbers(PLHashEntry* aHashEntry, PRIntn aIndex, void* aClosure)
 
469
{
 
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,
 
474
                            record->refCount,
 
475
                            record->COMPtrCount);
 
476
#else
 
477
  fprintf((FILE*) aClosure, "%d (%d references)\n",
 
478
                            record->serialNumber,
 
479
                            record->refCount);
 
480
#endif
 
481
  return HT_ENUMERATE_NEXT;
 
482
}
 
483
 
 
484
 
 
485
#endif /* NS_BUILD_REFCNT_LOGGING */
 
486
 
 
487
nsresult
 
488
nsTraceRefcntImpl::DumpStatistics(StatisticsType type, FILE* out)
 
489
{
 
490
  nsresult rv = NS_OK;
 
491
#ifdef NS_BUILD_REFCNT_LOGGING
 
492
  if (gBloatLog == nsnull || gBloatView == nsnull) {
 
493
    return NS_ERROR_FAILURE;
 
494
  }
 
495
  if (out == nsnull) {
 
496
    out = gBloatLog;
 
497
  }
 
498
 
 
499
  LOCK_TRACELOG();
 
500
 
 
501
  PRBool wasLogging = gLogging;
 
502
  gLogging = PR_FALSE;  // turn off logging for this method
 
503
  
 
504
  const char* msg;
 
505
  if (type == NEW_STATS) {
 
506
    if (gLogLeaksOnly)
 
507
      msg = "NEW (incremental) LEAK STATISTICS";
 
508
    else
 
509
      msg = "NEW (incremental) LEAK AND BLOAT STATISTICS";
 
510
  }
 
511
  else {
 
512
    if (gLogLeaksOnly)
 
513
      msg = "ALL (cumulative) LEAK STATISTICS";
 
514
    else
 
515
      msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
 
516
  }
 
517
  rv = BloatEntry::PrintDumpHeader(out, msg);
 
518
  if (NS_FAILED(rv)) goto done;
 
519
 
 
520
  {
 
521
    BloatEntry total("TOTAL", 0);
 
522
    PL_HashTableEnumerateEntries(gBloatView, BloatEntry::TotalEntries, &total);
 
523
    total.DumpTotal(gBloatView->nentries, out);
 
524
 
 
525
    nsVoidArray entries;
 
526
    PL_HashTableEnumerateEntries(gBloatView, BloatEntry::DumpEntry, &entries);
 
527
 
 
528
    fprintf(stdout, "nsTraceRefcntImpl::DumpStatistics: %d entries\n",
 
529
           entries.Count());
 
530
 
 
531
    // Sort the entries alphabetically by classname.
 
532
    PRInt32 i, j;
 
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]);
 
537
 
 
538
        if (PL_strcmp(left->GetClassName(), right->GetClassName()) < 0) {
 
539
          entries.ReplaceElementAt(right, i);
 
540
          entries.ReplaceElementAt(left, j);
 
541
        }
 
542
      }
 
543
    }
 
544
 
 
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);
 
549
    }
 
550
  }
 
551
 
 
552
  if (gSerialNumbers) {
 
553
    fprintf(out, "\n\nSerial Numbers of Leaked Objects:\n");
 
554
    PL_HashTableEnumerateEntries(gSerialNumbers, DumpSerialNumbers, out);
 
555
  }
 
556
 
 
557
done:
 
558
  gLogging = wasLogging;
 
559
  UNLOCK_TRACELOG();
 
560
#endif
 
561
  return rv;
 
562
}
 
563
 
 
564
void
 
565
nsTraceRefcntImpl::ResetStatistics()
 
566
{
 
567
#ifdef NS_BUILD_REFCNT_LOGGING
 
568
  LOCK_TRACELOG();
 
569
  if (gBloatView) {
 
570
    PL_HashTableDestroy(gBloatView);
 
571
    gBloatView = nsnull;
 
572
  }
 
573
  UNLOCK_TRACELOG();
 
574
#endif
 
575
}
 
576
 
 
577
#ifdef NS_BUILD_REFCNT_LOGGING
 
578
static PRBool LogThisType(const char* aTypeName)
 
579
{
 
580
  void* he = PL_HashTableLookup(gTypesToLog, aTypeName);
 
581
  return nsnull != he;
 
582
}
 
583
 
 
584
static PRInt32 GetSerialNumber(void* aPtr, PRBool aCreate)
 
585
{
 
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);
 
589
#endif
 
590
  PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
 
591
  if (hep && *hep) {
 
592
    return PRInt32((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->serialNumber);
 
593
  }
 
594
  else if (aCreate) {
 
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;
 
601
  }
 
602
  else {
 
603
    return 0;
 
604
  }
 
605
}
 
606
 
 
607
static PRInt32* GetRefCount(void* aPtr)
 
608
{
 
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);
 
612
#endif
 
613
  PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
 
614
  if (hep && *hep) {
 
615
    return &((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->refCount);
 
616
  } else {
 
617
    return nsnull;
 
618
  }
 
619
}
 
620
 
 
621
static PRInt32* GetCOMPtrCount(void* aPtr)
 
622
{
 
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);
 
626
#endif
 
627
  PLHashEntry** hep = PL_HashTableRawLookup(gSerialNumbers, PLHashNumber(NS_PTR_TO_INT32(aPtr)), aPtr);
 
628
  if (hep && *hep) {
 
629
    return &((NS_REINTERPRET_CAST(serialNumberRecord*,(*hep)->value))->COMPtrCount);
 
630
  } else {
 
631
    return nsnull;
 
632
  }
 
633
}
 
634
 
 
635
static void RecycleSerialNumberPtr(void* aPtr)
 
636
{
 
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);
 
640
#endif
 
641
  PL_HashTableRemove(gSerialNumbers, aPtr);
 
642
}
 
643
 
 
644
static PRBool LogThisObj(PRInt32 aSerialNumber)
 
645
{
 
646
  return nsnull != PL_HashTableLookup(gObjectsToLog, (const void*)(aSerialNumber));
 
647
}
 
648
 
 
649
static PRBool InitLog(const char* envVar, const char* msg, FILE* *result)
 
650
{
 
651
  const char* value = getenv(envVar);
 
652
  if (value) {
 
653
    if (nsCRT::strcmp(value, "1") == 0) {
 
654
      *result = stdout;
 
655
      fprintf(stdout, "### %s defined -- logging %s to stdout\n", 
 
656
              envVar, msg);
 
657
      return PR_TRUE;
 
658
    }
 
659
    else if (nsCRT::strcmp(value, "2") == 0) {
 
660
      *result = stderr;
 
661
      fprintf(stdout, "### %s defined -- logging %s to stderr\n", 
 
662
              envVar, msg);
 
663
      return PR_TRUE;
 
664
    }
 
665
    else {
 
666
      FILE *stream = ::fopen(value, "w");
 
667
      if (stream != NULL) {
 
668
        *result = stream;
 
669
        fprintf(stdout, "### %s defined -- logging %s to %s\n", 
 
670
                envVar, msg, value);
 
671
        return PR_TRUE;
 
672
      }
 
673
      else {
 
674
        fprintf(stdout, "### %s defined -- unable to log %s to %s\n", 
 
675
                envVar, msg, value);
 
676
        return PR_FALSE;
 
677
      }
 
678
    }
 
679
  }
 
680
  return PR_FALSE;
 
681
}
 
682
 
 
683
 
 
684
static PLHashNumber PR_CALLBACK HashNumber(const void* aKey)
 
685
{
 
686
  return PLHashNumber(NS_PTR_TO_INT32(aKey));
 
687
}
 
688
 
 
689
static void InitTraceLog(void)
 
690
{
 
691
  if (gInitialized) return;
 
692
  gInitialized = PR_TRUE;
 
693
 
 
694
#if defined(XP_MAC) && !TARGET_CARBON
 
695
  // this can get called before Toolbox has been initialized.
 
696
  InitializeMacToolbox();
 
697
#endif
 
698
 
 
699
  PRBool defined;
 
700
  defined = InitLog("XPCOM_MEM_BLOAT_LOG", "bloat/leaks", &gBloatLog);
 
701
  if (!defined)
 
702
    gLogLeaksOnly = InitLog("XPCOM_MEM_LEAK_LOG", "leaks", &gBloatLog);
 
703
  if (defined || gLogLeaksOnly) {
 
704
    RecreateBloatView();
 
705
    if (!gBloatView) {
 
706
      NS_WARNING("out of memory");
 
707
      gBloatLog = nsnull;
 
708
      gLogLeaksOnly = PR_FALSE;
 
709
    }
 
710
  }
 
711
 
 
712
  (void)InitLog("XPCOM_MEM_REFCNT_LOG", "refcounts", &gRefcntsLog);
 
713
 
 
714
  (void)InitLog("XPCOM_MEM_ALLOC_LOG", "new/delete", &gAllocLog);
 
715
 
 
716
  defined = InitLog("XPCOM_MEM_LEAKY_LOG", "for leaky", &gLeakyLog);
 
717
  if (defined) {
 
718
    gLogToLeaky = PR_TRUE;
 
719
    void* p = nsnull;
 
720
    void* q = nsnull;
 
721
#ifdef HAVE_LIBDL
 
722
    p = dlsym(0, "__log_addref");
 
723
    q = dlsym(0, "__log_release");
 
724
#endif
 
725
    if (p && q) {
 
726
      leakyLogAddRef = (void (*)(void*,int,int)) p;
 
727
      leakyLogRelease = (void (*)(void*,int,int)) q;
 
728
    }
 
729
    else {
 
730
      gLogToLeaky = PR_FALSE;
 
731
      fprintf(stdout, "### ERROR: XPCOM_MEM_LEAKY_LOG defined, but can't locate __log_addref and __log_release symbols\n");
 
732
      fflush(stdout);
 
733
    }
 
734
  }
 
735
 
 
736
  const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
 
737
 
 
738
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
 
739
  if (classes) {
 
740
    (void)InitLog("XPCOM_MEM_COMPTR_LOG", "nsCOMPtr", &gCOMPtrLog);
 
741
  } else {
 
742
    if (getenv("XPCOM_MEM_COMPTR_LOG")) {
 
743
      fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
 
744
    }
 
745
  }
 
746
#else
 
747
  const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
 
748
  if (comptr_log) {
 
749
    fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
 
750
  }
 
751
#endif
 
752
 
 
753
  if (classes) {
 
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,
 
757
                                  PL_HashString,
 
758
                                  PL_CompareStrings,
 
759
                                  PL_CompareValues,
 
760
                                  &typesToLogHashAllocOps, NULL);
 
761
    if (!gTypesToLog) {
 
762
      NS_WARNING("out of memory");
 
763
      fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- unable to log specific classes\n");
 
764
    }
 
765
    else {
 
766
      fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
 
767
      const char* cp = classes;
 
768
      for (;;) {
 
769
        char* cm = (char*) strchr(cp, ',');
 
770
        if (cm) {
 
771
          *cm = '\0';
 
772
        }
 
773
        PL_HashTableAdd(gTypesToLog, nsCRT::strdup(cp), (void*)1);
 
774
        fprintf(stdout, "%s ", cp);
 
775
        if (!cm) break;
 
776
        *cm = ',';
 
777
        cp = cm + 1;
 
778
      }
 
779
      fprintf(stdout, "\n");
 
780
    }
 
781
 
 
782
    gSerialNumbers = PL_NewHashTable(256,
 
783
                                     HashNumber,
 
784
                                     PL_CompareValues,
 
785
                                     PL_CompareValues,
 
786
                                     &serialNumberHashAllocOps, NULL);
 
787
 
 
788
 
 
789
  }
 
790
 
 
791
  const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
 
792
  if (objects) {
 
793
    gObjectsToLog = PL_NewHashTable(256,
 
794
                                    HashNumber,
 
795
                                    PL_CompareValues,
 
796
                                    PL_CompareValues,
 
797
                                    NULL, NULL);
 
798
 
 
799
    if (!gObjectsToLog) {
 
800
      NS_WARNING("out of memory");
 
801
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- unable to log specific objects\n");
 
802
    }
 
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");
 
805
    }
 
806
    else {
 
807
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
 
808
      const char* cp = objects;
 
809
      for (;;) {
 
810
        char* cm = (char*) strchr(cp, ',');
 
811
        if (cm) {
 
812
          *cm = '\0';
 
813
        }
 
814
        PRInt32 top = 0;
 
815
        PRInt32 bottom = 0;
 
816
        while (*cp) {
 
817
          if (*cp == '-') {
 
818
            bottom = top;
 
819
            top = 0;
 
820
            ++cp;
 
821
          }
 
822
          top *= 10;
 
823
          top += *cp - '0';
 
824
          ++cp;
 
825
        }
 
826
        if (!bottom) {
 
827
          bottom = top;
 
828
        }
 
829
        for(PRInt32 serialno = bottom; serialno <= top; serialno++) {
 
830
          PL_HashTableAdd(gObjectsToLog, (const void*)serialno, (void*)1);
 
831
          fprintf(stdout, "%d ", serialno);
 
832
        }
 
833
        if (!cm) break;
 
834
        *cm = ',';
 
835
        cp = cm + 1;
 
836
      }
 
837
      fprintf(stdout, "\n");
 
838
    }
 
839
  }
 
840
 
 
841
 
 
842
  if (gBloatLog || gRefcntsLog || gAllocLog || gLeakyLog || gCOMPtrLog) {
 
843
    gLogging = PR_TRUE;
 
844
  }
 
845
 
 
846
  gTraceLock = PR_NewLock();
 
847
}
 
848
 
 
849
#endif
 
850
 
 
851
#if defined(_WIN32) && defined(_M_IX86) // WIN32 x86 stack walking code
 
852
#include "nsStackFrameWin.h"
 
853
void
 
854
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
 
855
{
 
856
  DumpStackToFile(aStream);
 
857
}
 
858
 
 
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"
 
863
void
 
864
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
 
865
{
 
866
  DumpStackToFile(aStream);
 
867
}
 
868
 
 
869
#elif defined(XP_MAC)
 
870
 
 
871
/**
 
872
 * Stack walking code for the Mac OS.
 
873
 */
 
874
 
 
875
#include "gc_fragments.h"
 
876
 
 
877
#include <typeinfo>
 
878
 
 
879
extern "C" {
 
880
void MWUnmangle(const char *mangled_name, char *unmangled_name, size_t buffersize);
 
881
}
 
882
 
 
883
struct traceback_table {
 
884
        long zero;
 
885
        long magic;
 
886
        long reserved;
 
887
        long codeSize;
 
888
        short nameLength;
 
889
        char name[2];
 
890
};
 
891
 
 
892
static char* pc2name(long* pc, char name[], long size)
 
893
{
 
894
        name[0] = '\0';
 
895
        
 
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;
 
900
                
 
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';
 
907
                                break;
 
908
                        }
 
909
                        ++instruction;
 
910
                }
 
911
        }
 
912
        
 
913
        return name;
 
914
}
 
915
 
 
916
struct stack_frame {
 
917
        stack_frame*    next;                           // savedSP
 
918
        void*                   savedCR;
 
919
        void*                   savedLR;
 
920
        void*                   reserved0;
 
921
        void*                   reserved1;
 
922
        void*                   savedTOC;
 
923
};
 
924
 
 
925
static asm stack_frame* getStackFrame() 
 
926
{
 
927
        mr              r3, sp
 
928
        blr
 
929
}
 
930
 
 
931
NS_COM void
 
932
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
 
933
{
 
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.
 
937
    
 
938
        while (true) {
 
939
                // LR saved at 8(SP) in each frame. subtract 4 to get address of calling instruction.
 
940
                void* pc = currentFrame->savedLR;
 
941
 
 
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;
 
944
                
 
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);
 
948
        } else {
 
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);
 
952
        }
 
953
 
 
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)
 
957
                        break;
 
958
        }
 
959
}
 
960
 
 
961
#else // unsupported platform.
 
962
 
 
963
void
 
964
nsTraceRefcntImpl::WalkTheStack(FILE* aStream)
 
965
{
 
966
        fprintf(aStream, "write me, dammit!\n");
 
967
}
 
968
 
 
969
#endif
 
970
 
 
971
//----------------------------------------------------------------------
 
972
 
 
973
// This thing is exported by libstdc++
 
974
// Yes, this is a gcc only hack
 
975
#if defined(MOZ_DEMANGLE_SYMBOLS)
 
976
#include <cxxabi.h>
 
977
#include <stdlib.h> // for free()
 
978
#endif // MOZ_DEMANGLE_SYMBOLS
 
979
 
 
980
NS_COM void 
 
981
nsTraceRefcntImpl::DemangleSymbol(const char * aSymbol, 
 
982
                              char * aBuffer,
 
983
                              int aBufLen)
 
984
{
 
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");
 
988
 
 
989
  aBuffer[0] = '\0';
 
990
 
 
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);
 
994
 
 
995
  if (demangled)
 
996
  {
 
997
    strncpy(aBuffer,demangled,aBufLen);
 
998
    free(demangled);
 
999
  }
 
1000
#endif // MOZ_DEMANGLE_SYMBOLS
 
1001
}
 
1002
 
 
1003
 
 
1004
//----------------------------------------------------------------------
 
1005
 
 
1006
NS_COM void
 
1007
nsTraceRefcntImpl::LoadLibrarySymbols(const char* aLibraryName,
 
1008
                                  void* aLibrayHandle)
 
1009
{
 
1010
#ifdef NS_BUILD_REFCNT_LOGGING
 
1011
#if defined(_WIN32) && defined(_M_IX86) /* Win32 x86 only */
 
1012
  if (!gInitialized)
 
1013
    InitTraceLog();
 
1014
 
 
1015
  if (gAllocLog || gRefcntsLog) {
 
1016
    fprintf(stdout, "### Loading symbols for %s\n", aLibraryName);
 
1017
    fflush(stdout);
 
1018
 
 
1019
    HANDLE myProcess = ::GetCurrentProcess();    
 
1020
    BOOL ok = EnsureSymInitialized();
 
1021
    if (ok) {
 
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];
 
1028
          break;
 
1029
        }
 
1030
      }
 
1031
      DWORD baseAddr = _SymLoadModule(myProcess,
 
1032
                                     NULL,
 
1033
                                     (char*)baseName,
 
1034
                                     (char*)baseName,
 
1035
                                     0,
 
1036
                                     0);
 
1037
      ok = (baseAddr != nsnull);
 
1038
    }
 
1039
    if (!ok) {
 
1040
      LPVOID lpMsgBuf;
 
1041
      FormatMessage( 
 
1042
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
 
1043
        FORMAT_MESSAGE_FROM_SYSTEM | 
 
1044
        FORMAT_MESSAGE_IGNORE_INSERTS,
 
1045
        NULL,
 
1046
        GetLastError(),
 
1047
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
 
1048
        (LPTSTR) &lpMsgBuf,
 
1049
        0,
 
1050
        NULL 
 
1051
        );
 
1052
      fprintf(stdout, "### ERROR: LoadLibrarySymbols for %s: %s\n",
 
1053
              aLibraryName, lpMsgBuf);
 
1054
      fflush(stdout);
 
1055
      LocalFree( lpMsgBuf );
 
1056
    }
 
1057
  }
 
1058
#endif
 
1059
#endif
 
1060
}
 
1061
 
 
1062
//----------------------------------------------------------------------
 
1063
 
 
1064
 
 
1065
 
 
1066
 
 
1067
 
 
1068
 
 
1069
// don't use the logging ones. :-)
 
1070
NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::AddRef(void)
 
1071
{
 
1072
  NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
 
1073
  ++mRefCnt;
 
1074
  return mRefCnt;
 
1075
}
 
1076
 
 
1077
NS_IMETHODIMP_(nsrefcnt) nsTraceRefcntImpl::Release(void)                                
 
1078
{                                                                             
 
1079
  NS_PRECONDITION(0 != mRefCnt, "dup release");                               
 
1080
  --mRefCnt;                                                                  
 
1081
  if (mRefCnt == 0) {                                                         
 
1082
    mRefCnt = 1; /* stabilize */                                              
 
1083
    delete this;                                                              
 
1084
    return 0;                                                                 
 
1085
  }
 
1086
  return mRefCnt;                                                             
 
1087
}
 
1088
 
 
1089
NS_IMPL_QUERY_INTERFACE1(nsTraceRefcntImpl, nsITraceRefcnt)
 
1090
 
 
1091
nsTraceRefcntImpl::nsTraceRefcntImpl()
 
1092
{
 
1093
  /* member initializers and constructor code */
 
1094
}
 
1095
 
 
1096
NS_IMETHODIMP 
 
1097
nsTraceRefcntImpl::LogAddRef(void* aPtr,
 
1098
                             nsrefcnt aRefcnt,
 
1099
                             const char* aClazz,
 
1100
                             PRUint32 classSize)
 
1101
{
 
1102
#ifdef NS_BUILD_REFCNT_LOGGING
 
1103
  ASSERT_ACTIVITY_IS_LEGAL;
 
1104
  if (!gInitialized)
 
1105
    InitTraceLog();
 
1106
  if (gLogging) {
 
1107
    LOCK_TRACELOG();
 
1108
 
 
1109
    if (gBloatLog) {
 
1110
      BloatEntry* entry = GetBloatEntry(aClazz, classSize);
 
1111
      if (entry) {
 
1112
        entry->AddRef(aRefcnt);
 
1113
      }
 
1114
    }
 
1115
 
 
1116
    // Here's the case where neither NS_NEWXPCOM nor MOZ_COUNT_CTOR were used,
 
1117
    // yet we still want to see creation information:
 
1118
 
 
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);
 
1124
      if(count)
 
1125
        (*count)++;
 
1126
 
 
1127
    }
 
1128
 
 
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);
 
1134
    }
 
1135
 
 
1136
    if (gRefcntsLog && loggingThisType && loggingThisObject) {
 
1137
      if (gLogToLeaky) {
 
1138
        (*leakyLogAddRef)(aPtr, aRefcnt - 1, aRefcnt);
 
1139
      }
 
1140
      else {        
 
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);
 
1146
      }
 
1147
    }
 
1148
    UNLOCK_TRACELOG();
 
1149
  }
 
1150
#endif
 
1151
  return NS_OK;
 
1152
}
 
1153
 
 
1154
NS_IMETHODIMP 
 
1155
nsTraceRefcntImpl::LogRelease(void* aPtr,
 
1156
                          nsrefcnt aRefcnt,
 
1157
                          const char* aClazz)
 
1158
{
 
1159
#ifdef NS_BUILD_REFCNT_LOGGING
 
1160
  ASSERT_ACTIVITY_IS_LEGAL;
 
1161
  if (!gInitialized)
 
1162
    InitTraceLog();
 
1163
  if (gLogging) {
 
1164
    LOCK_TRACELOG();
 
1165
 
 
1166
    if (gBloatLog) {
 
1167
      BloatEntry* entry = GetBloatEntry(aClazz, 0);
 
1168
      if (entry) {
 
1169
        entry->Release(aRefcnt);
 
1170
      }
 
1171
    }
 
1172
 
 
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);
 
1178
      if(count)
 
1179
        (*count)--;
 
1180
 
 
1181
    }
 
1182
 
 
1183
    PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
 
1184
    if (gRefcntsLog && loggingThisType && loggingThisObject) {
 
1185
      if (gLogToLeaky) {
 
1186
        (*leakyLogRelease)(aPtr, aRefcnt + 1, aRefcnt);
 
1187
      }
 
1188
      else {
 
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);
 
1194
      }
 
1195
    }
 
1196
 
 
1197
    // Here's the case where neither NS_DELETEXPCOM nor MOZ_COUNT_DTOR were used,
 
1198
    // yet we still want to see deletion information:
 
1199
 
 
1200
    if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
 
1201
      fprintf(gAllocLog,
 
1202
              "\n<%s> 0x%08X %d Destroy\n",
 
1203
              aClazz, NS_PTR_TO_INT32(aPtr), serialno);
 
1204
      WalkTheStack(gAllocLog);
 
1205
    }
 
1206
 
 
1207
    if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
 
1208
      RecycleSerialNumberPtr(aPtr);
 
1209
    }
 
1210
 
 
1211
    UNLOCK_TRACELOG();
 
1212
  }
 
1213
#endif
 
1214
  return NS_OK;
 
1215
}
 
1216
 
 
1217
NS_IMETHODIMP 
 
1218
nsTraceRefcntImpl::LogCtor(void* aPtr,
 
1219
                           const char* aType,
 
1220
                           PRUint32 aInstanceSize)
 
1221
{
 
1222
#ifdef NS_BUILD_REFCNT_LOGGING
 
1223
  ASSERT_ACTIVITY_IS_LEGAL;
 
1224
  if (!gInitialized)
 
1225
    InitTraceLog();
 
1226
 
 
1227
  if (gLogging) {
 
1228
    LOCK_TRACELOG();
 
1229
 
 
1230
    if (gBloatLog) {
 
1231
      BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
 
1232
      if (entry) {
 
1233
        entry->Ctor();
 
1234
      }
 
1235
    }
 
1236
 
 
1237
    PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
 
1238
    PRInt32 serialno = 0;
 
1239
    if (gSerialNumbers && loggingThisType) {
 
1240
      serialno = GetSerialNumber(aPtr, PR_TRUE);
 
1241
    }
 
1242
 
 
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);
 
1248
    }
 
1249
 
 
1250
    UNLOCK_TRACELOG();
 
1251
  }
 
1252
#endif
 
1253
  return NS_OK;
 
1254
}
 
1255
 
 
1256
 
 
1257
NS_IMETHODIMP 
 
1258
nsTraceRefcntImpl::LogDtor(void* aPtr, 
 
1259
                           const char* aType,
 
1260
                           PRUint32 aInstanceSize)
 
1261
{
 
1262
#ifdef NS_BUILD_REFCNT_LOGGING
 
1263
  ASSERT_ACTIVITY_IS_LEGAL;
 
1264
  if (!gInitialized)
 
1265
    InitTraceLog();
 
1266
 
 
1267
  if (gLogging) {
 
1268
    LOCK_TRACELOG();
 
1269
 
 
1270
    if (gBloatLog) {
 
1271
      BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
 
1272
      if (entry) {
 
1273
        entry->Dtor();
 
1274
      }
 
1275
    }
 
1276
 
 
1277
    PRBool loggingThisType = (!gTypesToLog || LogThisType(aType));
 
1278
    PRInt32 serialno = 0;
 
1279
    if (gSerialNumbers && loggingThisType) {
 
1280
      serialno = GetSerialNumber(aPtr, PR_FALSE);
 
1281
      RecycleSerialNumberPtr(aPtr);
 
1282
    }
 
1283
 
 
1284
    PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
 
1285
 
 
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);
 
1292
    }
 
1293
 
 
1294
    UNLOCK_TRACELOG();
 
1295
  }
 
1296
#endif
 
1297
  return NS_OK;
 
1298
}
 
1299
 
 
1300
 
 
1301
NS_IMETHODIMP 
 
1302
nsTraceRefcntImpl::LogAddCOMPtr(void* aCOMPtr,
 
1303
                            nsISupports* aObject)
 
1304
{
 
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);
 
1308
 
 
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,
 
1311
  // then 
 
1312
  if (!gTypesToLog || !gSerialNumbers) {
 
1313
    return NS_OK;
 
1314
  }
 
1315
  PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
 
1316
  if (serialno == 0) {
 
1317
    return NS_OK;
 
1318
  }
 
1319
 
 
1320
  if (!gInitialized)
 
1321
    InitTraceLog();
 
1322
  if (gLogging) {
 
1323
    LOCK_TRACELOG();
 
1324
 
 
1325
    PRInt32* count = GetCOMPtrCount(object);
 
1326
    if(count)
 
1327
      (*count)++;
 
1328
 
 
1329
    PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
 
1330
 
 
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);
 
1335
    }
 
1336
 
 
1337
    UNLOCK_TRACELOG();
 
1338
  }
 
1339
#endif
 
1340
  return NS_OK;
 
1341
}
 
1342
 
 
1343
 
 
1344
NS_IMETHODIMP 
 
1345
nsTraceRefcntImpl::LogReleaseCOMPtr(void* aCOMPtr,
 
1346
                                    nsISupports* aObject)
 
1347
{
 
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);
 
1351
 
 
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,
 
1354
  // then 
 
1355
  if (!gTypesToLog || !gSerialNumbers) {
 
1356
    return NS_OK;
 
1357
  }
 
1358
  PRInt32 serialno = GetSerialNumber(object, PR_FALSE);
 
1359
  if (serialno == 0) {
 
1360
    return NS_OK;
 
1361
  }
 
1362
 
 
1363
  if (!gInitialized)
 
1364
    InitTraceLog();
 
1365
  if (gLogging) {
 
1366
    LOCK_TRACELOG();
 
1367
 
 
1368
    PRInt32* count = GetCOMPtrCount(object);
 
1369
    if(count)
 
1370
      (*count)--;
 
1371
 
 
1372
    PRBool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
 
1373
 
 
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);
 
1378
    }
 
1379
 
 
1380
    UNLOCK_TRACELOG();
 
1381
  }
 
1382
#endif
 
1383
  return NS_OK;
 
1384
}
 
1385
 
 
1386
NS_COM void
 
1387
nsTraceRefcntImpl::Startup()
 
1388
{
 
1389
#ifdef NS_BUILD_REFCNT_LOGGING
 
1390
  SetActivityIsLegal(PR_TRUE);
 
1391
#endif
 
1392
}
 
1393
 
 
1394
NS_COM void
 
1395
nsTraceRefcntImpl::Shutdown()
 
1396
{
 
1397
#ifdef NS_BUILD_REFCNT_LOGGING
 
1398
 
 
1399
  if (gBloatView) {
 
1400
    PL_HashTableDestroy(gBloatView);
 
1401
    gBloatView = nsnull;
 
1402
  }
 
1403
  if (gTypesToLog) {
 
1404
    PL_HashTableDestroy(gTypesToLog);
 
1405
    gTypesToLog = nsnull;
 
1406
  }
 
1407
  if (gObjectsToLog) {
 
1408
    PL_HashTableDestroy(gObjectsToLog);
 
1409
    gObjectsToLog = nsnull;
 
1410
  }
 
1411
  if (gSerialNumbers) {
 
1412
    PL_HashTableDestroy(gSerialNumbers);
 
1413
    gSerialNumbers = nsnull;
 
1414
  }
 
1415
 
 
1416
  SetActivityIsLegal(PR_FALSE);
 
1417
 
 
1418
#endif
 
1419
}
 
1420
 
 
1421
NS_COM void
 
1422
nsTraceRefcntImpl::SetActivityIsLegal(PRBool aLegal)
 
1423
{
 
1424
#ifdef NS_BUILD_REFCNT_LOGGING
 
1425
  gActivityIsLegal = aLegal;
 
1426
#endif
 
1427
}
 
1428
 
 
1429
 
 
1430
NS_METHOD
 
1431
nsTraceRefcntImpl::Create(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
 
1432
{
 
1433
  *aInstancePtr = nsnull;
 
1434
  nsITraceRefcnt* tracer = new nsTraceRefcntImpl();
 
1435
  if (!tracer)
 
1436
    return NS_ERROR_OUT_OF_MEMORY;
 
1437
  
 
1438
  nsresult rv = tracer->QueryInterface(aIID, aInstancePtr);
 
1439
  if (NS_FAILED(rv)) {
 
1440
    delete tracer;
 
1441
  }
 
1442
  
 
1443
  return rv;
 
1444
}