From aeaa95b765b59d2f26ce2e78e6cd9f4d9f2b951a Mon Sep 17 00:00:00 2001 From: Joshua Cranmer Date: Mon, 1 Apr 2013 00:04:32 -0500 Subject: [PATCH] Bug 480843 - Implement a memory reporter for mailnews code, r=irving. Unfortunately, due to issues with our build system, this patch makes the "Dark Matter Detector" reports incorrect since it doesn't realize that our memory reporter is reporting memory. This should be fixed as part of the build system overhaul in bug 846540, however. --- db/mork/public/mdb.h | 3 +- db/mork/src/morkZone.h | 2 + db/mork/src/orkinHeap.cpp | 91 +++-------------- db/mork/src/orkinHeap.h | 24 +---- mailnews/db/msgdb/public/nsDBFolderInfo.h | 10 +- mailnews/db/msgdb/public/nsMsgDatabase.h | 18 ++++ mailnews/db/msgdb/public/nsMsgHdr.h | 10 ++ mailnews/db/msgdb/src/nsMsgDatabase.cpp | 113 ++++++++++++++++++++++ 8 files changed, 174 insertions(+), 97 deletions(-) diff --git a/db/mork/public/mdb.h b/db/mork/public/mdb.h index ca7c7291d2..1156d7f341 100644 --- a/db/mork/public/mdb.h +++ b/db/mork/public/mdb.h @@ -460,7 +460,8 @@ public: NS_IMETHOD Free(nsIMdbEnv* ev, // free block from Alloc or Resize() void* ioBlock) = 0; // block to be destroyed/deallocated - + + virtual size_t GetUsedSize() = 0; // } ===== end nsIMdbHeap methods ===== }; diff --git a/db/mork/src/morkZone.h b/db/mork/src/morkZone.h index 1fa2402cf0..1e46168e2a 100644 --- a/db/mork/src/morkZone.h +++ b/db/mork/src/morkZone.h @@ -279,6 +279,8 @@ public: NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc() void* inBlock); + + virtual size_t GetUsedSize() { return mZone_Heap->GetUsedSize(); } // } ===== end nsIMdbHeap methods ===== // { ===== begin morkNode interface ===== diff --git a/db/mork/src/orkinHeap.cpp b/db/mork/src/orkinHeap.cpp index fdf75d200e..ac92894e62 100644 --- a/db/mork/src/orkinHeap.cpp +++ b/db/mork/src/orkinHeap.cpp @@ -19,22 +19,15 @@ #include "morkEnv.h" #endif +#include "nsIMemoryReporter.h" + #include //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 orkinHeap::orkinHeap() // does nothing -#ifdef MORK_DEBUG_HEAP_STATS - : sHeap_AllocCount( 0 ) - , sHeap_FreeCount( 0 ) - , sHeap_BlockCount( 0 ) - - , sHeap_BlockVolume( 0 ) - , sHeap_HighWaterVolume( 0 ) - , sHeap_HighWaterTenKilo( 0 ) - , sHeap_HighWaterHundredKilo( 0 ) -#endif /*MORK_DEBUG_HEAP_STATS*/ + : mUsedSize(0) { } @@ -43,101 +36,49 @@ orkinHeap::~orkinHeap() // does nothing { } +NS_MEMORY_REPORTER_MALLOC_SIZEOF_ON_ALLOC_FUN(MorkSizeOfOnAlloc) +NS_MEMORY_REPORTER_MALLOC_SIZEOF_ON_FREE_FUN(MorkSizeOfOnFree) + // { ===== begin nsIMdbHeap methods ===== /*virtual*/ mdb_err orkinHeap::Alloc(nsIMdbEnv* mev, // allocate a piece of memory - mdb_size inSize, // requested size of new memory block + mdb_size inSize, // requested size of new memory block void** outBlock) // memory block of inSize bytes, or nil { -#ifdef MORK_DEBUG_HEAP_STATS - mdb_size realSize = inSize; - inSize += 12; // sizeof(mork_u4) * 3 - ++sHeap_AllocCount; -#endif /*MORK_DEBUG_HEAP_STATS*/ MORK_USED_1(mev); mdb_err outErr = NS_OK; void* block = malloc(inSize); if ( !block ) outErr = morkEnv_kOutOfMemoryError; -#ifdef MORK_DEBUG_HEAP_STATS else - { - printf("%lx allocating %d\n", this, realSize); - mork_u4* array = (mork_u4*) block; - *array++ = (mork_u4) this; - *array++ = realSize; - *array++ = orkinHeap_kTag; - block = array; - ++sHeap_BlockCount; - mork_num blockVol = sHeap_BlockVolume + realSize; - sHeap_BlockVolume = blockVol; - if ( blockVol > sHeap_HighWaterVolume ) - { - sHeap_HighWaterVolume = blockVol; - - mork_num tenKiloVol = blockVol / (10 * 1024); - if ( tenKiloVol > sHeap_HighWaterTenKilo ) - { - sHeap_HighWaterTenKilo = tenKiloVol; - - mork_num hundredKiloVol = blockVol / (100 * 1024); - if ( hundredKiloVol > sHeap_HighWaterHundredKilo ) - sHeap_HighWaterHundredKilo = hundredKiloVol; - } - } - } -#endif /*MORK_DEBUG_HEAP_STATS*/ - + mUsedSize += MorkSizeOfOnAlloc(block); + MORK_ASSERT(outBlock); if ( outBlock ) *outBlock = block; return outErr; } - + /*virtual*/ mdb_err orkinHeap::Free(nsIMdbEnv* mev, // free block allocated earlier by Alloc() void* inBlock) { -#ifdef MORK_DEBUG_HEAP_STATS - ++sHeap_FreeCount; -#endif /*MORK_DEBUG_HEAP_STATS*/ - MORK_USED_1(mev); MORK_ASSERT(inBlock); if ( inBlock ) { -#ifdef MORK_DEBUG_HEAP_STATS - morkEnv* ev = 0; //morkEnv::FromMdbEnv(mev); - mork_u4* array = (mork_u4*) inBlock; - if ( *--array != orkinHeap_kTag ) - { - if ( ev ) - ev->NewWarning("heap block tag not hEaP"); - } - mork_u4 realSize = *--array; - inBlock = --array; // skip over heap ptr too. - - printf("%lx freeing %d\n", this, realSize); - if ( sHeap_BlockCount ) - --sHeap_BlockCount; - else if ( ev ) - ev->NewWarning("sHeap_BlockCount underflow"); - - if ( sHeap_BlockVolume >= realSize ) - sHeap_BlockVolume -= realSize; - else if ( ev ) - { - sHeap_BlockVolume = 0; - ev->NewWarning("sHeap_BlockVolume underflow"); - } -#endif /*MORK_DEBUG_HEAP_STATS*/ - + mUsedSize -= MorkSizeOfOnFree(inBlock); free(inBlock); } return NS_OK; } +size_t +orkinHeap::GetUsedSize() +{ + return mUsedSize; +} // } ===== end nsIMdbHeap methods ===== //3456789_123456789_123456789_123456789_123456789_123456789_123456789_123456789 diff --git a/db/mork/src/orkinHeap.h b/db/mork/src/orkinHeap.h index a66979b1df..a3c14d2954 100644 --- a/db/mork/src/orkinHeap.h +++ b/db/mork/src/orkinHeap.h @@ -21,27 +21,9 @@ /*| orkinHeap: |*/ class orkinHeap : public nsIMdbHeap { // - -#ifdef MORK_DEBUG_HEAP_STATS protected: - mork_num sHeap_AllocCount; // number of times Alloc() is called - mork_num sHeap_FreeCount; // number of times Free() is called - mork_num sHeap_BlockCount; // number of outstanding blocks - - mork_num sHeap_BlockVolume; // sum of sizes for all outstanding blocks - mork_num sHeap_HighWaterVolume; // largest value of sHeap_BlockVolume seen - mork_num sHeap_HighWaterTenKilo; // HighWaterVolume in 10K granularity - mork_num sHeap_HighWaterHundredKilo; // HighWaterVolume in 100K granularity - -public: // getters - mork_num HeapAllocCount() const { return sHeap_AllocCount; } - mork_num HeapFreeCount() const { return sHeap_FreeCount; } - mork_num HeapBlockCount() const { return sHeap_AllocCount - sHeap_FreeCount; } - - mork_num HeapBlockVolume() const { return sHeap_BlockVolume; } - mork_num HeapHighWaterVolume() const { return sHeap_HighWaterVolume; } -#endif /*MORK_DEBUG_HEAP_STATS*/ - + size_t mUsedSize; + public: orkinHeap(); // does nothing virtual ~orkinHeap(); // does nothing @@ -59,6 +41,8 @@ public: NS_IMETHOD Free(nsIMdbEnv* ev, // free block allocated earlier by Alloc() void* inBlock); + + virtual size_t GetUsedSize(); // } ===== end nsIMdbHeap methods ===== }; diff --git a/mailnews/db/msgdb/public/nsDBFolderInfo.h b/mailnews/db/msgdb/public/nsDBFolderInfo.h index 348c04eb38..7fec359845 100644 --- a/mailnews/db/msgdb/public/nsDBFolderInfo.h +++ b/mailnews/db/msgdb/public/nsDBFolderInfo.h @@ -59,7 +59,15 @@ public: uint64_t *propertyValue); nsTArray m_lateredKeys; // list of latered messages - + + virtual size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const + { + return m_lateredKeys.SizeOfExcludingThis(aMallocSizeOf); + } + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const + { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); + } protected: // initialize from appropriate table and row in existing db. diff --git a/mailnews/db/msgdb/public/nsMsgDatabase.h b/mailnews/db/msgdb/public/nsMsgDatabase.h index e0c8140461..5ff08007e8 100644 --- a/mailnews/db/msgdb/public/nsMsgDatabase.h +++ b/mailnews/db/msgdb/public/nsMsgDatabase.h @@ -98,6 +98,12 @@ protected: }; +namespace mozilla { +namespace mailnews { +class MsgDBReporter; +} +} + class nsMsgDatabase : public nsIMsgDatabase { public: @@ -407,8 +413,20 @@ protected: // not-reference holding array of enumerators we've handed out. // If a db goes away, it will clean up the outstanding enumerators. nsTArray m_enumerators; + + // Memory reporter details +public: + static size_t HeaderHashSizeOf(PLDHashEntryHdr *hdr, + nsMallocSizeOfFun aMallocSizeOf, + void *arg); + virtual size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const + { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); + } private: uint32_t m_cacheSize; + nsRefPtr mMemReporter; }; class nsMsgRetentionSettings : public nsIMsgRetentionSettings diff --git a/mailnews/db/msgdb/public/nsMsgHdr.h b/mailnews/db/msgdb/public/nsMsgHdr.h index d98f5cd1e3..8b3cbd51b5 100644 --- a/mailnews/db/msgdb/public/nsMsgHdr.h +++ b/mailnews/db/msgdb/public/nsMsgHdr.h @@ -40,6 +40,16 @@ public: bool IsAncestorOf(nsIMsgDBHdr *possibleChild); bool IsAncestorKilled(uint32_t ancestorsToCheck); void ReparentInThread(nsIMsgThread *thread); + + size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOfFun) const + { + return m_references.SizeOfExcludingThis(aMallocSizeOfFun); + } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOfFun) const + { + return aMallocSizeOfFun(this) + SizeOfExcludingThis(aMallocSizeOfFun); + } + protected: nsresult SetStringColumn(const char *str, mdb_token token); nsresult SetUInt32Column(uint32_t value, mdb_token token); diff --git a/mailnews/db/msgdb/src/nsMsgDatabase.cpp b/mailnews/db/msgdb/src/nsMsgDatabase.cpp index 91cee652f1..bf48d00e88 100644 --- a/mailnews/db/msgdb/src/nsMsgDatabase.cpp +++ b/mailnews/db/msgdb/src/nsMsgDatabase.cpp @@ -47,6 +47,7 @@ #include "nsIMsgPluggableStore.h" #include "nsAlgorithm.h" #include "nsArrayEnumerator.h" +#include "nsIMemoryReporter.h" #include #if defined(DEBUG_sspitzer_) || defined(DEBUG_seth_) @@ -996,6 +997,115 @@ void nsMsgDatabase::DumpCache() } } +// Memory Reporting implementations + +size_t nsMsgDatabase::HeaderHashSizeOf(PLDHashEntryHdr *hdr, + nsMallocSizeOfFun aMallocSizeOf, + void *arg) +{ + MsgHdrHashElement *entry = reinterpret_cast(hdr); + // Sigh, this is dangerous, but so long as this is a closed system, this is + // safe. + return static_cast(entry->mHdr)-> + SizeOfIncludingThis(aMallocSizeOf); +} + +size_t nsMsgDatabase::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t totalSize = 0; + if (m_dbFolderInfo) + totalSize += m_dbFolderInfo->SizeOfExcludingThis(aMallocSizeOf); + if (m_mdbEnv) + { + nsIMdbHeap *morkHeap = nullptr; + m_mdbEnv->GetHeap(&morkHeap); + if (morkHeap) + totalSize += morkHeap->GetUsedSize(); + } + totalSize += m_newSet.SizeOfExcludingThis(aMallocSizeOf); + totalSize += m_ChangeListeners.SizeOfExcludingThis(aMallocSizeOf); + totalSize += m_threads.SizeOfExcludingThis(aMallocSizeOf); + // We have two tables of header objects, but every header in m_cachedHeaders + // should be in m_headersInUse. + // double-counting... + size_t headerSize = 0; + if (m_headersInUse) + { + headerSize = PL_DHashTableSizeOfIncludingThis(m_headersInUse, + nsMsgDatabase::HeaderHashSizeOf, aMallocSizeOf); + } + totalSize += headerSize; + if (m_msgReferences) + totalSize += PL_DHashTableSizeOfIncludingThis(m_msgReferences, nullptr, + aMallocSizeOf); + return totalSize; +} + +namespace mozilla { +namespace mailnews { + +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(GetMallocSize) + +class MsgDBReporter MOZ_FINAL : public nsIMemoryReporter +{ + nsMsgDatabase *mDatabase; +public: + MsgDBReporter(nsMsgDatabase *db) : mDatabase(db) {} + + NS_DECL_ISUPPORTS + NS_IMETHOD GetProcess(nsACString &process) + { + process.Truncate(); + return NS_OK; + } + + NS_IMETHOD GetPath(nsACString &memoryPath) + { + memoryPath.AssignLiteral("explicit/maildb/database("); + nsCOMPtr folder; + mDatabase->GetFolder(getter_AddRefs(folder)); + if (folder) + { + nsAutoCString folderURL; + folder->GetFolderURL(folderURL); + folderURL.ReplaceChar('/', '\\'); + memoryPath += folderURL; + } else { + memoryPath.AppendLiteral("UNKNOWN-FOLDER"); + } + memoryPath.Append(')'); + return NS_OK; + } + + NS_IMETHOD GetKind(int *kind) + { + *kind = nsIMemoryReporter::KIND_HEAP; + return NS_OK; + } + + NS_IMETHOD GetUnits(int *units) + { + *units = nsIMemoryReporter::UNITS_BYTES; + return NS_OK; + } + + NS_IMETHOD GetAmount(int64_t *amount) + { + *amount = mDatabase->SizeOfIncludingThis(GetMallocSize); + return NS_OK; + } + + NS_IMETHOD GetDescription(nsACString &desc) + { + desc.AssignLiteral("Memory used for the folder database."); + return NS_OK; + } +}; + +NS_IMPL_ISUPPORTS1(MsgDBReporter, nsIMemoryReporter) +} +} + nsMsgDatabase::nsMsgDatabase() : m_dbFolderInfo(nullptr), m_nextPseudoMsgKey(kFirstPseudoKey), @@ -1042,10 +1152,13 @@ nsMsgDatabase::nsMsgDatabase() m_msgReferences(nullptr), m_cacheSize(kMaxHdrsInCache) { + mMemReporter = new mozilla::mailnews::MsgDBReporter(this); + NS_RegisterMemoryReporter(mMemReporter); } nsMsgDatabase::~nsMsgDatabase() { + NS_UnregisterMemoryReporter(mMemReporter); // Close(FALSE); // better have already been closed. ClearCachedObjects(true); ClearEnumerators();