Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/base/nsTraceRefcnt.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "nsTraceRefcnt.h"
8
#include "mozilla/CycleCollectedJSContext.h"
9
#include "mozilla/IntegerPrintfMacros.h"
10
#include "mozilla/Path.h"
11
#include "mozilla/StaticPtr.h"
12
#include "nsXPCOMPrivate.h"
13
#include "nscore.h"
14
#include "nsClassHashtable.h"
15
#include "nsISupports.h"
16
#include "nsHashKeys.h"
17
#include "nsTArray.h"
18
#include "nsTHashtable.h"
19
#include "prenv.h"
20
#include "plstr.h"
21
#include "prlink.h"
22
#include "nsCRT.h"
23
#include <math.h>
24
#include "nsHashKeys.h"
25
#include "mozilla/StackWalk.h"
26
#include "nsThreadUtils.h"
27
#include "CodeAddressService.h"
28
29
#include "nsXULAppAPI.h"
30
#ifdef XP_WIN
31
#include <process.h>
32
#define getpid _getpid
33
#else
34
#include <unistd.h>
35
#endif
36
37
#include "mozilla/Atomics.h"
38
#include "mozilla/AutoRestore.h"
39
#include "mozilla/BlockingResourceBase.h"
40
#include "mozilla/PoisonIOInterposer.h"
41
#include "mozilla/UniquePtr.h"
42
43
#include <string>
44
#include <vector>
45
46
#ifdef HAVE_DLOPEN
47
#include <dlfcn.h>
48
#endif
49
50
#ifdef MOZ_DMD
51
#include "base/process_util.h"
52
#include "nsMemoryInfoDumper.h"
53
#endif
54
55
////////////////////////////////////////////////////////////////////////////////
56
57
#include "prthread.h"
58
59
// We use a spin lock instead of a regular mutex because this lock is usually
60
// only held for a very short time, and gets grabbed at a very high frequency
61
// (~100000 times per second). On Mac, the overhead of using a regular lock
62
// is very high, see bug 1137963.
63
static mozilla::Atomic<uintptr_t, mozilla::ReleaseAcquire> gTraceLogLocked;
64
65
struct MOZ_STACK_CLASS AutoTraceLogLock final
66
{
67
  bool doRelease;
68
  AutoTraceLogLock()
69
    : doRelease(true)
70
0
  {
71
0
    uintptr_t currentThread = reinterpret_cast<uintptr_t>(PR_GetCurrentThread());
72
0
    if (gTraceLogLocked == currentThread) {
73
0
      doRelease = false;
74
0
    } else {
75
0
      while (!gTraceLogLocked.compareExchange(0, currentThread)) {
76
0
        PR_Sleep(PR_INTERVAL_NO_WAIT); /* yield */
77
0
      }
78
0
    }
79
0
  }
80
0
  ~AutoTraceLogLock() { if (doRelease) gTraceLogLocked = 0; }
81
};
82
83
class BloatEntry;
84
struct SerialNumberRecord;
85
86
using BloatHash = nsClassHashtable<nsDepCharHashKey, BloatEntry>;
87
using CharPtrSet = nsTHashtable<nsCharPtrHashKey>;
88
using IntPtrSet = nsTHashtable<IntPtrHashKey>;
89
using SerialHash = nsClassHashtable<nsVoidPtrHashKey, SerialNumberRecord>;
90
91
static StaticAutoPtr<BloatHash> gBloatView;
92
static StaticAutoPtr<CharPtrSet> gTypesToLog;
93
static StaticAutoPtr<IntPtrSet> gObjectsToLog;
94
static StaticAutoPtr<SerialHash> gSerialNumbers;
95
96
static intptr_t gNextSerialNumber;
97
static bool gDumpedStatistics = false;
98
static bool gLogJSStacks = false;
99
100
// By default, debug builds only do bloat logging. Bloat logging
101
// only tries to record when an object is created or destroyed, so we
102
// optimize the common case in NS_LogAddRef and NS_LogRelease where
103
// only bloat logging is enabled and no logging needs to be done.
104
enum LoggingType
105
{
106
  NoLogging,
107
  OnlyBloatLogging,
108
  FullLogging
109
};
110
111
static LoggingType gLogging;
112
113
static bool gLogLeaksOnly;
114
115
9
#define BAD_TLS_INDEX ((unsigned)-1)
116
117
// if gActivityTLS == BAD_TLS_INDEX, then we're
118
// unitialized... otherwise this points to a NSPR TLS thread index
119
// indicating whether addref activity is legal. If the PTR_TO_INT32 is 0 then
120
// activity is ok, otherwise not!
121
static unsigned gActivityTLS = BAD_TLS_INDEX;
122
123
static bool gInitialized;
124
static nsrefcnt gInitCount;
125
126
static FILE* gBloatLog = nullptr;
127
static FILE* gRefcntsLog = nullptr;
128
static FILE* gAllocLog = nullptr;
129
static FILE* gCOMPtrLog = nullptr;
130
131
static void
132
WalkTheStackSavingLocations(std::vector<void*>& aLocations);
133
134
struct SerialNumberRecord
135
{
136
  SerialNumberRecord()
137
    : serialNumber(++gNextSerialNumber)
138
    , refCount(0)
139
    , COMPtrCount(0)
140
0
  {}
141
142
  intptr_t serialNumber;
143
  int32_t refCount;
144
  int32_t COMPtrCount;
145
  // We use std:: classes here rather than the XPCOM equivalents because the
146
  // XPCOM equivalents do leak-checking, and if you try to leak-check while
147
  // leak-checking, you're gonna have a bad time.
148
  std::vector<void*> allocationStack;
149
  mozilla::UniquePtr<char[]> jsStack;
150
151
0
  void SaveJSStack() {
152
0
    // If this thread isn't running JS, there's nothing to do.
153
0
    if (!CycleCollectedJSContext::Get()) {
154
0
      return;
155
0
    }
156
0
157
0
    JSContext* cx = nsContentUtils::GetCurrentJSContext();
158
0
    if (!cx) {
159
0
      return;
160
0
    }
161
0
162
0
    JS::UniqueChars chars = xpc_PrintJSStack(cx,
163
0
                                             /*showArgs=*/ false,
164
0
                                             /*showLocals=*/ false,
165
0
                                             /*showThisProps=*/ false);
166
0
    size_t len = strlen(chars.get());
167
0
    jsStack = MakeUnique<char[]>(len + 1);
168
0
    memcpy(jsStack.get(), chars.get(), len + 1);
169
0
  }
170
};
171
172
struct nsTraceRefcntStats
173
{
174
  uint64_t mCreates;
175
  uint64_t mDestroys;
176
177
  bool HaveLeaks() const
178
0
  {
179
0
    return mCreates != mDestroys;
180
0
  }
181
182
  void Clear()
183
0
  {
184
0
    mCreates = 0;
185
0
    mDestroys = 0;
186
0
  }
187
188
  int64_t NumLeaked() const
189
0
  {
190
0
    return (int64_t)(mCreates - mDestroys);
191
0
  }
192
};
193
194
#ifdef DEBUG
195
static const char kStaticCtorDtorWarning[] =
196
  "XPCOM objects created/destroyed from static ctor/dtor";
