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