diff --git a/memory/build/replace_malloc_bridge.h b/memory/build/replace_malloc_bridge.h index fb45355d369389d6dbf98687c66def23b285ba91..20683c85df052258b27d9c73a7dc4f045e73434f 100644 --- a/memory/build/replace_malloc_bridge.h +++ b/memory/build/replace_malloc_bridge.h @@ -117,6 +117,16 @@ struct DMDFuncs; namespace phc { class AddrInfo; + +struct MemoryUsage { + // The amount of memory used for PHC metadata, eg information about each + // allocation including stacks. + size_t mMetadataBytes = 0; + + // The amount of memory lost due to rounding allocation sizes up to the + // nearest page. AKA internal fragmentation. + size_t mFragmentationBytes = 0; +}; } // namespace phc // Callbacks to register debug file handles for Poison IO interpose. @@ -126,11 +136,10 @@ struct DebugFdRegistry { virtual void UnRegisterHandle(intptr_t aFd); }; - } // namespace mozilla struct ReplaceMallocBridge { - ReplaceMallocBridge() : mVersion(4) {} + ReplaceMallocBridge() : mVersion(5) {} // This method was added in version 1 of the bridge. virtual mozilla::dmd::DMDFuncs* GetDMDFuncs() { return nullptr; } @@ -182,6 +191,10 @@ struct ReplaceMallocBridge { // This method was added in version 4 of the bridge. virtual bool IsPHCEnabledOnCurrentThread() { return false; } + // Return PHC memory usage information by filling in the supplied structure. + // This method was added in version 5 of the bridge. + virtual void PHCMemoryUsage(mozilla::phc::MemoryUsage& aMemoryUsage) {} + # ifndef REPLACE_MALLOC_IMPL // Returns the replace-malloc bridge if its version is at least the // requested one. @@ -249,6 +262,13 @@ struct ReplaceMalloc { auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 4); return singleton ? singleton->IsPHCEnabledOnCurrentThread() : false; } + + static void PHCMemoryUsage(mozilla::phc::MemoryUsage& aMemoryUsage) { + auto singleton = ReplaceMallocBridge::Get(/* minimumVersion */ 5); + if (singleton) { + singleton->PHCMemoryUsage(aMemoryUsage); + } + } }; # endif diff --git a/memory/replace/phc/PHC.cpp b/memory/replace/phc/PHC.cpp index 02bc0d3d398caede5a959c66bb7a33965b507f6f..cd0f6322bb98ab117659f9330c4e4524e2517dfd 100644 --- a/memory/replace/phc/PHC.cpp +++ b/memory/replace/phc/PHC.cpp @@ -656,6 +656,12 @@ class GMut { (kPageSize - 1)); } + // The internal fragmentation for this allocation. + size_t FragmentationBytes() const { + MOZ_ASSERT(kPageSize >= UsableSize()); + return mState == AllocPageState::InUse ? kPageSize - UsableSize() : 0; + } + // The allocation stack. // - NeverAllocated: Nothing. // - InUse | Freed: Some. @@ -719,6 +725,15 @@ class GMut { return page.UsableSize(); } + // The total fragmentation in PHC + size_t FragmentationBytes() const { + size_t sum = 0; + for (auto page : mAllocPages) { + sum += page.FragmentationBytes(); + } + return sum; + } + void SetPageInUse(GMutLock aLock, uintptr_t aIndex, const Maybe<arena_id_t>& aArenaId, uint8_t* aBaseAddr, const StackTrace& aAllocStack) { @@ -1389,6 +1404,11 @@ static size_t replace_malloc_usable_size(usable_ptr_t aPtr) { return gMut->PageUsableSize(lock, index); } +static size_t metadata_size() { + return sMallocTable.malloc_usable_size(gConst) + + sMallocTable.malloc_usable_size(gMut); +} + void replace_jemalloc_stats(jemalloc_stats_t* aStats, jemalloc_bin_stats_t* aBinStats) { sMallocTable.jemalloc_stats_internal(aStats, aBinStats); @@ -1419,10 +1439,11 @@ void replace_jemalloc_stats(jemalloc_stats_t* aStats, // aStats.page_cache and aStats.bin_unused are left unchanged because PHC // doesn't have anything corresponding to those. - // gConst and gMut are normal heap allocations, so they're measured by + // The metadata is stored in normal heap allocations, so they're measured by // mozjemalloc as `allocated`. Move them into `bookkeeping`. - size_t bookkeeping = sMallocTable.malloc_usable_size(gConst) + - sMallocTable.malloc_usable_size(gMut); + // They're also reported under explicit/heap-overhead/phc/fragmentation in + // about:memory. + size_t bookkeeping = metadata_size(); aStats->allocated -= bookkeeping; aStats->bookkeeping += bookkeeping; } @@ -1554,6 +1575,17 @@ class PHCBridge : public ReplaceMallocBridge { LOG("IsPHCEnabledOnCurrentThread: %zu\n", size_t(enabled)); return enabled; } + + virtual void PHCMemoryUsage( + mozilla::phc::MemoryUsage& aMemoryUsage) override { + aMemoryUsage.mMetadataBytes = metadata_size(); + if (gMut) { + MutexAutoLock lock(GMut::sMutex); + aMemoryUsage.mFragmentationBytes = gMut->FragmentationBytes(); + } else { + aMemoryUsage.mFragmentationBytes = 0; + } + } }; // WARNING: this function runs *very* early -- before all static initializers diff --git a/xpcom/base/moz.build b/xpcom/base/moz.build index 5576b3194c3ca6ccc90016043944fbb305437adb..f685758b6fadb8c99e842c32f0af43f92cf3c638 100644 --- a/xpcom/base/moz.build +++ b/xpcom/base/moz.build @@ -220,6 +220,9 @@ if CONFIG["OS_TARGET"] == "Linux": "AvailableMemoryWatcherUtils.h", ] +if CONFIG["MOZ_PHC"]: + DEFINES["MOZ_PHC"] = True + GeneratedFile("ErrorList.h", script="ErrorList.py", entry_point="error_list_h") GeneratedFile( "ErrorNamesInternal.h", script="ErrorList.py", entry_point="error_names_internal_h" diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index de76bd7d8afbd939226deb88384f1c73f23a572b..723cc6753205fab3f9576343e44a0d2a9d161ef0 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -1335,6 +1335,25 @@ class JemallocHeapReporter final : public nsIMemoryReporter { MOZ_COLLECT_REPORT( "heap-chunksize", KIND_OTHER, UNITS_BYTES, stats.chunksize, "Size of chunks."); + +#ifdef MOZ_PHC + mozilla::phc::MemoryUsage usage; + ReplaceMalloc::PHCMemoryUsage(usage); + + MOZ_COLLECT_REPORT( + "explicit/heap-overhead/phc/metadata", KIND_NONHEAP, UNITS_BYTES, + usage.mMetadataBytes, +"Memory used by PHC to store stacks and other metadata for each allocation"); + MOZ_COLLECT_REPORT( + "explicit/heap-overhead/phc/fragmentation", KIND_NONHEAP, UNITS_BYTES, + usage.mFragmentationBytes, +"The amount of memory lost due to rounding up allocations to the next page " +"size. " +"This is also known as 'internal fragmentation'. " +"Note that all allocators have some internal fragmentation, there may still " +"be some internal fragmentation without PHC."); +#endif + // clang-format on return NS_OK;