197
198
static void
199
AssertActivityIsLegal()
200
{
201
  if (recordreplay::IsRecordingOrReplaying()) {
202
    // Avoid recorded events in the TLS accesses below.
203
    return;
204
  }
205
  if (gActivityTLS == BAD_TLS_INDEX || PR_GetThreadPrivate(gActivityTLS)) {
206
    if (PR_GetEnv("MOZ_FATAL_STATIC_XPCOM_CTORS_DTORS")) {
207
      MOZ_CRASH_UNSAFE_OOL(kStaticCtorDtorWarning);
208
    } else {
209
      NS_WARNING(kStaticCtorDtorWarning);
210
    }
211
  }
212
}
213
#  define ASSERT_ACTIVITY_IS_LEGAL              \
214
  do {                                          \
215
    AssertActivityIsLegal();                    \
216
  } while(0)
217
#else
218
0
#  define ASSERT_ACTIVITY_IS_LEGAL do { } while(0)
219
#endif // DEBUG
220
221
////////////////////////////////////////////////////////////////////////////////
222
223
class CodeAddressServiceStringTable final
224
{
225
public:
226
0
  CodeAddressServiceStringTable() : mSet(32) {}
227
228
  const char* Intern(const char* aString)
229
0
  {
230
0
    nsCharPtrHashKey* e = mSet.PutEntry(aString);
231
0
    return e->GetKey();
232
0
  }
233
234
  size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
235
0
  {
236
0
    return mSet.SizeOfExcludingThis(aMallocSizeOf);
237
0
  }
238
239
private:
240
  typedef nsTHashtable<nsCharPtrHashKey> StringSet;
241
  StringSet mSet;
242
};
243
244
struct CodeAddressServiceStringAlloc final
245
{
246
0
  static char* copy(const char* aStr) { return strdup(aStr); }
247
0
  static void free(char* aPtr) { ::free(aPtr); }
248
};
249
250
// WalkTheStack does not hold any locks needed by MozDescribeCodeAddress, so
251
// this class does not need to do anything.
252
struct CodeAddressServiceLock final
253
{
254
0
  static void Unlock() {}
255
0
  static void Lock() {}
256
0
  static bool IsLocked() { return true; }
257
};
258
259
typedef mozilla::CodeAddressService<CodeAddressServiceStringTable,
260
                                    CodeAddressServiceStringAlloc,
261
                                    CodeAddressServiceLock> WalkTheStackCodeAddressService;
