1*76559068SAndroid Build Coastguard Worker //===-- secondary_test.cpp --------------------------------------*- C++ -*-===//
2*76559068SAndroid Build Coastguard Worker //
3*76559068SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*76559068SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*76559068SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*76559068SAndroid Build Coastguard Worker //
7*76559068SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*76559068SAndroid Build Coastguard Worker
9*76559068SAndroid Build Coastguard Worker #include "memtag.h"
10*76559068SAndroid Build Coastguard Worker #include "tests/scudo_unit_test.h"
11*76559068SAndroid Build Coastguard Worker
12*76559068SAndroid Build Coastguard Worker #include "allocator_config.h"
13*76559068SAndroid Build Coastguard Worker #include "allocator_config_wrapper.h"
14*76559068SAndroid Build Coastguard Worker #include "secondary.h"
15*76559068SAndroid Build Coastguard Worker
16*76559068SAndroid Build Coastguard Worker #include <algorithm>
17*76559068SAndroid Build Coastguard Worker #include <condition_variable>
18*76559068SAndroid Build Coastguard Worker #include <memory>
19*76559068SAndroid Build Coastguard Worker #include <mutex>
20*76559068SAndroid Build Coastguard Worker #include <random>
21*76559068SAndroid Build Coastguard Worker #include <stdio.h>
22*76559068SAndroid Build Coastguard Worker #include <thread>
23*76559068SAndroid Build Coastguard Worker #include <vector>
24*76559068SAndroid Build Coastguard Worker
getOptionsForConfig()25*76559068SAndroid Build Coastguard Worker template <typename Config> static scudo::Options getOptionsForConfig() {
26*76559068SAndroid Build Coastguard Worker if (!Config::getMaySupportMemoryTagging() ||
27*76559068SAndroid Build Coastguard Worker !scudo::archSupportsMemoryTagging() ||
28*76559068SAndroid Build Coastguard Worker !scudo::systemSupportsMemoryTagging())
29*76559068SAndroid Build Coastguard Worker return {};
30*76559068SAndroid Build Coastguard Worker scudo::AtomicOptions AO;
31*76559068SAndroid Build Coastguard Worker AO.set(scudo::OptionBit::UseMemoryTagging);
32*76559068SAndroid Build Coastguard Worker return AO.load();
33*76559068SAndroid Build Coastguard Worker }
34*76559068SAndroid Build Coastguard Worker
testSecondaryBasic(void)35*76559068SAndroid Build Coastguard Worker template <typename Config> static void testSecondaryBasic(void) {
36*76559068SAndroid Build Coastguard Worker using SecondaryT = scudo::MapAllocator<scudo::SecondaryConfig<Config>>;
37*76559068SAndroid Build Coastguard Worker scudo::Options Options =
38*76559068SAndroid Build Coastguard Worker getOptionsForConfig<scudo::SecondaryConfig<Config>>();
39*76559068SAndroid Build Coastguard Worker
40*76559068SAndroid Build Coastguard Worker scudo::GlobalStats S;
41*76559068SAndroid Build Coastguard Worker S.init();
42*76559068SAndroid Build Coastguard Worker std::unique_ptr<SecondaryT> L(new SecondaryT);
43*76559068SAndroid Build Coastguard Worker L->init(&S);
44*76559068SAndroid Build Coastguard Worker const scudo::uptr Size = 1U << 16;
45*76559068SAndroid Build Coastguard Worker void *P = L->allocate(Options, Size);
46*76559068SAndroid Build Coastguard Worker EXPECT_NE(P, nullptr);
47*76559068SAndroid Build Coastguard Worker memset(P, 'A', Size);
48*76559068SAndroid Build Coastguard Worker EXPECT_GE(SecondaryT::getBlockSize(P), Size);
49*76559068SAndroid Build Coastguard Worker L->deallocate(Options, P);
50*76559068SAndroid Build Coastguard Worker
51*76559068SAndroid Build Coastguard Worker // If the Secondary can't cache that pointer, it will be unmapped.
52*76559068SAndroid Build Coastguard Worker if (!L->canCache(Size)) {
53*76559068SAndroid Build Coastguard Worker EXPECT_DEATH(
54*76559068SAndroid Build Coastguard Worker {
55*76559068SAndroid Build Coastguard Worker // Repeat few time to avoid missing crash if it's mmaped by unrelated
56*76559068SAndroid Build Coastguard Worker // code.
57*76559068SAndroid Build Coastguard Worker for (int i = 0; i < 10; ++i) {
58*76559068SAndroid Build Coastguard Worker P = L->allocate(Options, Size);
59*76559068SAndroid Build Coastguard Worker L->deallocate(Options, P);
60*76559068SAndroid Build Coastguard Worker memset(P, 'A', Size);
61*76559068SAndroid Build Coastguard Worker }
62*76559068SAndroid Build Coastguard Worker },
63*76559068SAndroid Build Coastguard Worker "");
64*76559068SAndroid Build Coastguard Worker }
65*76559068SAndroid Build Coastguard Worker
66*76559068SAndroid Build Coastguard Worker const scudo::uptr Align = 1U << 16;
67*76559068SAndroid Build Coastguard Worker P = L->allocate(Options, Size + Align, Align);
68*76559068SAndroid Build Coastguard Worker EXPECT_NE(P, nullptr);
69*76559068SAndroid Build Coastguard Worker void *AlignedP = reinterpret_cast<void *>(
70*76559068SAndroid Build Coastguard Worker scudo::roundUp(reinterpret_cast<scudo::uptr>(P), Align));
71*76559068SAndroid Build Coastguard Worker memset(AlignedP, 'A', Size);
72*76559068SAndroid Build Coastguard Worker L->deallocate(Options, P);
73*76559068SAndroid Build Coastguard Worker
74*76559068SAndroid Build Coastguard Worker std::vector<void *> V;
75*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < 32U; I++)
76*76559068SAndroid Build Coastguard Worker V.push_back(L->allocate(Options, Size));
77*76559068SAndroid Build Coastguard Worker std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()()));
78*76559068SAndroid Build Coastguard Worker while (!V.empty()) {
79*76559068SAndroid Build Coastguard Worker L->deallocate(Options, V.back());
80*76559068SAndroid Build Coastguard Worker V.pop_back();
81*76559068SAndroid Build Coastguard Worker }
82*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
83*76559068SAndroid Build Coastguard Worker L->getStats(&Str);
84*76559068SAndroid Build Coastguard Worker Str.output();
85*76559068SAndroid Build Coastguard Worker L->unmapTestOnly();
86*76559068SAndroid Build Coastguard Worker }
87*76559068SAndroid Build Coastguard Worker
88*76559068SAndroid Build Coastguard Worker struct NoCacheConfig {
89*76559068SAndroid Build Coastguard Worker static const bool MaySupportMemoryTagging = false;
90*76559068SAndroid Build Coastguard Worker template <typename> using TSDRegistryT = void;
91*76559068SAndroid Build Coastguard Worker template <typename> using PrimaryT = void;
92*76559068SAndroid Build Coastguard Worker template <typename Config> using SecondaryT = scudo::MapAllocator<Config>;
93*76559068SAndroid Build Coastguard Worker
94*76559068SAndroid Build Coastguard Worker struct Secondary {
95*76559068SAndroid Build Coastguard Worker template <typename Config>
96*76559068SAndroid Build Coastguard Worker using CacheT = scudo::MapAllocatorNoCache<Config>;
97*76559068SAndroid Build Coastguard Worker };
98*76559068SAndroid Build Coastguard Worker };
99*76559068SAndroid Build Coastguard Worker
100*76559068SAndroid Build Coastguard Worker struct TestConfig {
101*76559068SAndroid Build Coastguard Worker static const bool MaySupportMemoryTagging = false;
102*76559068SAndroid Build Coastguard Worker template <typename> using TSDRegistryT = void;
103*76559068SAndroid Build Coastguard Worker template <typename> using PrimaryT = void;
104*76559068SAndroid Build Coastguard Worker template <typename> using SecondaryT = void;
105*76559068SAndroid Build Coastguard Worker
106*76559068SAndroid Build Coastguard Worker struct Secondary {
107*76559068SAndroid Build Coastguard Worker struct Cache {
108*76559068SAndroid Build Coastguard Worker static const scudo::u32 EntriesArraySize = 128U;
109*76559068SAndroid Build Coastguard Worker static const scudo::u32 QuarantineSize = 0U;
110*76559068SAndroid Build Coastguard Worker static const scudo::u32 DefaultMaxEntriesCount = 64U;
111*76559068SAndroid Build Coastguard Worker static const scudo::uptr DefaultMaxEntrySize = 1UL << 20;
112*76559068SAndroid Build Coastguard Worker static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;
113*76559068SAndroid Build Coastguard Worker static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
114*76559068SAndroid Build Coastguard Worker };
115*76559068SAndroid Build Coastguard Worker
116*76559068SAndroid Build Coastguard Worker template <typename Config> using CacheT = scudo::MapAllocatorCache<Config>;
117*76559068SAndroid Build Coastguard Worker };
118*76559068SAndroid Build Coastguard Worker };
119*76559068SAndroid Build Coastguard Worker
TEST(ScudoSecondaryTest,SecondaryBasic)120*76559068SAndroid Build Coastguard Worker TEST(ScudoSecondaryTest, SecondaryBasic) {
121*76559068SAndroid Build Coastguard Worker testSecondaryBasic<NoCacheConfig>();
122*76559068SAndroid Build Coastguard Worker testSecondaryBasic<scudo::DefaultConfig>();
123*76559068SAndroid Build Coastguard Worker testSecondaryBasic<TestConfig>();
124*76559068SAndroid Build Coastguard Worker }
125*76559068SAndroid Build Coastguard Worker
126*76559068SAndroid Build Coastguard Worker struct MapAllocatorTest : public Test {
127*76559068SAndroid Build Coastguard Worker using Config = scudo::DefaultConfig;
128*76559068SAndroid Build Coastguard Worker using LargeAllocator = scudo::MapAllocator<scudo::SecondaryConfig<Config>>;
129*76559068SAndroid Build Coastguard Worker
SetUpMapAllocatorTest130*76559068SAndroid Build Coastguard Worker void SetUp() override { Allocator->init(nullptr); }
131*76559068SAndroid Build Coastguard Worker
TearDownMapAllocatorTest132*76559068SAndroid Build Coastguard Worker void TearDown() override { Allocator->unmapTestOnly(); }
133*76559068SAndroid Build Coastguard Worker
134*76559068SAndroid Build Coastguard Worker std::unique_ptr<LargeAllocator> Allocator =
135*76559068SAndroid Build Coastguard Worker std::make_unique<LargeAllocator>();
136*76559068SAndroid Build Coastguard Worker scudo::Options Options =
137*76559068SAndroid Build Coastguard Worker getOptionsForConfig<scudo::SecondaryConfig<Config>>();
138*76559068SAndroid Build Coastguard Worker };
139*76559068SAndroid Build Coastguard Worker
140*76559068SAndroid Build Coastguard Worker // This exercises a variety of combinations of size and alignment for the
141*76559068SAndroid Build Coastguard Worker // MapAllocator. The size computation done here mimic the ones done by the
142*76559068SAndroid Build Coastguard Worker // combined allocator.
TEST_F(MapAllocatorTest,SecondaryCombinations)143*76559068SAndroid Build Coastguard Worker TEST_F(MapAllocatorTest, SecondaryCombinations) {
144*76559068SAndroid Build Coastguard Worker constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16);
145*76559068SAndroid Build Coastguard Worker constexpr scudo::uptr HeaderSize = scudo::roundUp(8, MinAlign);
146*76559068SAndroid Build Coastguard Worker for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) {
147*76559068SAndroid Build Coastguard Worker for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16;
148*76559068SAndroid Build Coastguard Worker AlignLog++) {
149*76559068SAndroid Build Coastguard Worker const scudo::uptr Align = 1U << AlignLog;
150*76559068SAndroid Build Coastguard Worker for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) {
151*76559068SAndroid Build Coastguard Worker if ((1LL << SizeLog) + Delta <= 0)
152*76559068SAndroid Build Coastguard Worker continue;
153*76559068SAndroid Build Coastguard Worker const scudo::uptr UserSize = scudo::roundUp(
154*76559068SAndroid Build Coastguard Worker static_cast<scudo::uptr>((1LL << SizeLog) + Delta), MinAlign);
155*76559068SAndroid Build Coastguard Worker const scudo::uptr Size =
156*76559068SAndroid Build Coastguard Worker HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0);
157*76559068SAndroid Build Coastguard Worker void *P = Allocator->allocate(Options, Size, Align);
158*76559068SAndroid Build Coastguard Worker EXPECT_NE(P, nullptr);
159*76559068SAndroid Build Coastguard Worker void *AlignedP = reinterpret_cast<void *>(
160*76559068SAndroid Build Coastguard Worker scudo::roundUp(reinterpret_cast<scudo::uptr>(P), Align));
161*76559068SAndroid Build Coastguard Worker memset(AlignedP, 0xff, UserSize);
162*76559068SAndroid Build Coastguard Worker Allocator->deallocate(Options, P);
163*76559068SAndroid Build Coastguard Worker }
164*76559068SAndroid Build Coastguard Worker }
165*76559068SAndroid Build Coastguard Worker }
166*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
167*76559068SAndroid Build Coastguard Worker Allocator->getStats(&Str);
168*76559068SAndroid Build Coastguard Worker Str.output();
169*76559068SAndroid Build Coastguard Worker }
170*76559068SAndroid Build Coastguard Worker
TEST_F(MapAllocatorTest,SecondaryIterate)171*76559068SAndroid Build Coastguard Worker TEST_F(MapAllocatorTest, SecondaryIterate) {
172*76559068SAndroid Build Coastguard Worker std::vector<void *> V;
173*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSize = scudo::getPageSizeCached();
174*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < 32U; I++)
175*76559068SAndroid Build Coastguard Worker V.push_back(Allocator->allocate(
176*76559068SAndroid Build Coastguard Worker Options, (static_cast<scudo::uptr>(std::rand()) % 16U) * PageSize));
177*76559068SAndroid Build Coastguard Worker auto Lambda = [&V](scudo::uptr Block) {
178*76559068SAndroid Build Coastguard Worker EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)),
179*76559068SAndroid Build Coastguard Worker V.end());
180*76559068SAndroid Build Coastguard Worker };
181*76559068SAndroid Build Coastguard Worker Allocator->disable();
182*76559068SAndroid Build Coastguard Worker Allocator->iterateOverBlocks(Lambda);
183*76559068SAndroid Build Coastguard Worker Allocator->enable();
184*76559068SAndroid Build Coastguard Worker while (!V.empty()) {
185*76559068SAndroid Build Coastguard Worker Allocator->deallocate(Options, V.back());
186*76559068SAndroid Build Coastguard Worker V.pop_back();
187*76559068SAndroid Build Coastguard Worker }
188*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
189*76559068SAndroid Build Coastguard Worker Allocator->getStats(&Str);
190*76559068SAndroid Build Coastguard Worker Str.output();
191*76559068SAndroid Build Coastguard Worker }
192*76559068SAndroid Build Coastguard Worker
TEST_F(MapAllocatorTest,SecondaryCacheOptions)193*76559068SAndroid Build Coastguard Worker TEST_F(MapAllocatorTest, SecondaryCacheOptions) {
194*76559068SAndroid Build Coastguard Worker if (!Allocator->canCache(0U))
195*76559068SAndroid Build Coastguard Worker TEST_SKIP("Secondary Cache disabled");
196*76559068SAndroid Build Coastguard Worker
197*76559068SAndroid Build Coastguard Worker // Attempt to set a maximum number of entries higher than the array size.
198*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4096U));
199*76559068SAndroid Build Coastguard Worker
200*76559068SAndroid Build Coastguard Worker // Attempt to set an invalid (negative) number of entries
201*76559068SAndroid Build Coastguard Worker EXPECT_FALSE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, -1));
202*76559068SAndroid Build Coastguard Worker
203*76559068SAndroid Build Coastguard Worker // Various valid combinations.
204*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
205*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(
206*76559068SAndroid Build Coastguard Worker Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
207*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Allocator->canCache(1UL << 18));
208*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(
209*76559068SAndroid Build Coastguard Worker Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17));
210*76559068SAndroid Build Coastguard Worker EXPECT_FALSE(Allocator->canCache(1UL << 18));
211*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Allocator->canCache(1UL << 16));
212*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 0U));
213*76559068SAndroid Build Coastguard Worker EXPECT_FALSE(Allocator->canCache(1UL << 16));
214*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
215*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(
216*76559068SAndroid Build Coastguard Worker Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
217*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Allocator->canCache(1UL << 16));
218*76559068SAndroid Build Coastguard Worker }
219*76559068SAndroid Build Coastguard Worker
220*76559068SAndroid Build Coastguard Worker struct MapAllocatorWithReleaseTest : public MapAllocatorTest {
SetUpMapAllocatorWithReleaseTest221*76559068SAndroid Build Coastguard Worker void SetUp() override { Allocator->init(nullptr, /*ReleaseToOsInterval=*/0); }
222*76559068SAndroid Build Coastguard Worker
performAllocationsMapAllocatorWithReleaseTest223*76559068SAndroid Build Coastguard Worker void performAllocations() {
224*76559068SAndroid Build Coastguard Worker std::vector<void *> V;
225*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSize = scudo::getPageSizeCached();
226*76559068SAndroid Build Coastguard Worker {
227*76559068SAndroid Build Coastguard Worker std::unique_lock<std::mutex> Lock(Mutex);
228*76559068SAndroid Build Coastguard Worker while (!Ready)
229*76559068SAndroid Build Coastguard Worker Cv.wait(Lock);
230*76559068SAndroid Build Coastguard Worker }
231*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < 128U; I++) {
232*76559068SAndroid Build Coastguard Worker // Deallocate 75% of the blocks.
233*76559068SAndroid Build Coastguard Worker const bool Deallocate = (std::rand() & 3) != 0;
234*76559068SAndroid Build Coastguard Worker void *P = Allocator->allocate(
235*76559068SAndroid Build Coastguard Worker Options, (static_cast<scudo::uptr>(std::rand()) % 16U) * PageSize);
236*76559068SAndroid Build Coastguard Worker if (Deallocate)
237*76559068SAndroid Build Coastguard Worker Allocator->deallocate(Options, P);
238*76559068SAndroid Build Coastguard Worker else
239*76559068SAndroid Build Coastguard Worker V.push_back(P);
240*76559068SAndroid Build Coastguard Worker }
241*76559068SAndroid Build Coastguard Worker while (!V.empty()) {
242*76559068SAndroid Build Coastguard Worker Allocator->deallocate(Options, V.back());
243*76559068SAndroid Build Coastguard Worker V.pop_back();
244*76559068SAndroid Build Coastguard Worker }
245*76559068SAndroid Build Coastguard Worker }
246*76559068SAndroid Build Coastguard Worker
247*76559068SAndroid Build Coastguard Worker std::mutex Mutex;
248*76559068SAndroid Build Coastguard Worker std::condition_variable Cv;
249*76559068SAndroid Build Coastguard Worker bool Ready = false;
250*76559068SAndroid Build Coastguard Worker };
251*76559068SAndroid Build Coastguard Worker
TEST_F(MapAllocatorWithReleaseTest,SecondaryThreadsRace)252*76559068SAndroid Build Coastguard Worker TEST_F(MapAllocatorWithReleaseTest, SecondaryThreadsRace) {
253*76559068SAndroid Build Coastguard Worker std::thread Threads[16];
254*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++)
255*76559068SAndroid Build Coastguard Worker Threads[I] =
256*76559068SAndroid Build Coastguard Worker std::thread(&MapAllocatorWithReleaseTest::performAllocations, this);
257*76559068SAndroid Build Coastguard Worker {
258*76559068SAndroid Build Coastguard Worker std::unique_lock<std::mutex> Lock(Mutex);
259*76559068SAndroid Build Coastguard Worker Ready = true;
260*76559068SAndroid Build Coastguard Worker Cv.notify_all();
261*76559068SAndroid Build Coastguard Worker }
262*76559068SAndroid Build Coastguard Worker for (auto &T : Threads)
263*76559068SAndroid Build Coastguard Worker T.join();
264*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
265*76559068SAndroid Build Coastguard Worker Allocator->getStats(&Str);
266*76559068SAndroid Build Coastguard Worker Str.output();
267*76559068SAndroid Build Coastguard Worker }
268*76559068SAndroid Build Coastguard Worker
269*76559068SAndroid Build Coastguard Worker struct MapAllocatorCacheTest : public Test {
270*76559068SAndroid Build Coastguard Worker static constexpr scudo::u32 UnmappedMarker = 0xDEADBEEF;
271*76559068SAndroid Build Coastguard Worker
testUnmapCallbackMapAllocatorCacheTest272*76559068SAndroid Build Coastguard Worker static void testUnmapCallback(scudo::MemMapT &MemMap) {
273*76559068SAndroid Build Coastguard Worker scudo::u32 *Ptr = reinterpret_cast<scudo::u32 *>(MemMap.getBase());
274*76559068SAndroid Build Coastguard Worker *Ptr = UnmappedMarker;
275*76559068SAndroid Build Coastguard Worker }
276*76559068SAndroid Build Coastguard Worker
277*76559068SAndroid Build Coastguard Worker using SecondaryConfig = scudo::SecondaryConfig<TestConfig>;
278*76559068SAndroid Build Coastguard Worker using CacheConfig = SecondaryConfig::CacheConfig;
279*76559068SAndroid Build Coastguard Worker using CacheT = scudo::MapAllocatorCache<CacheConfig, testUnmapCallback>;
280*76559068SAndroid Build Coastguard Worker
281*76559068SAndroid Build Coastguard Worker std::unique_ptr<CacheT> Cache = std::make_unique<CacheT>();
282*76559068SAndroid Build Coastguard Worker
283*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSize = scudo::getPageSizeCached();
284*76559068SAndroid Build Coastguard Worker // The current test allocation size is set to the maximum
285*76559068SAndroid Build Coastguard Worker // cache entry size
286*76559068SAndroid Build Coastguard Worker static constexpr scudo::uptr TestAllocSize =
287*76559068SAndroid Build Coastguard Worker CacheConfig::getDefaultMaxEntrySize();
288*76559068SAndroid Build Coastguard Worker
289*76559068SAndroid Build Coastguard Worker scudo::Options Options = getOptionsForConfig<SecondaryConfig>();
290*76559068SAndroid Build Coastguard Worker
SetUpMapAllocatorCacheTest291*76559068SAndroid Build Coastguard Worker void SetUp() override { Cache->init(/*ReleaseToOsInterval=*/-1); }
292*76559068SAndroid Build Coastguard Worker
TearDownMapAllocatorCacheTest293*76559068SAndroid Build Coastguard Worker void TearDown() override { Cache->unmapTestOnly(); }
294*76559068SAndroid Build Coastguard Worker
allocateMapAllocatorCacheTest295*76559068SAndroid Build Coastguard Worker scudo::MemMapT allocate(scudo::uptr Size) {
296*76559068SAndroid Build Coastguard Worker scudo::uptr MapSize = scudo::roundUp(Size, PageSize);
297*76559068SAndroid Build Coastguard Worker scudo::ReservedMemoryT ReservedMemory;
298*76559068SAndroid Build Coastguard Worker CHECK(ReservedMemory.create(0U, MapSize, nullptr, MAP_ALLOWNOMEM));
299*76559068SAndroid Build Coastguard Worker
300*76559068SAndroid Build Coastguard Worker scudo::MemMapT MemMap = ReservedMemory.dispatch(
301*76559068SAndroid Build Coastguard Worker ReservedMemory.getBase(), ReservedMemory.getCapacity());
302*76559068SAndroid Build Coastguard Worker MemMap.remap(MemMap.getBase(), MemMap.getCapacity(), "scudo:test",
303*76559068SAndroid Build Coastguard Worker MAP_RESIZABLE | MAP_ALLOWNOMEM);
304*76559068SAndroid Build Coastguard Worker return MemMap;
305*76559068SAndroid Build Coastguard Worker }
306*76559068SAndroid Build Coastguard Worker
fillCacheWithSameSizeBlocksMapAllocatorCacheTest307*76559068SAndroid Build Coastguard Worker void fillCacheWithSameSizeBlocks(std::vector<scudo::MemMapT> &MemMaps,
308*76559068SAndroid Build Coastguard Worker scudo::uptr NumEntries, scudo::uptr Size) {
309*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < NumEntries; I++) {
310*76559068SAndroid Build Coastguard Worker MemMaps.emplace_back(allocate(Size));
311*76559068SAndroid Build Coastguard Worker auto &MemMap = MemMaps[I];
312*76559068SAndroid Build Coastguard Worker Cache->store(Options, MemMap.getBase(), MemMap.getCapacity(),
313*76559068SAndroid Build Coastguard Worker MemMap.getBase(), MemMap);
314*76559068SAndroid Build Coastguard Worker }
315*76559068SAndroid Build Coastguard Worker }
316*76559068SAndroid Build Coastguard Worker };
317*76559068SAndroid Build Coastguard Worker
TEST_F(MapAllocatorCacheTest,CacheOrder)318*76559068SAndroid Build Coastguard Worker TEST_F(MapAllocatorCacheTest, CacheOrder) {
319*76559068SAndroid Build Coastguard Worker std::vector<scudo::MemMapT> MemMaps;
320*76559068SAndroid Build Coastguard Worker Cache->setOption(scudo::Option::MaxCacheEntriesCount,
321*76559068SAndroid Build Coastguard Worker CacheConfig::getEntriesArraySize());
322*76559068SAndroid Build Coastguard Worker
323*76559068SAndroid Build Coastguard Worker fillCacheWithSameSizeBlocks(MemMaps, CacheConfig::getEntriesArraySize(),
324*76559068SAndroid Build Coastguard Worker TestAllocSize);
325*76559068SAndroid Build Coastguard Worker
326*76559068SAndroid Build Coastguard Worker // Retrieval order should be the inverse of insertion order
327*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = CacheConfig::getEntriesArraySize(); I > 0; I--) {
328*76559068SAndroid Build Coastguard Worker scudo::uptr EntryHeaderPos;
329*76559068SAndroid Build Coastguard Worker scudo::CachedBlock Entry =
330*76559068SAndroid Build Coastguard Worker Cache->retrieve(0, TestAllocSize, PageSize, 0, EntryHeaderPos);
331*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Entry.MemMap.getBase(), MemMaps[I - 1].getBase());
332*76559068SAndroid Build Coastguard Worker }
333*76559068SAndroid Build Coastguard Worker
334*76559068SAndroid Build Coastguard Worker // Clean up MemMaps
335*76559068SAndroid Build Coastguard Worker for (auto &MemMap : MemMaps)
336*76559068SAndroid Build Coastguard Worker MemMap.unmap();
337*76559068SAndroid Build Coastguard Worker }
338*76559068SAndroid Build Coastguard Worker
TEST_F(MapAllocatorCacheTest,PartialChunkHeuristicRetrievalTest)339*76559068SAndroid Build Coastguard Worker TEST_F(MapAllocatorCacheTest, PartialChunkHeuristicRetrievalTest) {
340*76559068SAndroid Build Coastguard Worker const scudo::uptr FragmentedPages =
341*76559068SAndroid Build Coastguard Worker 1 + scudo::CachedBlock::MaxReleasedCachePages;
342*76559068SAndroid Build Coastguard Worker scudo::uptr EntryHeaderPos;
343*76559068SAndroid Build Coastguard Worker scudo::CachedBlock Entry;
344*76559068SAndroid Build Coastguard Worker scudo::MemMapT MemMap = allocate(PageSize + FragmentedPages * PageSize);
345*76559068SAndroid Build Coastguard Worker Cache->store(Options, MemMap.getBase(), MemMap.getCapacity(),
346*76559068SAndroid Build Coastguard Worker MemMap.getBase(), MemMap);
347*76559068SAndroid Build Coastguard Worker
348*76559068SAndroid Build Coastguard Worker // FragmentedPages > MaxAllowedFragmentedPages so PageSize
349*76559068SAndroid Build Coastguard Worker // cannot be retrieved from the cache
350*76559068SAndroid Build Coastguard Worker Entry = Cache->retrieve(/*MaxAllowedFragmentedPages=*/0, PageSize, PageSize,
351*76559068SAndroid Build Coastguard Worker 0, EntryHeaderPos);
352*76559068SAndroid Build Coastguard Worker EXPECT_FALSE(Entry.isValid());
353*76559068SAndroid Build Coastguard Worker
354*76559068SAndroid Build Coastguard Worker // FragmentedPages == MaxAllowedFragmentedPages so PageSize
355*76559068SAndroid Build Coastguard Worker // can be retrieved from the cache
356*76559068SAndroid Build Coastguard Worker Entry =
357*76559068SAndroid Build Coastguard Worker Cache->retrieve(FragmentedPages, PageSize, PageSize, 0, EntryHeaderPos);
358*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Entry.isValid());
359*76559068SAndroid Build Coastguard Worker
360*76559068SAndroid Build Coastguard Worker MemMap.unmap();
361*76559068SAndroid Build Coastguard Worker }
362*76559068SAndroid Build Coastguard Worker
TEST_F(MapAllocatorCacheTest,MemoryLeakTest)363*76559068SAndroid Build Coastguard Worker TEST_F(MapAllocatorCacheTest, MemoryLeakTest) {
364*76559068SAndroid Build Coastguard Worker std::vector<scudo::MemMapT> MemMaps;
365*76559068SAndroid Build Coastguard Worker // Fill the cache above MaxEntriesCount to force an eviction
366*76559068SAndroid Build Coastguard Worker // The first cache entry should be evicted (because it is the oldest)
367*76559068SAndroid Build Coastguard Worker // due to the maximum number of entries being reached
368*76559068SAndroid Build Coastguard Worker fillCacheWithSameSizeBlocks(
369*76559068SAndroid Build Coastguard Worker MemMaps, CacheConfig::getDefaultMaxEntriesCount() + 1, TestAllocSize);
370*76559068SAndroid Build Coastguard Worker
371*76559068SAndroid Build Coastguard Worker std::vector<scudo::CachedBlock> RetrievedEntries;
372*76559068SAndroid Build Coastguard Worker
373*76559068SAndroid Build Coastguard Worker // First MemMap should be evicted from cache because it was the first
374*76559068SAndroid Build Coastguard Worker // inserted into the cache
375*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = CacheConfig::getDefaultMaxEntriesCount(); I > 0; I--) {
376*76559068SAndroid Build Coastguard Worker scudo::uptr EntryHeaderPos;
377*76559068SAndroid Build Coastguard Worker RetrievedEntries.push_back(
378*76559068SAndroid Build Coastguard Worker Cache->retrieve(0, TestAllocSize, PageSize, 0, EntryHeaderPos));
379*76559068SAndroid Build Coastguard Worker EXPECT_EQ(MemMaps[I].getBase(), RetrievedEntries.back().MemMap.getBase());
380*76559068SAndroid Build Coastguard Worker }
381*76559068SAndroid Build Coastguard Worker
382*76559068SAndroid Build Coastguard Worker // Evicted entry should be marked due to unmap callback
383*76559068SAndroid Build Coastguard Worker EXPECT_EQ(*reinterpret_cast<scudo::u32 *>(MemMaps[0].getBase()),
384*76559068SAndroid Build Coastguard Worker UnmappedMarker);
385*76559068SAndroid Build Coastguard Worker
386*76559068SAndroid Build Coastguard Worker // Clean up MemMaps
387*76559068SAndroid Build Coastguard Worker for (auto &MemMap : MemMaps)
388*76559068SAndroid Build Coastguard Worker MemMap.unmap();
389*76559068SAndroid Build Coastguard Worker }
390