LCOV - code coverage report
Current view: top level - xpcom/base - nsTraceRefcnt.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 41 534 7.7 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.13-14-ga5dd952