summaryrefslogtreecommitdiff
path: root/lib/scudo/standalone/tests/primary_test.cpp
diff options
context:
space:
mode:
authorKostya Kortchinsky <kostyak@google.com>2019-10-07 17:37:39 +0000
committerKostya Kortchinsky <kostyak@google.com>2019-10-07 17:37:39 +0000
commit5b3c533a7b7066ffe881b7e9a15b026682e04427 (patch)
treed684fc80a38285242ee8b1e494c462f40a361886 /lib/scudo/standalone/tests/primary_test.cpp
parent3154ce262c73bf5daecedbad78183d1d3d1fc648 (diff)
downloadcompiler-rt-5b3c533a7b7066ffe881b7e9a15b026682e04427.tar.gz
[scudo][standalone] Correct releaseToOS behavior
Summary: There was an issue in `releaseToOSMaybe`: one of the criteria to decide if we should proceed with the release was wrong. Namely: ``` const uptr N = Sci->Stats.PoppedBlocks - Sci->Stats.PushedBlocks; if (N * BlockSize < PageSize) return; // No chance to release anything. ``` I meant to check if the amount of bytes in the free list was lower than a page, but this actually checks if the amount of **in use** bytes was lower than a page. The correct code is: ``` const uptr BytesInFreeList = Region->AllocatedUser - (Region->Stats.PoppedBlocks - Region->Stats.PushedBlocks) * BlockSize; if (BytesInFreeList < PageSize) return 0; // No chance to release anything. ``` Consequences of the bug: - if a class size has less than a page worth of in-use bytes (allocated or in a cache), reclaiming would not occur, whatever the amount of blocks in the free list; in real world scenarios this is unlikely to happen and be impactful; - if a class size had less than a page worth of free bytes (and enough in-use bytes, etc), then reclaiming would be attempted, with likely no result. This means the reclaiming was overzealous at times. I didn't have a good way to test for this, so I changed the prototype of the function to return the number of bytes released, allowing to get the information needed. The test added fails with the initial criteria. Another issue is that `ReleaseToOsInterval` can actually be 0, meaning we always try to release (side note: it's terrible for performances). so change a `> 0` check to `>= 0`. Additionally, decrease the `CanRelease` threshold to `PageSize / 32`. I still have to make that configurable but I will do it at another time. Finally, rename some variables in `printStats`: I feel like "available" was too ambiguous, so change it to "total". Reviewers: morehouse, hctim, eugenis, vitalybuka, cferris Reviewed By: morehouse Subscribers: delcypher, #sanitizers, llvm-commits Tags: #llvm, #sanitizers Differential Revision: https://reviews.llvm.org/D68471 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@373930 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/scudo/standalone/tests/primary_test.cpp')
-rw-r--r--lib/scudo/standalone/tests/primary_test.cpp29
1 files changed, 29 insertions, 0 deletions
diff --git a/lib/scudo/standalone/tests/primary_test.cpp b/lib/scudo/standalone/tests/primary_test.cpp
index 329a4c119..a6cfc6bdb 100644
--- a/lib/scudo/standalone/tests/primary_test.cpp
+++ b/lib/scudo/standalone/tests/primary_test.cpp
@@ -188,3 +188,32 @@ TEST(ScudoPrimaryTest, PrimaryThreaded) {
testPrimaryThreaded<scudo::SizeClassAllocator32<SizeClassMap, 18U>>();
testPrimaryThreaded<scudo::SizeClassAllocator64<SizeClassMap, 24U>>();
}
+
+// Through a simple allocation that spans two pages, verify that releaseToOS
+// actually releases some bytes (at least one page worth). This is a regression
+// test for an error in how the release criteria were computed.
+template <typename Primary> static void testReleaseToOS() {
+ auto Deleter = [](Primary *P) {
+ P->unmapTestOnly();
+ delete P;
+ };
+ std::unique_ptr<Primary, decltype(Deleter)> Allocator(new Primary, Deleter);
+ Allocator->init(/*ReleaseToOsInterval=*/-1);
+ typename Primary::CacheT Cache;
+ Cache.init(nullptr, Allocator.get());
+ const scudo::uptr Size = scudo::getPageSizeCached() * 2;
+ EXPECT_TRUE(Primary::canAllocate(Size));
+ const scudo::uptr ClassId =
+ Primary::SizeClassMap::getClassIdBySize(Size);
+ void *P = Cache.allocate(ClassId);
+ EXPECT_NE(P, nullptr);
+ Cache.deallocate(ClassId, P);
+ Cache.destroy(nullptr);
+ EXPECT_GT(Allocator->releaseToOS(), 0U);
+}
+
+TEST(ScudoPrimaryTest, ReleaseToOS) {
+ using SizeClassMap = scudo::DefaultSizeClassMap;
+ testReleaseToOS<scudo::SizeClassAllocator32<SizeClassMap, 18U>>();
+ testReleaseToOS<scudo::SizeClassAllocator64<SizeClassMap, 24U>>();
+}