blob: 723679228cbabce181d19636b9cb13d646408cc5 [file] [log] [blame]
Dynamic Tools Team517193e2019-09-11 14:48:41 +00001//===-- secondary_test.cpp --------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
Vitaly Bukaace22c72021-06-30 23:39:12 -07009#include "memtag.h"
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080010#include "tests/scudo_unit_test.h"
Dynamic Tools Team517193e2019-09-11 14:48:41 +000011
Peter Collingbourne7488a172020-12-14 13:57:59 -080012#include "allocator_config.h"
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080013#include "secondary.h"
Dynamic Tools Team517193e2019-09-11 14:48:41 +000014
Dynamic Tools Team517193e2019-09-11 14:48:41 +000015#include <condition_variable>
Vitaly Bukad3199322021-07-01 10:22:35 -070016#include <memory>
Dynamic Tools Team517193e2019-09-11 14:48:41 +000017#include <mutex>
Dynamic Tools Teamd2740c52019-11-19 13:58:06 -080018#include <random>
Vitaly Bukad3199322021-07-01 10:22:35 -070019#include <stdio.h>
Dynamic Tools Team517193e2019-09-11 14:48:41 +000020#include <thread>
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080021#include <vector>
Dynamic Tools Team517193e2019-09-11 14:48:41 +000022
Vitaly Buka97742dd2021-07-01 11:56:11 -070023template <typename Config> static scudo::Options getOptionsForConfig() {
Vitaly Buka7878d872021-07-01 21:40:04 -070024 if (!Config::MaySupportMemoryTagging || !scudo::archSupportsMemoryTagging() ||
25 !scudo::systemSupportsMemoryTagging())
Vitaly Bukaace22c72021-06-30 23:39:12 -070026 return {};
27 scudo::AtomicOptions AO;
28 AO.set(scudo::OptionBit::UseMemoryTagging);
29 return AO.load();
Vitaly Buka97742dd2021-07-01 11:56:11 -070030}
31
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080032template <typename Config> static void testSecondaryBasic(void) {
33 using SecondaryT = scudo::MapAllocator<Config>;
Vitaly Buka97742dd2021-07-01 11:56:11 -070034 scudo::Options Options = getOptionsForConfig<Config>();
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080035
Dynamic Tools Team517193e2019-09-11 14:48:41 +000036 scudo::GlobalStats S;
37 S.init();
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -070038 std::unique_ptr<SecondaryT> L(new SecondaryT);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000039 L->init(&S);
40 const scudo::uptr Size = 1U << 16;
Vitaly Buka97742dd2021-07-01 11:56:11 -070041 void *P = L->allocate(Options, Size);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000042 EXPECT_NE(P, nullptr);
43 memset(P, 'A', Size);
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070044 EXPECT_GE(SecondaryT::getBlockSize(P), Size);
Vitaly Buka97742dd2021-07-01 11:56:11 -070045 L->deallocate(Options, P);
Mitch Phillipsa99b7792021-05-20 10:56:47 -070046
Dynamic Tools Teamc5d5abc2020-01-27 14:03:21 -080047 // If the Secondary can't cache that pointer, it will be unmapped.
Vitaly Buka5cb96362021-05-20 16:17:25 -070048 if (!L->canCache(Size)) {
49 EXPECT_DEATH(
50 {
51 // Repeat few time to avoid missing crash if it's mmaped by unrelated
52 // code.
53 for (int i = 0; i < 10; ++i) {
Vitaly Buka97742dd2021-07-01 11:56:11 -070054 P = L->allocate(Options, Size);
55 L->deallocate(Options, P);
Vitaly Buka5cb96362021-05-20 16:17:25 -070056 memset(P, 'A', Size);
57 }
58 },
59 "");
Vitaly Buka5cb96362021-05-20 16:17:25 -070060 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +000061
62 const scudo::uptr Align = 1U << 16;
Vitaly Buka97742dd2021-07-01 11:56:11 -070063 P = L->allocate(Options, Size + Align, Align);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000064 EXPECT_NE(P, nullptr);
65 void *AlignedP = reinterpret_cast<void *>(
66 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
67 memset(AlignedP, 'A', Size);
Vitaly Buka97742dd2021-07-01 11:56:11 -070068 L->deallocate(Options, P);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000069
70 std::vector<void *> V;
71 for (scudo::uptr I = 0; I < 32U; I++)
Vitaly Buka97742dd2021-07-01 11:56:11 -070072 V.push_back(L->allocate(Options, Size));
Dynamic Tools Teamd2740c52019-11-19 13:58:06 -080073 std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()()));
Dynamic Tools Team517193e2019-09-11 14:48:41 +000074 while (!V.empty()) {
Vitaly Buka97742dd2021-07-01 11:56:11 -070075 L->deallocate(Options, V.back());
Dynamic Tools Team517193e2019-09-11 14:48:41 +000076 V.pop_back();
77 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -070078 scudo::ScopedString Str;
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +000079 L->getStats(&Str);
80 Str.output();
Mitch Phillipsda86a542021-05-24 16:08:57 -070081 L->unmapTestOnly();
Dynamic Tools Team517193e2019-09-11 14:48:41 +000082}
83
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080084struct NoCacheConfig {
85 typedef scudo::MapAllocatorNoCache SecondaryCache;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080086 static const bool MaySupportMemoryTagging = false;
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080087};
88
Peter Collingbourne7488a172020-12-14 13:57:59 -080089struct TestConfig {
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080090 typedef scudo::MapAllocatorCache<TestConfig> SecondaryCache;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080091 static const bool MaySupportMemoryTagging = false;
Peter Collingbourne7488a172020-12-14 13:57:59 -080092 static const scudo::u32 SecondaryCacheEntriesArraySize = 128U;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080093 static const scudo::u32 SecondaryCacheQuarantineSize = 0U;
Peter Collingbourne7488a172020-12-14 13:57:59 -080094 static const scudo::u32 SecondaryCacheDefaultMaxEntriesCount = 64U;
95 static const scudo::uptr SecondaryCacheDefaultMaxEntrySize = 1UL << 20;
96 static const scudo::s32 SecondaryCacheMinReleaseToOsIntervalMs = INT32_MIN;
97 static const scudo::s32 SecondaryCacheMaxReleaseToOsIntervalMs = INT32_MAX;
98};
99
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -0700100TEST(ScudoSecondaryTest, SecondaryBasic) {
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -0800101 testSecondaryBasic<NoCacheConfig>();
102 testSecondaryBasic<scudo::DefaultConfig>();
103 testSecondaryBasic<TestConfig>();
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -0700104}
105
Vitaly Bukad3199322021-07-01 10:22:35 -0700106struct MapAllocatorTest : public Test {
Vitaly Buka97742dd2021-07-01 11:56:11 -0700107 using Config = scudo::DefaultConfig;
108 using LargeAllocator = scudo::MapAllocator<Config>;
109
Vitaly Bukad3199322021-07-01 10:22:35 -0700110 void SetUp() override { Allocator->init(nullptr); }
111
112 void TearDown() override { Allocator->unmapTestOnly(); }
113
114 std::unique_ptr<LargeAllocator> Allocator =
115 std::make_unique<LargeAllocator>();
Vitaly Buka97742dd2021-07-01 11:56:11 -0700116 scudo::Options Options = getOptionsForConfig<Config>();
Vitaly Bukad3199322021-07-01 10:22:35 -0700117};
118
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000119// This exercises a variety of combinations of size and alignment for the
120// MapAllocator. The size computation done here mimic the ones done by the
121// combined allocator.
Vitaly Bukad3199322021-07-01 10:22:35 -0700122TEST_F(MapAllocatorTest, SecondaryCombinations) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000123 constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16);
124 constexpr scudo::uptr HeaderSize = scudo::roundUpTo(8, MinAlign);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000125 for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) {
126 for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16;
127 AlignLog++) {
128 const scudo::uptr Align = 1U << AlignLog;
129 for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) {
130 if (static_cast<scudo::sptr>(1U << SizeLog) + Delta <= 0)
131 continue;
132 const scudo::uptr UserSize =
133 scudo::roundUpTo((1U << SizeLog) + Delta, MinAlign);
134 const scudo::uptr Size =
135 HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0);
Vitaly Bukad3199322021-07-01 10:22:35 -0700136 void *P = Allocator->allocate(Options, Size, Align);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000137 EXPECT_NE(P, nullptr);
138 void *AlignedP = reinterpret_cast<void *>(
139 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
140 memset(AlignedP, 0xff, UserSize);
Vitaly Bukad3199322021-07-01 10:22:35 -0700141 Allocator->deallocate(Options, P);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000142 }
143 }
144 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700145 scudo::ScopedString Str;
Vitaly Bukad3199322021-07-01 10:22:35 -0700146 Allocator->getStats(&Str);
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000147 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000148}
149
Vitaly Bukad3199322021-07-01 10:22:35 -0700150TEST_F(MapAllocatorTest, SecondaryIterate) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000151 std::vector<void *> V;
152 const scudo::uptr PageSize = scudo::getPageSizeCached();
153 for (scudo::uptr I = 0; I < 32U; I++)
Vitaly Bukad3199322021-07-01 10:22:35 -0700154 V.push_back(Allocator->allocate(Options, (std::rand() % 16) * PageSize));
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000155 auto Lambda = [V](scudo::uptr Block) {
156 EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)),
157 V.end());
158 };
Vitaly Bukad3199322021-07-01 10:22:35 -0700159 Allocator->disable();
160 Allocator->iterateOverBlocks(Lambda);
161 Allocator->enable();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000162 while (!V.empty()) {
Vitaly Bukad3199322021-07-01 10:22:35 -0700163 Allocator->deallocate(Options, V.back());
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000164 V.pop_back();
165 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700166 scudo::ScopedString Str;
Vitaly Bukad3199322021-07-01 10:22:35 -0700167 Allocator->getStats(&Str);
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000168 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000169}
170
Vitaly Bukad3199322021-07-01 10:22:35 -0700171TEST_F(MapAllocatorTest, SecondaryOptions) {
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700172 // Attempt to set a maximum number of entries higher than the array size.
Vitaly Bukad3199322021-07-01 10:22:35 -0700173 EXPECT_FALSE(
174 Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4096U));
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700175 // A negative number will be cast to a scudo::u32, and fail.
Vitaly Bukad3199322021-07-01 10:22:35 -0700176 EXPECT_FALSE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, -1));
177 if (Allocator->canCache(0U)) {
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700178 // Various valid combinations.
Vitaly Bukad3199322021-07-01 10:22:35 -0700179 EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
180 EXPECT_TRUE(
181 Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
182 EXPECT_TRUE(Allocator->canCache(1UL << 18));
183 EXPECT_TRUE(
184 Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17));
185 EXPECT_FALSE(Allocator->canCache(1UL << 18));
186 EXPECT_TRUE(Allocator->canCache(1UL << 16));
187 EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 0U));
188 EXPECT_FALSE(Allocator->canCache(1UL << 16));
189 EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
190 EXPECT_TRUE(
191 Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
192 EXPECT_TRUE(Allocator->canCache(1UL << 16));
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000193 }
194}
195
Vitaly Bukad3199322021-07-01 10:22:35 -0700196struct MapAllocatorWithReleaseTest : public MapAllocatorTest {
197 void SetUp() override { Allocator->init(nullptr, /*ReleaseToOsInterval=*/0); }
198
199 void performAllocations() {
200 std::vector<void *> V;
201 const scudo::uptr PageSize = scudo::getPageSizeCached();
202 {
203 std::unique_lock<std::mutex> Lock(Mutex);
204 while (!Ready)
205 Cv.wait(Lock);
206 }
207 for (scudo::uptr I = 0; I < 128U; I++) {
208 // Deallocate 75% of the blocks.
209 const bool Deallocate = (rand() & 3) != 0;
210 void *P = Allocator->allocate(Options, (std::rand() % 16) * PageSize);
211 if (Deallocate)
212 Allocator->deallocate(Options, P);
213 else
214 V.push_back(P);
215 }
216 while (!V.empty()) {
217 Allocator->deallocate(Options, V.back());
218 V.pop_back();
219 }
220 }
221
222 std::mutex Mutex;
223 std::condition_variable Cv;
224 bool Ready = false;
225};
226
227TEST_F(MapAllocatorWithReleaseTest, SecondaryThreadsRace) {
Dynamic Tools Teamfa69c702020-02-05 09:58:52 -0800228 std::thread Threads[16];
229 for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++)
Vitaly Bukad3199322021-07-01 10:22:35 -0700230 Threads[I] =
231 std::thread(&MapAllocatorWithReleaseTest::performAllocations, this);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000232 {
233 std::unique_lock<std::mutex> Lock(Mutex);
234 Ready = true;
235 Cv.notify_all();
236 }
237 for (auto &T : Threads)
238 T.join();
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700239 scudo::ScopedString Str;
Vitaly Bukad3199322021-07-01 10:22:35 -0700240 Allocator->getStats(&Str);
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000241 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000242}