262
263
mozilla::StaticAutoPtr<WalkTheStackCodeAddressService> gCodeAddressService;
264
265
////////////////////////////////////////////////////////////////////////////////
266
267
class BloatEntry
268
{
269
public:
270
  BloatEntry(const char* aClassName, uint32_t aClassSize)
271
    : mClassSize(aClassSize)
272
    , mStats()
273
0
  {
274
0
    MOZ_ASSERT(strlen(aClassName) > 0, "BloatEntry name must be non-empty");
275
0
    mClassName = PL_strdup(aClassName);
276
0
    mStats.Clear();
277
0
    mTotalLeaked = 0;
278
0
  }
279
280
  ~BloatEntry()
281
0
  {
282
0
    PL_strfree(mClassName);
283
0
  }
284
285
  uint32_t GetClassSize()
286
0
  {
287
0
    return (uint32_t)mClassSize;
288
0
  }
289
  const char* GetClassName()
290
0
  {
291
0
    return mClassName;
292
0
  }
293
294
  void Ctor()
295
0
  {
296
0
    mStats.mCreates++;
297
0
  }
298
299
  void Dtor()
300
0
  {
301
0
    mStats.mDestroys++;
302
0
  }
303
304
  void Total(BloatEntry* aTotal)
305
0
  {
306
0
    aTotal->mStats.mCreates += mStats.mCreates;
307
0
    aTotal->mStats.mDestroys += mStats.mDestroys;
308
0
    aTotal->mClassSize += mClassSize * mStats.mCreates;    // adjust for average in DumpTotal
309
0
    aTotal->mTotalLeaked += mClassSize * mStats.NumLeaked();
310
0
  }
311
312
  void DumpTotal(FILE* aOut)
313
0
  {
314
0
    mClassSize /= mStats.mCreates;
315
0
    Dump(-1, aOut);
316
0
  }
317
318
  bool PrintDumpHeader(FILE* aOut, const char* aMsg)
319
0
  {
320
0
    fprintf(aOut, "\n== BloatView: %s, %s process %d\n", aMsg,
321
0
            XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid());
322
0
    if (gLogLeaksOnly && !mStats.HaveLeaks()) {
323
0
      return false;
324
0
    }
325
0
326
0
    fprintf(aOut,
327
0
            "\n" \
328
0
            "     |<----------------Class--------------->|<-----Bytes------>|<----Objects---->|\n" \
329
0
            "     |                                      | Per-Inst   Leaked|   Total      Rem|\n");
330
0
331
0
    this->DumpTotal(aOut);
332
0
333
0
    return true;
334
0
  }
335
336
  void Dump(int aIndex, FILE* aOut)
337
0
  {
338
0
    if (gLogLeaksOnly && !mStats.HaveLeaks()) {
339
0
      return;
340
0
    }
341
0
342
0
    if (mStats.HaveLeaks() || mStats.mCreates != 0) {
343
0
      fprintf(aOut, "%4d |%-38.38s| %8d %8" PRId64 "|%8" PRIu64 " %8" PRId64"|\n",
344
0
              aIndex + 1, mClassName,
345
0
              GetClassSize(),
346
0
              nsCRT::strcmp(mClassName, "TOTAL") ? (mStats.NumLeaked() * GetClassSize()) : mTotalLeaked,
347
0
              mStats.mCreates,
348
0
              mStats.NumLeaked());
349
0
    }
350
0
  }
351
352
protected:
353
  char* mClassName;
354
  double mClassSize; // This is stored as a double because of the way we compute the avg class size for total bloat.
355
  int64_t mTotalLeaked; // Used only for TOTAL entry.
356
  nsTraceRefcntStats mStats;
357
};
358
359
static void
360
RecreateBloatView()
361
0
{
362
0
  gBloatView = new BloatHash(256);
363
0
}
364
365
static BloatEntry*
366
GetBloatEntry(const char* aTypeName, uint32_t aInstanceSize)
367
0
{
368
0
  if (!gBloatView) {
369
0
    RecreateBloatView();
370
0
  }
371
0
  BloatEntry* entry = gBloatView->Get(aTypeName);
372
0
  if (!entry && aInstanceSize > 0) {
373
0
    entry = new BloatEntry(aTypeName, aInstanceSize);
374
0
    gBloatView->Put(aTypeName, entry);
375
0
  } else {
376
0
    MOZ_ASSERT(aInstanceSize == 0 || entry->GetClassSize() == aInstanceSize,
377
0
         "Mismatched sizes were recorded in the memory leak logging table. "
378
0
         "The usual cause of this is having a templated class that uses "
379
0
         "MOZ_COUNT_{C,D}TOR in the constructor or destructor, respectively. "
380
0
         "As a workaround, the MOZ_COUNT_{C,D}TOR calls can be moved to a "
381
0
         "non-templated base class. Another possible cause is a runnable with "
382
0
         "an mName that matches another refcounted class.");
383
0
  }
384
0
  return entry;
385
0
}
386
387
static void
388
DumpSerialNumbers(const SerialHash::Iterator& aHashEntry, FILE* aFd)
389
0
{
390
0
  SerialNumberRecord* record = aHashEntry.Data();
391
0
  auto* outputFile = aFd;
392
0
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
393
0
  fprintf(outputFile, "%" PRIdPTR
394
0
          " @%p (%d references; %d from COMPtrs)\n",
395
0
          record->serialNumber,
396
0
          aHashEntry.Key(),
397
0
          record->refCount,
398
0
          record->COMPtrCount);
399
#else
400
  fprintf(outputFile, "%" PRIdPTR
401
          " @%p (%d references)\n",
402
          record->serialNumber,
403
          aHashEntry.Key(),
404
          record->refCount);
405
#endif
406
0
  if (!record->allocationStack.empty()) {
407
0
    static const size_t bufLen = 1024;
408
0
    char buf[bufLen];
409
0
    fprintf(outputFile, "allocation stack:\n");
410
0
    for (size_t i = 0, length = record->allocationStack.size();
411
0
         i < length;
412
0
         ++i) {
413
0
      gCodeAddressService->GetLocation(i, record->allocationStack[i],
414
0
                                       buf, bufLen);
415
0
      fprintf(outputFile, "%s\n", buf);
416
0
    }
417
0
  }
418
0
419
0
  if (gLogJSStacks) {
420
0
    if (record->jsStack) {
421
0
      fprintf(outputFile, "JS allocation stack:\n%s\n", record->jsStack.get());
422
0
    } else {
423
0
      fprintf(outputFile, "There is no JS context on the stack.\n");
424
0
    }
425
0
  }
426
0
}
427
428
429
template<>
430
class nsDefaultComparator<BloatEntry*, BloatEntry*>
431
{
432
public:
433
  bool Equals(BloatEntry* const& aEntry1, BloatEntry* const& aEntry2) const
434
0
  {
435
0
    return PL_strcmp(aEntry1->GetClassName(), aEntry2->GetClassName()) == 0;
436
0
  }
437
  bool LessThan(BloatEntry* const& aEntry1, BloatEntry* const& aEntry2) const
438
0
  {
439
0
    return PL_strcmp(aEntry1->GetClassName(), aEntry2->GetClassName()) < 0;
440
0
  }
441
};
442
443
444
nsresult
445
nsTraceRefcnt::DumpStatistics()
446
0
{
447
0
  if (!gBloatLog || !gBloatView) {
448
0
    return NS_ERROR_FAILURE;
449
0
  }
450
0
451
0
  AutoTraceLogLock lock;
452
0
453
0
  MOZ_ASSERT(!gDumpedStatistics,
454
0
             "Calling DumpStatistics more than once may result in "
455
0
             "bogus positive or negative leaks being reported");
456
0
  gDumpedStatistics = true;
457
0
458
0
  // Don't try to log while we hold the lock, we'd deadlock.
459
0
  AutoRestore<LoggingType> saveLogging(gLogging);
460
0
  gLogging = NoLogging;
461
0
462
0
  BloatEntry total("TOTAL", 0);
463
0
  for (auto iter = gBloatView->Iter(); !iter.Done(); iter.Next()) {
464
0
    BloatEntry* entry = iter.Data();
465
0
    if (nsCRT::strcmp(entry->GetClassName(), "TOTAL") != 0) {
466
0
      entry->Total(&total);
467
0
    }
468
0
  }
469
0
470
0
  const char* msg;
471
0
  if (gLogLeaksOnly) {
472
0
    msg = "ALL (cumulative) LEAK STATISTICS";
473
0
  } else {
474
0
    msg = "ALL (cumulative) LEAK AND BLOAT STATISTICS";
475
0
  }
476
0
  const bool leaked = total.PrintDumpHeader(gBloatLog, msg);
477
0
478
0
  nsTArray<BloatEntry*> entries;
479
0
  for (auto iter = gBloatView->Iter(); !iter.Done(); iter.Next()) {
480
0
    entries.AppendElement(iter.Data());
481
0
  }
482
0
483
0
  const uint32_t count = entries.Length();
484
0
485
0
  if (!gLogLeaksOnly || leaked) {
486
0
    // Sort the entries alphabetically by classname.
487
0
    entries.Sort();
488
0
489
0
    for (uint32_t i = 0; i < count; ++i) {
490
0
      BloatEntry* entry = entries[i];
491
0
      entry->Dump(i, gBloatLog);
492
0
    }
493
0
494
0
    fprintf(gBloatLog, "\n");
495
0
  }
496
0
497
0
  fprintf(gBloatLog, "nsTraceRefcnt::DumpStatistics: %d entries\n", count);
498
0
499
0
  if (gSerialNumbers) {
500
0
    fprintf(gBloatLog, "\nSerial Numbers of Leaked Objects:\n");
501
0
    for (auto iter = gSerialNumbers->Iter(); !iter.Done(); iter.Next()) {
502
0
      DumpSerialNumbers(iter, gBloatLog);
503
0
    }
504
0
  }
505
0
506
0
  return NS_OK;
507
0
}
508
509
void
510
nsTraceRefcnt::ResetStatistics()
511
0
{
512
0
  AutoTraceLogLock lock;
513
0
  gBloatView = nullptr;
514
0
}
515
516
static intptr_t
517
GetSerialNumber(void* aPtr, bool aCreate)
518
0
{
519
0
  if (!aCreate) {
520
0
    auto record = gSerialNumbers->Get(aPtr);
521
0
    return record ? record->serialNumber : 0;
522
0
  }
523
0
524
0
  auto entry = gSerialNumbers->LookupForAdd(aPtr);
525
0
  if (entry) {
526
0
    MOZ_CRASH("If an object already has a serial number, we should be destroying it.");
527
0
  }
528
0
529
0
  auto record = entry.OrInsert([]() { return new SerialNumberRecord(); });
530
0
  WalkTheStackSavingLocations(record->allocationStack);
531
0
  if (gLogJSStacks) {
532
0
    record->SaveJSStack();
533
0
  }
534
0
  return gNextSerialNumber;
535
0
}
536
537
static void
538
RecycleSerialNumberPtr(void* aPtr)
539
0
{
540
0
  gSerialNumbers->Remove(aPtr);
541
0
}
542
543
static bool
544
LogThisObj(intptr_t aSerialNumber)
545
0
{
546
0
  return gObjectsToLog->Contains(aSerialNumber);
547
0
}
548
549
using EnvCharType = mozilla::filesystem::Path::value_type;
550
551
static bool
552
InitLog(const EnvCharType* aEnvVar, const char* aMsg, FILE** aResult)
553
0
{
554
#ifdef XP_WIN
555
  // This is gross, I know.
556
  const wchar_t* envvar = reinterpret_cast<const wchar_t*>(aEnvVar);
557
  const char16_t* value = reinterpret_cast<const char16_t*>(::_wgetenv(envvar));
558
#define ENVVAR_PRINTF "%S"
559
#else
560
  const char* envvar = aEnvVar;
561
0
  const char* value = ::getenv(aEnvVar);
562
0
#define ENVVAR_PRINTF "%s"
563
0
#endif
564
0
565
0
  if (value) {
566
0
    nsTDependentString<EnvCharType> fname(value);
567
0
    if (fname.EqualsLiteral("1")) {
568
0
      *aResult = stdout;
569
0
      fprintf(stdout, "### " ENVVAR_PRINTF " defined -- logging %s to stdout\n",
570
0
              envvar, aMsg);
571
0
      return true;
572
0
    } else if (fname.EqualsLiteral("2")) {
573
0
      *aResult = stderr;
574
0
      fprintf(stdout, "### " ENVVAR_PRINTF " defined -- logging %s to stderr\n",
575
0
              envvar, aMsg);
576
0
      return true;
577
0
    } else {
578
0
      if (!XRE_IsParentProcess()) {
579
0
        bool hasLogExtension =
580
0
          fname.RFind(".log", true, -1, 4) == kNotFound ? false : true;
581
0
        if (hasLogExtension) {
582
0
          fname.Cut(fname.Length() - 4, 4);
583
0
        }
584
0
        fname.Append('_');
585
0
        const char* processType = XRE_ChildProcessTypeToString(XRE_GetProcessType());
586
0
        fname.AppendASCII(processType);
587
0
        fname.AppendLiteral("_pid");
588
0
        fname.AppendInt((uint32_t)getpid());
589
0
        if (hasLogExtension) {
590
0
          fname.AppendLiteral(".log");
591
0
        }
592
0
      }
593
#ifdef XP_WIN
594
      FILE* stream = ::_wfopen(fname.get(), L"wN");
595
      const wchar_t* fp = (const wchar_t*)fname.get();
596
#else
597
      FILE* stream = ::fopen(fname.get(), "w");
598
0
      const char* fp = fname.get();
599
0
#endif
600
0
      if (stream) {
601
0
        MozillaRegisterDebugFD(fileno(stream));
602
0
        *aResult = stream;
603
0
        fprintf(stderr, "### " ENVVAR_PRINTF " defined -- logging %s to " ENVVAR_PRINTF "\n",
604
0
                envvar, aMsg, fp);
605
0
      } else {
606
0
        fprintf(stderr, "### " ENVVAR_PRINTF " defined -- unable to log %s to " ENVVAR_PRINTF "\n",
607
0
                envvar, aMsg, fp);
608
0
        MOZ_ASSERT(false, "Tried and failed to create an XPCOM log");
609
0
      }
610
0
#undef ENVVAR_PRINTF
611
0
      return stream != nullptr;
612
0
    }
613
0
  }
614
0
  return false;
615
0
}
616
617
618
static void
619
maybeUnregisterAndCloseFile(FILE*& aFile)
620
0
{
621
0
  if (!aFile) {
622
0
    return;
623
0
  }
624
0
625
0
  MozillaUnRegisterDebugFILE(aFile);
626
0
  fclose(aFile);
627
0
  aFile = nullptr;
628
0
}
629
630
631
static void
632
InitTraceLog()
633
0
{
634
#ifdef XP_WIN
635
#define ENVVAR(x) u"" x
636
#else
637
0
#define ENVVAR(x) x
638
0
#endif
639
0
640
0
  if (gInitialized) {
641
0
    return;
642
0
  }
643
0
  gInitialized = true;
644
0
645
0
  // Don't trace refcounts while recording or replaying, these are not
646
0
  // required to match up between the two executions.
647
0
  if (mozilla::recordreplay::IsRecordingOrReplaying()) {
648
0
    return;
649
0
  }
650
0
651
0
  bool defined = InitLog(ENVVAR("XPCOM_MEM_BLOAT_LOG"), "bloat/leaks", &gBloatLog);
652
0
  if (!defined) {
653
0
    gLogLeaksOnly = InitLog(ENVVAR("XPCOM_MEM_LEAK_LOG"), "leaks", &gBloatLog);
654
0
  }
655
0
  if (defined || gLogLeaksOnly) {
656
0
    RecreateBloatView();
657
0
    if (!gBloatView) {
658
0
      NS_WARNING("out of memory");
659
0
      maybeUnregisterAndCloseFile(gBloatLog);
660
0
      gLogLeaksOnly = false;
661
0
    }
662
0
  }
663
0
664
0
  InitLog(ENVVAR("XPCOM_MEM_REFCNT_LOG"), "refcounts", &gRefcntsLog);
665
0
666
0
  InitLog(ENVVAR("XPCOM_MEM_ALLOC_LOG"), "new/delete", &gAllocLog);
667
0
668
0
  const char* classes = getenv("XPCOM_MEM_LOG_CLASSES");
669
0
670
0
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
671
0
  if (classes) {
672
0
    InitLog(ENVVAR("XPCOM_MEM_COMPTR_LOG"), "nsCOMPtr", &gCOMPtrLog);
673
0
  } else {
674
0
    if (getenv("XPCOM_MEM_COMPTR_LOG")) {
675
0
      fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but XPCOM_MEM_LOG_CLASSES is not defined\n");
676
0
    }
677
0
  }
678
#else
679
  const char* comptr_log = getenv("XPCOM_MEM_COMPTR_LOG");
680
  if (comptr_log) {
681
    fprintf(stdout, "### XPCOM_MEM_COMPTR_LOG defined -- but it will not work without dynamic_cast\n");
682
  }
683
#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
684
685
0
#undef ENVVAR
686
0
687
0
  if (classes) {
688
0
    // if XPCOM_MEM_LOG_CLASSES was set to some value, the value is interpreted
689
0
    // as a list of class names to track
690
0
    gTypesToLog = new CharPtrSet(256);
691
0
692
0
    fprintf(stdout, "### XPCOM_MEM_LOG_CLASSES defined -- only logging these classes: ");
693
0
    const char* cp = classes;
694
0
    for (;;) {
695
0
      char* cm = (char*)strchr(cp, ',');
696
0
      if (cm) {
697
0
        *cm = '\0';
698
0
      }
699
0
      gTypesToLog->PutEntry(cp);
700
0
      fprintf(stdout, "%s ", cp);
701
0
      if (!cm) {
702
0
        break;
703
0
      }
704
0
      *cm = ',';
705
0
      cp = cm + 1;
706
0
    }
707
0
    fprintf(stdout, "\n");
708
0
709
0
    gSerialNumbers = new SerialHash(256);
710
0
  }
711
0
712
0
  const char* objects = getenv("XPCOM_MEM_LOG_OBJECTS");
713
0
  if (objects) {
714
0
    gObjectsToLog = new IntPtrSet(256);
715
0
716
0
    if (!(gRefcntsLog || gAllocLog || gCOMPtrLog)) {
717
0
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- but none of XPCOM_MEM_(REFCNT|ALLOC|COMPTR)_LOG is defined\n");
718
0
    } else {
719
0
      fprintf(stdout, "### XPCOM_MEM_LOG_OBJECTS defined -- only logging these objects: ");
720
0
      const char* cp = objects;
721
0
      for (;;) {
722
0
        char* cm = (char*)strchr(cp, ',');
723
0
        if (cm) {
724
0
          *cm = '\0';
725
0
        }
726
0
        intptr_t top = 0;
727
0
        intptr_t bottom = 0;
728
0
        while (*cp) {
729
0
          if (*cp == '-') {
730
0
            bottom = top;
731
0
            top = 0;
732
0
            ++cp;
733
0
          }
734
0
          top *= 10;
735
0
          top += *cp - '0';
736
0
          ++cp;
737
0
        }
738
0
        if (!bottom) {
739
0
          bottom = top;
740
0
        }
741
0
        for (intptr_t serialno = bottom; serialno <= top; serialno++) {
742
0
          gObjectsToLog->PutEntry(serialno);
743
0
          fprintf(stdout, "%" PRIdPTR " ", serialno);
744
0
        }
745
0
        if (!cm) {
746
0
          break;
747
0
        }
748
0
        *cm = ',';
749
0
        cp = cm + 1;
750
0
      }
751
0
      fprintf(stdout, "\n");
752
0
    }
753
0
  }
754
0
755
0
  if (getenv("XPCOM_MEM_LOG_JS_STACK")) {
756
0
    fprintf(stdout, "### XPCOM_MEM_LOG_JS_STACK defined\n");
757
0
    gLogJSStacks = true;
758
0
  }
759
0
760
0
  if (gBloatLog) {
761
0
    gLogging = OnlyBloatLogging;
762
0
  }
763
0
764
0
  if (gRefcntsLog || gAllocLog || gCOMPtrLog) {
765
0
    gLogging = FullLogging;
766
0
  }
767
0
}
768
769
770
extern "C" {
771
772
static void
773
EnsureWrite(FILE* aStream, const char* aBuf, size_t aLen)
774
0
{
775
#ifdef XP_WIN
776
  int fd = _fileno(aStream);
777
#else
778
  int fd = fileno(aStream);
779
0
#endif
780
0
  while (aLen > 0) {
781
#ifdef XP_WIN
782
    auto written = _write(fd, aBuf, aLen);
783
#else
784
    auto written = write(fd, aBuf, aLen);
785
0
#endif
786
0
    if (written <= 0 || size_t(written) > aLen) {
787
0
      break;
788
0
    }
789
0
    aBuf += written;
790
0
    aLen -= written;
791
0
  }
792
0
}
793
794
static void
795
PrintStackFrame(uint32_t aFrameNumber, void* aPC, void* aSP, void* aClosure)
796
0
{
797
0
  FILE* stream = (FILE*)aClosure;
798
0
  MozCodeAddressDetails details;
799
0
  static const size_t buflen = 1024;
800
0
  char buf[buflen + 1];  // 1 for trailing '\n'
801
0
802
0
  MozDescribeCodeAddress(aPC, &details);
803
0
  MozFormatCodeAddressDetails(buf, buflen, aFrameNumber, aPC, &details);
804
0
  size_t len = std::min(strlen(buf), buflen + 1 - 2);
805
0
  buf[len++] = '\n';
806
0
  buf[len] = '\0';
807
0
  fflush(stream);
808
0
  EnsureWrite(stream, buf, len);
809
0
}
810
811
static void
812
PrintStackFrameCached(uint32_t aFrameNumber, void* aPC, void* aSP,
813
                      void* aClosure)
814
0
{
815
0
  auto stream = static_cast<FILE*>(aClosure);
816
0
  static const size_t buflen = 1024;
817
0
  char buf[buflen + 5] = "    ";  // 5 for leading "    " and trailing '\n'
818
0
  gCodeAddressService->GetLocation(aFrameNumber, aPC, buf + 4, buflen);
819
0
  size_t len = std::min(strlen(buf), buflen + 5 - 2);
820
0
  buf[len++] = '\n';
821
0
  buf[len] = '\0';
822
0
  fflush(stream);
823
0
  EnsureWrite(stream, buf, len);
824
0
}
825
826
static void
827
RecordStackFrame(uint32_t /*aFrameNumber*/, void* aPC, void* /*aSP*/,
828
                 void* aClosure)
829
0
{
830
0
  auto locations = static_cast<std::vector<void*>*>(aClosure);
831
0
  locations->push_back(aPC);
832
0
}
833
834
}
835
836
void
837
nsTraceRefcnt::WalkTheStack(FILE* aStream)
838
0
{
839
0
  MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0, aStream);
