[compiler-rt] [scudo] Track maximum resident memory in cache (PR #219036)
Sadaf Ebrahimi via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 27 09:45:53 PDT 2026
https://github.com/sadafebrahimi updated https://github.com/llvm/llvm-project/pull/219036
>From f3ba7a9a3cd690967122cb911d521b555c6af0ea 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 | 37 +++++++++++++++-
.../scudo/standalone/tests/secondary_test.cpp | 44 +++++++++++++++++++
2 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/lib/scudo/standalone/secondary.h b/compiler-rt/lib/scudo/standalone/secondary.h
index 16e94f0cb7511..5750636eb9484 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");
}
@@ -231,11 +234,12 @@ class MapAllocatorCache {
Str->append(
"Stats: MapAllocatorCache: EntriesCount: %zu, "
"MaxEntriesCount: %u, MaxEntrySize: %zu, ReleaseToOsSkips: "
- "%zu, ReleaseToOsIntervalMs = %d, Unmapped due to eviction: %u, ",
+ "%zu, ReleaseToOsIntervalMs = %d, Unmapped due to eviction: %u, "
+ "MaxResidentBytes: %zu, CurrentResidentBytes: %zu\n",
LRUEntries.size(), atomic_load_relaxed(&MaxEntriesCount),
atomic_load_relaxed(&MaxEntrySize),
atomic_load_relaxed(&ReleaseToOsSkips), Interval >= 0 ? Interval : -1,
- EvictedCount);
+ EvictedCount, MaxResidentBytes, CurrentResidentBytes);
Str->append("Stats: CacheRetrievalStats: SuccessRate: %u/%u "
"(%zu.%02zu%%)\n",
SuccessfulRetrieves, CallsToRetrieve, Integral, Fractional);
@@ -565,6 +569,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 +595,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 +609,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 +625,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 +660,7 @@ class MapAllocatorCache {
Entry->MemMap.releaseAndZeroPagesToOS(Entry->CommitBase,
Entry->CommitSize);
+ CurrentResidentBytes -= Entry->CommitSize;
Entry->Time = 0;
}
OldestPresentEntry = nullptr;
@@ -651,6 +674,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 +761,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..8e9d78c4b5453 100644
--- a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
@@ -680,3 +680,47 @@ 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();
+ EXPECT_NE(0U, Size1);
+ 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();
+ EXPECT_NE(0U, Size2);
+ 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();
+ EXPECT_NE(0U, Size3);
+ 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