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