840
0
}
841
842
/**
843
 * This is a variant of |WalkTheStack| that uses |CodeAddressService| to cache
844
 * the results of |NS_DescribeCodeAddress|. If |WalkTheStackCached| is being
845
 * called frequently, it will be a few orders of magnitude faster than
846
 * |WalkTheStack|. However, the cache uses a lot of memory, which can cause
847
 * OOM crashes. Therefore, this should only be used for things like refcount
848
 * logging which walk the stack extremely frequently.
849
 */
850
static void
851
WalkTheStackCached(FILE* aStream)
852
0
{
853
0
  if (!gCodeAddressService) {
854
0
    gCodeAddressService = new WalkTheStackCodeAddressService();
855
0
  }
856
0
  MozStackWalk(PrintStackFrameCached, /* skipFrames */ 2, /* maxFrames */ 0,
857
0
               aStream);
858
0
}
859
860
static void
861
WalkTheStackSavingLocations(std::vector<void*>& aLocations)
862
0
{
863
0
  if (!gCodeAddressService) {
864
0
    gCodeAddressService = new WalkTheStackCodeAddressService();
865
0
  }
866
0
  static const int kFramesToSkip =
867
0
    0 +                         // this frame gets inlined
868
0
    1 +                         // GetSerialNumber
869
0
    1;                          // NS_LogCtor
870
0
  MozStackWalk(RecordStackFrame, kFramesToSkip, /* maxFrames */ 0, &aLocations);
871
0
}
872
873
//----------------------------------------------------------------------
874
875
EXPORT_XPCOM_API(void)
876
NS_LogInit()
877
9
{
878
9
  NS_SetMainThread();
879
9
880
9
  // FIXME: This is called multiple times, we should probably not allow that.
881
9
  if (++gInitCount) {
882
9
    nsTraceRefcnt::SetActivityIsLegal(true);
883
9
  }
884
9
}
885
886
EXPORT_XPCOM_API(void)
887
NS_LogTerm()
888
0
{
889
0
  mozilla::LogTerm();
890
0
}
891
892
#ifdef MOZ_DMD
893
// If MOZ_DMD_SHUTDOWN_LOG is set, dump a DMD report to a file.
894
// The value of this environment variable is used as the prefix
895
// of the file name, so you probably want something like "/tmp/".
896
// By default, this is run in all processes, but you can record a
897
// log only for a specific process type by setting MOZ_DMD_LOG_PROCESS
898
// to the process type you want to log, such as "default" or "tab".
899
// This method can't use the higher level XPCOM file utilities
900
// because it is run very late in shutdown to avoid recording
901
// information about refcount logging entries.
902
static void
903
LogDMDFile()
904
{
905
  const char* dmdFilePrefix = PR_GetEnv("MOZ_DMD_SHUTDOWN_LOG");
906
  if (!dmdFilePrefix) {
907
    return;
908
  }
909
910
  const char* logProcessEnv = PR_GetEnv("MOZ_DMD_LOG_PROCESS");
911
  if (logProcessEnv && !!strcmp(logProcessEnv, XRE_ChildProcessTypeToString(XRE_GetProcessType()))) {
912
    return;
913
  }
914
915
  nsPrintfCString fileName("%sdmd-%d.log.gz", dmdFilePrefix, base::GetCurrentProcId());
916
  FILE* logFile = fopen(fileName.get(), "w");
917
  if (NS_WARN_IF(!logFile)) {
918
    return;
919
  }
920
921
  nsMemoryInfoDumper::DumpDMDToFile(logFile);
922
}
923
#endif // MOZ_DMD
924
925
namespace mozilla {
926
void
927
LogTerm()
928
0
{
929
0
  NS_ASSERTION(gInitCount > 0,
930
0
               "NS_LogTerm without matching NS_LogInit");
931
0
932
0
  if (--gInitCount == 0) {
933
#ifdef DEBUG
934
    /* FIXME bug 491977: This is only going to operate on the
935
     * BlockingResourceBase which is compiled into
936
     * libxul/libxpcom_core.so. Anyone using external linkage will
937
     * have their own copy of BlockingResourceBase statics which will
938
     * not be freed by this method.
939
     *
940
     * It sounds like what we really want is to be able to register a
941
     * callback function to call at XPCOM shutdown.  Note that with
942
     * this solution, however, we need to guarantee that
943
     * BlockingResourceBase::Shutdown() runs after all other shutdown
944
     * functions.
945
     */
946
    BlockingResourceBase::Shutdown();
947
#endif
948
949
0
    if (gInitialized) {
950
0
      nsTraceRefcnt::DumpStatistics();
951
0
      nsTraceRefcnt::ResetStatistics();
952
0
    }
953
0
    nsTraceRefcnt::Shutdown();
954
0
    nsTraceRefcnt::SetActivityIsLegal(false);
955
0
    gActivityTLS = BAD_TLS_INDEX;
956
0
957
#ifdef MOZ_DMD
958
    LogDMDFile();
959
#endif
960
  }
961
0
}
962
963
} // namespace mozilla
964
965
EXPORT_XPCOM_API(void)
966
NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt,
967
             const char* aClass, uint32_t aClassSize)
