[compiler-rt] [scudo] Track maximum resident memory in cache (PR #219036)

Sadaf Ebrahimi via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 26 13:57:57 PDT 2026


https://github.com/sadafebrahimi created https://github.com/llvm/llvm-project/pull/219036

Add tracking for the resident memory in MapAllocatorCache. Unreleased cache entries where Time != 0 are assumed to be resident, while entries that have passed through releaseOlderThan where Time == 0 have had their physical pages discarded.

Tracking is performed by updating CurrentResidentBytes on store, remove, and release operations, recoding the peak in maxResidentBytes and reporting it in getStats.

>From 3bc912d70757457f8cf2b24f173a420ee29a955f Mon Sep 17 00:00:00 2001
From: Sadaf Ebrahimi <sadafebrahimi at google.com>
Date: Wed, 26 Aug 2026 20:49:58 +0000
Subject: [PATCH] [scudo] Track maximum resident memory in cache

Add tracking for the resident memory in MapAllocatorCache. Unreleased
cache entries where Time != 0 are assumed to be resident, while entries
that have passed through releaseOlderThan where Time == 0 have had
their physical pages discarded.

Tracking is performed by updating CurrentResidentBytes on store, remove,
and release operations, recoding the peak in maxResidentBytes and
reporting it in getStats.
---
 compiler-rt/lib/scudo/standalone/secondary.h  | 32 +++++++++++++++
 .../scudo/standalone/tests/secondary_test.cpp | 41 +++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/compiler-rt/lib/scudo/standalone/secondary.h b/compiler-rt/lib/scudo/standalone/secondary.h
index 16e94f0cb7511..956ce7c6c8ffd 100644
--- a/compiler-rt/lib/scudo/standalone/secondary.h
+++ b/compiler-rt/lib/scudo/standalone/secondary.h
@@ -142,6 +142,9 @@ template <typename Config> class MapAllocatorNoCache {
     return true;
   }
 
+  uptr getMaxResidentBytesTestOnly() const { return 0; }
+  uptr getCurrentResidentBytesTestOnly() const { return 0; }
+
   void getStats(UNUSED ScopedString *Str) {
     Str->append("Secondary Cache Disabled\n");
   }
@@ -565,6 +568,16 @@ class MapAllocatorCache {
 
   void enable() NO_THREAD_SAFETY_ANALYSIS { Mutex.unlock(); }
 
+  uptr getMaxResidentBytesTestOnly() {
+    ScopedLock L(Mutex);
+    return MaxResidentBytes;
+  }
+
+  uptr getCurrentResidentBytesTestOnly() {
+    ScopedLock L(Mutex);
+    return CurrentResidentBytes;
+  }
+
   void unmapTestOnly() { empty(); }
 
   void releaseOlderThanTestOnly(u64 ReleaseTime) {
@@ -581,6 +594,11 @@ class MapAllocatorCache {
     LRUEntries.push_front(AvailEntry);
     if (OldestPresentEntry == nullptr && AvailEntry->Time != 0)
       OldestPresentEntry = AvailEntry;
+    if (AvailEntry->Time != 0) {
+      CurrentResidentBytes += Entry.CommitSize;
+      if (CurrentResidentBytes > MaxResidentBytes)
+        MaxResidentBytes = CurrentResidentBytes;
+    }
   }
 
   void remove(CachedBlock *Entry) REQUIRES(Mutex) {
@@ -590,6 +608,8 @@ class MapAllocatorCache {
       DCHECK(OldestPresentEntry == nullptr || OldestPresentEntry->Time != 0);
     }
     LRUEntries.remove(Entry);
+    if (Entry->Time != 0)
+      CurrentResidentBytes -= Entry->CommitSize;
     Entry->invalidate();
     AvailEntries.push_front(Entry);
   }
@@ -604,6 +624,7 @@ class MapAllocatorCache {
         MapInfo[N++] = Entry.MemMap;
       LRUEntries.clear();
       OldestPresentEntry = nullptr;
+      CurrentResidentBytes = 0;
     }
     for (uptr I = 0; I < N; I++) {
       MemMapT &MemMap = MapInfo[I];
@@ -638,6 +659,7 @@ class MapAllocatorCache {
 
       Entry->MemMap.releaseAndZeroPagesToOS(Entry->CommitBase,
                                             Entry->CommitSize);
+      CurrentResidentBytes -= Entry->CommitSize;
       Entry->Time = 0;
     }
     OldestPresentEntry = nullptr;
@@ -651,6 +673,8 @@ class MapAllocatorCache {
   u32 CallsToRetrieve GUARDED_BY(Mutex) = 0;
   u32 SuccessfulRetrieves GUARDED_BY(Mutex) = 0;
   u32 EvictedCount GUARDED_BY(Mutex) = 0;
+  uptr CurrentResidentBytes GUARDED_BY(Mutex) = 0;
+  uptr MaxResidentBytes GUARDED_BY(Mutex) = 0;
   atomic_uptr ReleaseToOsSkips = {};
 
   CachedBlock Entries[Config::getEntriesArraySize()] GUARDED_BY(Mutex) = {};
@@ -736,6 +760,14 @@ template <typename Config> class MapAllocator {
 
   void unmapTestOnly() { Cache.unmapTestOnly(); }
 
+  uptr getMaxResidentBytesTestOnly() {
+    return Cache.getMaxResidentBytesTestOnly();
+  }
+
+  uptr getCurrentResidentBytesTestOnly() {
+    return Cache.getCurrentResidentBytesTestOnly();
+  }
+
   void getStats(ScopedString *Str);
 
 private:
diff --git a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
index a3cb493778f38..1665328abf5df 100644
--- a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
@@ -680,3 +680,44 @@ TEST(ScudoSecondaryTest, ReleaseOlderThanGroups) {
     EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(Info.MemMaps[I].getBase()), 0U);
   }
 }
+
+TEST(ScudoSecondaryTest, AllocatorCacheMaxResidentBytes) {
+  CacheInfoType<TestCacheConfig> Info;
+
+  Info.Cache->setOption(scudo::Option::ReleaseInterval, -1);
+  Info.Cache->setOption(scudo::Option::MaxCacheEntriesCount, 10);
+  Info.Cache->setOption(scudo::Option::MaxCacheEntrySize, 1024 * 1024);
+
+  EXPECT_EQ(Info.Cache->getCurrentResidentBytesTestOnly(), 0U);
+  EXPECT_EQ(Info.Cache->getMaxResidentBytesTestOnly(), 0U);
+
+  Info.MemMaps.emplace_back(Info.allocate(1024));
+  const scudo::uptr Size1 = Info.MemMaps[0].getCapacity();
+  Info.storeMemMap(Info.MemMaps[0]);
+
+  EXPECT_EQ(Info.Cache->getCurrentResidentBytesTestOnly(), Size1);
+  EXPECT_EQ(Info.Cache->getMaxResidentBytesTestOnly(), Size1);
+
+  Info.MemMaps.emplace_back(Info.allocate(1024));
+  const scudo::uptr Size2 = Info.MemMaps[1].getCapacity();
+  Info.storeMemMap(Info.MemMaps[1]);
+
+  EXPECT_EQ(Info.Cache->getCurrentResidentBytesTestOnly(), Size1 + Size2);
+  EXPECT_EQ(Info.Cache->getMaxResidentBytesTestOnly(), Size1 + Size2);
+
+  const scudo::uptr PeakBytes = Size1 + Size2;
+
+  // Releasing pages should drop CurrentResidentBytes to 0, while
+  // MaxResidentBytes stays at peak
+  Info.Cache->releaseOlderThanTestOnly(UINT64_MAX);
+  EXPECT_EQ(Info.Cache->getCurrentResidentBytesTestOnly(), 0U);
+  EXPECT_EQ(Info.Cache->getMaxResidentBytesTestOnly(), PeakBytes);
+
+  // Store a third map to verify CurrentResidentBytes increases again
+  Info.MemMaps.emplace_back(Info.allocate(1024));
+  const scudo::uptr Size3 = Info.MemMaps[2].getCapacity();
+  Info.storeMemMap(Info.MemMaps[2]);
+
+  EXPECT_EQ(Info.Cache->getCurrentResidentBytesTestOnly(), Size3);
+  EXPECT_EQ(Info.Cache->getMaxResidentBytesTestOnly(), PeakBytes);
+}



More information about the llvm-commits mailing list