From dcda564f85ea8fa1206ef75302ce6983e2f5d63e Mon Sep 17 00:00:00 2001
From: Paul Bone <pbone@mozilla.com>
Date: Wed, 29 Mar 2023 11:08:17 +0000
Subject: [PATCH] Bug 1822451 - Add about:memory entries for PHC r=glandium

Differential Revision: https://phabricator.services.mozilla.com/D173099

Depends on D173098
---
 memory/build/replace_malloc_bridge.h   | 24 ++++++++++++++--
 memory/replace/phc/PHC.cpp             | 38 ++++++++++++++++++++++++--
 xpcom/base/moz.build                   |  3 ++
 xpcom/base/nsMemoryReporterManager.cpp | 19 +++++++++++++
 4 files changed, 79 insertions(+), 5 deletions(-)

diff --git a/memory/build/replace_malloc_bridge.h b/memory/build/replace_malloc_bridge.h
index fb45355d36938..20683c85df052 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 02bc0d3d398ca..cd0f6322bb98a 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 5576b3194c3ca..f685758b6fadb 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 de76bd7d8afbd..723cc6753205f 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;
-- 
GitLab