968
0
{
969
0
  ASSERT_ACTIVITY_IS_LEGAL;
970
0
  if (!gInitialized) {
971
0
    InitTraceLog();
972
0
  }
973
0
  if (gLogging == NoLogging) {
974
0
    return;
975
0
  }
976
0
  if (aRefcnt == 1 || gLogging == FullLogging) {
977
0
    AutoTraceLogLock lock;
978
0
979
0
    if (aRefcnt == 1 && gBloatLog) {
980
0
      BloatEntry* entry = GetBloatEntry(aClass, aClassSize);
981
0
      if (entry) {
982
0
        entry->Ctor();
983
0
      }
984
0
    }
985
0
986
0
    // Here's the case where MOZ_COUNT_CTOR was not used,
987
0
    // yet we still want to see creation information:
988
0
989
0
    bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aClass));
990
0
    intptr_t serialno = 0;
991
0
    if (gSerialNumbers && loggingThisType) {
992
0
      serialno = GetSerialNumber(aPtr, aRefcnt == 1);
993
0
      MOZ_ASSERT(serialno != 0,
994
0
                 "Serial number requested for unrecognized pointer!  "
995
0
                 "Are you memmoving a refcounted object?");
996
0
      auto record = gSerialNumbers->Get(aPtr);
997
0
      if (record) {
998
0
        ++record->refCount;
999
0
      }
1000
0
    }
