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

Generated by: LCOV version 1.13