[compiler-rt] 373d35d - [scudo] Added test fixture for cache tests. (#102230)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 8 13:44:30 PDT 2024
Author: Joshua Baehring
Date: 2024-08-08T13:44:27-07:00
New Revision: 373d35d1e157996e168bb4fcaef0348bea12e295
URL: https://github.com/llvm/llvm-project/commit/373d35d1e157996e168bb4fcaef0348bea12e295
DIFF: https://github.com/llvm/llvm-project/commit/373d35d1e157996e168bb4fcaef0348bea12e295.diff
LOG: [scudo] Added test fixture for cache tests. (#102230)
The test fixture simplifies some of the logic for allocations and
mmap-based allocations
are separated from the cache to allow for more direct cache tests.
Additionally, a couple
of end to end tests for the cache and the LRU algorithm are added.
Added:
Modified:
compiler-rt/lib/scudo/standalone/secondary.h
compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
Removed:
################################################################################
diff --git a/compiler-rt/lib/scudo/standalone/secondary.h b/compiler-rt/lib/scudo/standalone/secondary.h
index 34b16df75841b5..27f8697db7838f 100644
--- a/compiler-rt/lib/scudo/standalone/secondary.h
+++ b/compiler-rt/lib/scudo/standalone/secondary.h
@@ -178,7 +178,11 @@ template <typename T> class NonZeroLengthArray<T, 0> {
T &operator[](uptr UNUSED Idx) { UNREACHABLE("Unsupported!"); }
};
-template <typename Config> class MapAllocatorCache {
+// The default unmap callback is simply scudo::unmap.
+// In testing, a
diff erent unmap callback is used to
+// record information about unmaps in the cache
+template <typename Config, void (*unmapCallBack)(MemMapT &) = unmap>
+class MapAllocatorCache {
public:
void getStats(ScopedString *Str) {
ScopedLock L(Mutex);
@@ -246,6 +250,7 @@ template <typename Config> class MapAllocatorCache {
const s32 Interval = atomic_load_relaxed(&ReleaseToOsIntervalMs);
u64 Time;
CachedBlock Entry;
+
Entry.CommitBase = CommitBase;
Entry.CommitSize = CommitSize;
Entry.BlockBegin = BlockBegin;
@@ -290,7 +295,7 @@ template <typename Config> class MapAllocatorCache {
// read Options and when we locked Mutex. We can't insert our entry into
// the quarantine or the cache because the permissions would be wrong so
// just unmap it.
- unmap(Entry.MemMap);
+ unmapCallBack(Entry.MemMap);
break;
}
if (Config::getQuarantineSize() && useMemoryTagging<Config>(Options)) {
@@ -321,7 +326,7 @@ template <typename Config> class MapAllocatorCache {
} while (0);
for (MemMapT &EvictMemMap : EvictionMemMaps)
- unmap(EvictMemMap);
+ unmapCallBack(EvictMemMap);
if (Interval >= 0) {
// TODO: Add ReleaseToOS logic to LRU algorithm
@@ -423,7 +428,7 @@ template <typename Config> class MapAllocatorCache {
for (u32 I = 0; I != Config::getQuarantineSize(); ++I) {
if (Quarantine[I].isValid()) {
MemMapT &MemMap = Quarantine[I].MemMap;
- unmap(MemMap);
+ unmapCallBack(MemMap);
Quarantine[I].invalidate();
}
}
@@ -517,7 +522,7 @@ template <typename Config> class MapAllocatorCache {
}
for (uptr I = 0; I < N; I++) {
MemMapT &MemMap = MapInfo[I];
- unmap(MemMap);
+ unmapCallBack(MemMap);
}
}
diff --git a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
index 5685a9335316d1..e85b6abdb36d22 100644
--- a/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp
@@ -265,3 +265,101 @@ TEST_F(MapAllocatorWithReleaseTest, SecondaryThreadsRace) {
Allocator->getStats(&Str);
Str.output();
}
+
+struct MapAllocatorCacheTest : public Test {
+ static constexpr scudo::u32 UnmappedMarker = 0xDEADBEEF;
+
+ static void testUnmapCallback(scudo::MemMapT &MemMap) {
+ scudo::u32 *Ptr = reinterpret_cast<scudo::u32 *>(MemMap.getBase());
+ *Ptr = UnmappedMarker;
+ }
+
+ using SecondaryConfig = scudo::SecondaryConfig<TestConfig>;
+ using CacheConfig = SecondaryConfig::CacheConfig;
+ using CacheT = scudo::MapAllocatorCache<CacheConfig, testUnmapCallback>;
+
+ std::unique_ptr<CacheT> Cache = std::make_unique<CacheT>();
+
+ const scudo::uptr PageSize = scudo::getPageSizeCached();
+ // The current test allocation size is set to the minimum size
+ // needed for the scudo allocator to fall back to the secondary allocator
+ static constexpr scudo::uptr TestAllocSize =
+ CacheConfig::getDefaultMaxEntrySize();
+
+ scudo::Options Options = getOptionsForConfig<SecondaryConfig>();
+
+ void SetUp() override { Cache->init(/*ReleaseToOsInterval=*/-1); }
+
+ void TearDown() override { Cache->unmapTestOnly(); }
+
+ scudo::MemMapT allocate(scudo::uptr Size) {
+ scudo::uptr MapSize = scudo::roundUp(Size, PageSize);
+ scudo::ReservedMemoryT ReservedMemory;
+ CHECK(ReservedMemory.create(0U, MapSize, nullptr, MAP_ALLOWNOMEM));
+
+ scudo::MemMapT MemMap = ReservedMemory.dispatch(
+ ReservedMemory.getBase(), ReservedMemory.getCapacity());
+ MemMap.remap(MemMap.getBase(), MemMap.getCapacity(), "scudo:test",
+ MAP_RESIZABLE | MAP_ALLOWNOMEM);
+ return MemMap;
+ }
+
+ void fillCacheWithSameSizeBlocks(std::vector<scudo::MemMapT> &MemMaps,
+ scudo::uptr NumEntries, scudo::uptr Size) {
+ for (scudo::uptr I = 0; I < NumEntries; I++) {
+ MemMaps.emplace_back(allocate(Size));
+ auto &MemMap = MemMaps[I];
+ Cache->store(Options, MemMap.getBase(), MemMap.getCapacity(),
+ MemMap.getBase(), MemMap);
+ }
+ }
+};
+
+TEST_F(MapAllocatorCacheTest, CacheOrder) {
+ std::vector<scudo::MemMapT> MemMaps;
+ Cache->setOption(scudo::Option::MaxCacheEntriesCount,
+ CacheConfig::getEntriesArraySize());
+
+ fillCacheWithSameSizeBlocks(MemMaps, CacheConfig::getEntriesArraySize(),
+ TestAllocSize);
+
+ // Retrieval order should be the inverse of insertion order
+ for (scudo::uptr I = CacheConfig::getEntriesArraySize(); I > 0; I--) {
+ scudo::uptr EntryHeaderPos;
+ scudo::CachedBlock Entry =
+ Cache->retrieve(TestAllocSize, PageSize, 0, EntryHeaderPos);
+ EXPECT_EQ(Entry.MemMap.getBase(), MemMaps[I - 1].getBase());
+ }
+
+ // Clean up MemMaps
+ for (auto &MemMap : MemMaps)
+ MemMap.unmap();
+}
+
+TEST_F(MapAllocatorCacheTest, MemoryLeakTest) {
+ std::vector<scudo::MemMapT> MemMaps;
+ // Fill the cache above MaxEntriesCount to force an eviction
+ // The first cache entry should be evicted (because it is the oldest)
+ // due to the maximum number of entries being reached
+ fillCacheWithSameSizeBlocks(
+ MemMaps, CacheConfig::getDefaultMaxEntriesCount() + 1, TestAllocSize);
+
+ std::vector<scudo::CachedBlock> RetrievedEntries;
+
+ // First MemMap should be evicted from cache because it was the first
+ // inserted into the cache
+ for (scudo::uptr I = CacheConfig::getDefaultMaxEntriesCount(); I > 0; I--) {
+ scudo::uptr EntryHeaderPos;
+ RetrievedEntries.push_back(
+ Cache->retrieve(TestAllocSize, PageSize, 0, EntryHeaderPos));
+ EXPECT_EQ(MemMaps[I].getBase(), RetrievedEntries.back().MemMap.getBase());
+ }
+
+ // Evicted entry should be marked due to unmap callback
+ EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(MemMaps[0].getBase()),
+ UnmappedMarker);
+
+ // Clean up MemMaps
+ for (auto &MemMap : MemMaps)
+ MemMap.unmap();
+}
More information about the llvm-commits
mailing list