1001
0
1002
0
    bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1003
0
    if (aRefcnt == 1 && gAllocLog && loggingThisType && loggingThisObject) {
1004
0
      fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Create [thread %p]\n", aClass, aPtr, serialno, PR_GetCurrentThread());
1005
0
      WalkTheStackCached(gAllocLog);
1006
0
    }
1007
0
1008
0
    if (gRefcntsLog && loggingThisType && loggingThisObject) {
1009
0
      // Can't use MOZ_LOG(), b/c it truncates the line
1010
0
      fprintf(gRefcntsLog, "\n<%s> %p %" PRIuPTR " AddRef %" PRIuPTR " [thread %p]\n",
1011
0
              aClass, aPtr, serialno, aRefcnt, PR_GetCurrentThread());
1012
0
      WalkTheStackCached(gRefcntsLog);
1013
0
      fflush(gRefcntsLog);
1014
0
    }
1015
0
  }
1016
0
}
1017
1018
EXPORT_XPCOM_API(void)
1019
NS_LogRelease(void* aPtr, nsrefcnt aRefcnt, const char* aClass)
1020
0
{
1021
0
  ASSERT_ACTIVITY_IS_LEGAL;
1022
0
  if (!gInitialized) {
1023
0
    InitTraceLog();
1024
0
  }
1025
0
  if (gLogging == NoLogging) {
1026
0
    return;
1027
0
  }
1028
0
  if (aRefcnt == 0 || gLogging == FullLogging) {
1029
0
    AutoTraceLogLock lock;
1030
0
1031
0
    if (aRefcnt == 0 && gBloatLog) {
1032
0
      BloatEntry* entry = GetBloatEntry(aClass, 0);
1033
0
      if (entry) {
1034
0
        entry->Dtor();
1035
0
      }
1036
0
    }
1037
0
1038
0
    bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aClass));
1039
0
    intptr_t serialno = 0;
1040
0
    if (gSerialNumbers && loggingThisType) {
1041
0
      serialno = GetSerialNumber(aPtr, false);
1042
0
      MOZ_ASSERT(serialno != 0,
1043
0
                 "Serial number requested for unrecognized pointer!  "
1044
0
                 "Are you memmoving a refcounted object?");
1045
0
      auto record = gSerialNumbers->Get(aPtr);
1046
0
      if (record) {
1047
0
        --record->refCount;
1048
0
      }
1049
0
    }
1050
0
1051
0
    bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1052
0
    if (gRefcntsLog && loggingThisType && loggingThisObject) {
1053
0
      // Can't use MOZ_LOG(), b/c it truncates the line
1054
0
      fprintf(gRefcntsLog,
1055
0
              "\n<%s> %p %" PRIuPTR " Release %" PRIuPTR " [thread %p]\n",
1056
0
              aClass, aPtr, serialno, aRefcnt, PR_GetCurrentThread());
1057
0
      WalkTheStackCached(gRefcntsLog);
1058
0
      fflush(gRefcntsLog);
1059
0
    }
1060
0
1061
0
    // Here's the case where MOZ_COUNT_DTOR was not used,
1062
0
    // yet we still want to see deletion information:
1063
0
1064
0
    if (aRefcnt == 0 && gAllocLog && loggingThisType && loggingThisObject) {
1065
0
      fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Destroy [thread %p]\n", aClass, aPtr, serialno, PR_GetCurrentThread());
1066
0
      WalkTheStackCached(gAllocLog);
1067
0
    }
1068
0
1069
0
    if (aRefcnt == 0 && gSerialNumbers && loggingThisType) {
1070
0
      RecycleSerialNumberPtr(aPtr);
1071
0
    }
1072
0
  }
1073
0
}
1074
1075
EXPORT_XPCOM_API(void)
1076
NS_LogCtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
1077
0
{
1078
0
  ASSERT_ACTIVITY_IS_LEGAL;
1079
0
  if (!gInitialized) {
1080
0
    InitTraceLog();
1081
0
  }
1082
0
1083
0
  if (gLogging == NoLogging) {
1084
0
    return;
1085
0
  }
1086
0
1087
0
  AutoTraceLogLock lock;
1088
0
1089
0
  if (gBloatLog) {
1090
0
    BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1091
0
    if (entry) {
1092
0
      entry->Ctor();
1093
0
    }
1094
0
  }
1095
0
1096
0
  bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aType));
1097
0
  intptr_t serialno = 0;
1098
0
  if (gSerialNumbers && loggingThisType) {
1099
0
    serialno = GetSerialNumber(aPtr, true);
1100
0
    MOZ_ASSERT(serialno != 0, "GetSerialNumber should never return 0 when passed true");
1101
0
  }
1102
0
1103
0
  bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1104
0
  if (gAllocLog && loggingThisType && loggingThisObject) {
1105
0
    fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Ctor (%d)\n",
1106
0
            aType, aPtr, serialno, aInstanceSize);
1107
0
    WalkTheStackCached(gAllocLog);
1108
0
  }
1109
0
}
1110
1111
1112
EXPORT_XPCOM_API(void)
1113
NS_LogDtor(void* aPtr, const char* aType, uint32_t aInstanceSize)
1114
0
{
1115
0
  ASSERT_ACTIVITY_IS_LEGAL;
1116
0
  if (!gInitialized) {
1117
0
    InitTraceLog();
1118
0
  }
1119
0
1120
0
  if (gLogging == NoLogging) {
1121
0
    return;
1122
0
  }
1123
0
1124
0
  AutoTraceLogLock lock;
1125
0
1126
0
  if (gBloatLog) {
1127
0
    BloatEntry* entry = GetBloatEntry(aType, aInstanceSize);
1128
0
    if (entry) {
1129
0
      entry->Dtor();
1130
0
    }
1131
0
  }
1132
0
1133
0
  bool loggingThisType = (!gTypesToLog || gTypesToLog->Contains(aType));
1134
0
  intptr_t serialno = 0;
1135
0
  if (gSerialNumbers && loggingThisType) {
1136
0
    serialno = GetSerialNumber(aPtr, false);
1137
0
    MOZ_ASSERT(serialno != 0,
1138
0
               "Serial number requested for unrecognized pointer!  "
1139
0
               "Are you memmoving a MOZ_COUNT_CTOR-tracked object?");
1140
0
    RecycleSerialNumberPtr(aPtr);
1141
0
  }
1142
0
1143
0
  bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1144
0
1145
0
  // (If we're on a losing architecture, don't do this because we'll be
1146
0
  // using LogDeleteXPCOM instead to get file and line numbers.)
1147
0
  if (gAllocLog && loggingThisType && loggingThisObject) {
1148
0
    fprintf(gAllocLog, "\n<%s> %p %" PRIdPTR " Dtor (%d)\n",
1149
0
            aType, aPtr, serialno, aInstanceSize);
1150
0
    WalkTheStackCached(gAllocLog);
1151
0
  }
1152
0
}
1153
1154
1155
EXPORT_XPCOM_API(void)
1156
NS_LogCOMPtrAddRef(void* aCOMPtr, nsISupports* aObject)
1157
0
{
1158
0
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
1159
0
  // Get the most-derived object.
1160
0
  void* object = dynamic_cast<void*>(aObject);
1161
0
1162
0
  // This is a very indirect way of finding out what the class is
1163
0
  // of the object being logged.  If we're logging a specific type,
1164
0
  // then
1165
0
  if (!gTypesToLog || !gSerialNumbers) {
1166
0
    return;
1167
0
  }
1168
0
  if (!gInitialized) {
1169
0
    InitTraceLog();
1170
0
  }
1171
0
  if (gLogging == FullLogging) {
1172
0
    AutoTraceLogLock lock;
1173
0
1174
0
    intptr_t serialno = GetSerialNumber(object, false);
1175
0
    if (serialno == 0) {
1176
0
      return;
1177
0
    }
1178
0
1179
0
    auto record = gSerialNumbers->Get(object);
1180
0
    int32_t count = record ? ++record->COMPtrCount : -1;
1181
0
    bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1182
0
1183
0
    if (gCOMPtrLog && loggingThisObject) {
1184
0
      fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrAddRef %d %p\n",
1185
0
              object, serialno, count, aCOMPtr);
1186
0
      WalkTheStackCached(gCOMPtrLog);
1187
0
    }
1188
0
  }
1189
0
#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
1190
0
}
1191
1192
1193
EXPORT_XPCOM_API(void)
1194
NS_LogCOMPtrRelease(void* aCOMPtr, nsISupports* aObject)
1195
0
{
1196
0
#ifdef HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
1197
0
  // Get the most-derived object.
1198
0
  void* object = dynamic_cast<void*>(aObject);
1199
0
1200
0
  // This is a very indirect way of finding out what the class is
1201
0
  // of the object being logged.  If we're logging a specific type,
1202
0
  // then
1203
0
  if (!gTypesToLog || !gSerialNumbers) {
1204
0
    return;
1205
0
  }
1206
0
  if (!gInitialized) {
1207
0
    InitTraceLog();
1208
0
  }
1209
0
  if (gLogging == FullLogging) {
1210
0
    AutoTraceLogLock lock;
1211
0
1212
0
    intptr_t serialno = GetSerialNumber(object, false);
1213
0
    if (serialno == 0) {
1214
0
      return;
1215
0
    }
1216
0
1217
0
    auto record = gSerialNumbers->Get(object);
1218
0
    int32_t count = record ? --record->COMPtrCount : -1;
1219
0
    bool loggingThisObject = (!gObjectsToLog || LogThisObj(serialno));
1220
0
1221
0
    if (gCOMPtrLog && loggingThisObject) {
1222
0
      fprintf(gCOMPtrLog, "\n<?> %p %" PRIdPTR " nsCOMPtrRelease %d %p\n",
1223
0
              object, serialno, count, aCOMPtr);
1224
0
      WalkTheStackCached(gCOMPtrLog);
1225
0
    }
1226
0
  }
1227
0
#endif // HAVE_CPP_DYNAMIC_CAST_TO_VOID_PTR
1228
0
}
1229
1230
void
1231
nsTraceRefcnt::Shutdown()
1232
0
{
1233
0
  gCodeAddressService = nullptr;
1234
0
  gBloatView = nullptr;
1235
0
  gTypesToLog = nullptr;
1236
0
  gObjectsToLog = nullptr;
1237
0
  gSerialNumbers = nullptr;
1238
0
  maybeUnregisterAndCloseFile(gBloatLog);
1239
0
  maybeUnregisterAndCloseFile(gRefcntsLog);
1240
0
  maybeUnregisterAndCloseFile(gAllocLog);
1241
0
  maybeUnregisterAndCloseFile(gCOMPtrLog);
1242
0
}
1243
1244
void
1245
nsTraceRefcnt::SetActivityIsLegal(bool aLegal)
1246
9
{
1247
9
  if (gActivityTLS == BAD_TLS_INDEX) {
1248
3
    PR_NewThreadPrivateIndex(&gActivityTLS, nullptr);
1249
3
  }
1250
9
1251
9
  PR_SetThreadPrivate(gActivityTLS, reinterpret_cast<void*>(!aLegal));
1252
9
}