1*76559068SAndroid Build Coastguard Worker //===-- release_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 "tests/scudo_unit_test.h"
10*76559068SAndroid Build Coastguard Worker
11*76559068SAndroid Build Coastguard Worker #include "list.h"
12*76559068SAndroid Build Coastguard Worker #include "release.h"
13*76559068SAndroid Build Coastguard Worker #include "size_class_map.h"
14*76559068SAndroid Build Coastguard Worker
15*76559068SAndroid Build Coastguard Worker #include <string.h>
16*76559068SAndroid Build Coastguard Worker
17*76559068SAndroid Build Coastguard Worker #include <algorithm>
18*76559068SAndroid Build Coastguard Worker #include <random>
19*76559068SAndroid Build Coastguard Worker #include <set>
20*76559068SAndroid Build Coastguard Worker
TEST(ScudoReleaseTest,RegionPageMap)21*76559068SAndroid Build Coastguard Worker TEST(ScudoReleaseTest, RegionPageMap) {
22*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < SCUDO_WORDSIZE; I++) {
23*76559068SAndroid Build Coastguard Worker // Various valid counter's max values packed into one word.
24*76559068SAndroid Build Coastguard Worker scudo::RegionPageMap PageMap2N(1U, 1U, 1UL << I);
25*76559068SAndroid Build Coastguard Worker ASSERT_TRUE(PageMap2N.isAllocated());
26*76559068SAndroid Build Coastguard Worker EXPECT_EQ(1U, PageMap2N.getBufferNumElements());
27*76559068SAndroid Build Coastguard Worker // Check the "all bit set" values too.
28*76559068SAndroid Build Coastguard Worker scudo::RegionPageMap PageMap2N1_1(1U, 1U, ~0UL >> I);
29*76559068SAndroid Build Coastguard Worker ASSERT_TRUE(PageMap2N1_1.isAllocated());
30*76559068SAndroid Build Coastguard Worker EXPECT_EQ(1U, PageMap2N1_1.getBufferNumElements());
31*76559068SAndroid Build Coastguard Worker // Verify the packing ratio, the counter is Expected to be packed into the
32*76559068SAndroid Build Coastguard Worker // closest power of 2 bits.
33*76559068SAndroid Build Coastguard Worker scudo::RegionPageMap PageMap(1U, SCUDO_WORDSIZE, 1UL << I);
34*76559068SAndroid Build Coastguard Worker ASSERT_TRUE(PageMap.isAllocated());
35*76559068SAndroid Build Coastguard Worker EXPECT_EQ(scudo::roundUpPowerOfTwo(I + 1), PageMap.getBufferNumElements());
36*76559068SAndroid Build Coastguard Worker }
37*76559068SAndroid Build Coastguard Worker
38*76559068SAndroid Build Coastguard Worker // Go through 1, 2, 4, 8, .. {32,64} bits per counter.
39*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; (SCUDO_WORDSIZE >> I) != 0; I++) {
40*76559068SAndroid Build Coastguard Worker // Make sure counters request one memory page for the buffer.
41*76559068SAndroid Build Coastguard Worker const scudo::uptr NumCounters =
42*76559068SAndroid Build Coastguard Worker (scudo::getPageSizeCached() / 8) * (SCUDO_WORDSIZE >> I);
43*76559068SAndroid Build Coastguard Worker scudo::RegionPageMap PageMap(1U, NumCounters,
44*76559068SAndroid Build Coastguard Worker 1UL << ((1UL << I) - 1));
45*76559068SAndroid Build Coastguard Worker ASSERT_TRUE(PageMap.isAllocated());
46*76559068SAndroid Build Coastguard Worker PageMap.inc(0U, 0U);
47*76559068SAndroid Build Coastguard Worker for (scudo::uptr C = 1; C < NumCounters - 1; C++) {
48*76559068SAndroid Build Coastguard Worker EXPECT_EQ(0UL, PageMap.get(0U, C));
49*76559068SAndroid Build Coastguard Worker PageMap.inc(0U, C);
50*76559068SAndroid Build Coastguard Worker EXPECT_EQ(1UL, PageMap.get(0U, C - 1));
51*76559068SAndroid Build Coastguard Worker }
52*76559068SAndroid Build Coastguard Worker EXPECT_EQ(0UL, PageMap.get(0U, NumCounters - 1));
53*76559068SAndroid Build Coastguard Worker PageMap.inc(0U, NumCounters - 1);
54*76559068SAndroid Build Coastguard Worker if (I > 0) {
55*76559068SAndroid Build Coastguard Worker PageMap.incRange(0u, 0U, NumCounters - 1);
56*76559068SAndroid Build Coastguard Worker for (scudo::uptr C = 0; C < NumCounters; C++)
57*76559068SAndroid Build Coastguard Worker EXPECT_EQ(2UL, PageMap.get(0U, C));
58*76559068SAndroid Build Coastguard Worker }
59*76559068SAndroid Build Coastguard Worker }
60*76559068SAndroid Build Coastguard Worker
61*76559068SAndroid Build Coastguard Worker // Similar to the above except that we are using incN().
62*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; (SCUDO_WORDSIZE >> I) != 0; I++) {
63*76559068SAndroid Build Coastguard Worker // Make sure counters request one memory page for the buffer.
64*76559068SAndroid Build Coastguard Worker const scudo::uptr NumCounters =
65*76559068SAndroid Build Coastguard Worker (scudo::getPageSizeCached() / 8) * (SCUDO_WORDSIZE >> I);
66*76559068SAndroid Build Coastguard Worker scudo::uptr MaxValue = 1UL << ((1UL << I) - 1);
67*76559068SAndroid Build Coastguard Worker if (MaxValue <= 1U)
68*76559068SAndroid Build Coastguard Worker continue;
69*76559068SAndroid Build Coastguard Worker
70*76559068SAndroid Build Coastguard Worker scudo::RegionPageMap PageMap(1U, NumCounters, MaxValue);
71*76559068SAndroid Build Coastguard Worker
72*76559068SAndroid Build Coastguard Worker scudo::uptr N = MaxValue / 2;
73*76559068SAndroid Build Coastguard Worker PageMap.incN(0U, 0, N);
74*76559068SAndroid Build Coastguard Worker for (scudo::uptr C = 1; C < NumCounters; C++) {
75*76559068SAndroid Build Coastguard Worker EXPECT_EQ(0UL, PageMap.get(0U, C));
76*76559068SAndroid Build Coastguard Worker PageMap.incN(0U, C, N);
77*76559068SAndroid Build Coastguard Worker EXPECT_EQ(N, PageMap.get(0U, C - 1));
78*76559068SAndroid Build Coastguard Worker }
79*76559068SAndroid Build Coastguard Worker EXPECT_EQ(N, PageMap.get(0U, NumCounters - 1));
80*76559068SAndroid Build Coastguard Worker }
81*76559068SAndroid Build Coastguard Worker }
82*76559068SAndroid Build Coastguard Worker
83*76559068SAndroid Build Coastguard Worker class StringRangeRecorder {
84*76559068SAndroid Build Coastguard Worker public:
85*76559068SAndroid Build Coastguard Worker std::string ReportedPages;
86*76559068SAndroid Build Coastguard Worker
StringRangeRecorder()87*76559068SAndroid Build Coastguard Worker StringRangeRecorder()
88*76559068SAndroid Build Coastguard Worker : PageSizeScaledLog(scudo::getLog2(scudo::getPageSizeCached())) {}
89*76559068SAndroid Build Coastguard Worker
releasePageRangeToOS(scudo::uptr From,scudo::uptr To)90*76559068SAndroid Build Coastguard Worker void releasePageRangeToOS(scudo::uptr From, scudo::uptr To) {
91*76559068SAndroid Build Coastguard Worker From >>= PageSizeScaledLog;
92*76559068SAndroid Build Coastguard Worker To >>= PageSizeScaledLog;
93*76559068SAndroid Build Coastguard Worker EXPECT_LT(From, To);
94*76559068SAndroid Build Coastguard Worker if (!ReportedPages.empty())
95*76559068SAndroid Build Coastguard Worker EXPECT_LT(LastPageReported, From);
96*76559068SAndroid Build Coastguard Worker ReportedPages.append(From - LastPageReported, '.');
97*76559068SAndroid Build Coastguard Worker ReportedPages.append(To - From, 'x');
98*76559068SAndroid Build Coastguard Worker LastPageReported = To;
99*76559068SAndroid Build Coastguard Worker }
100*76559068SAndroid Build Coastguard Worker
101*76559068SAndroid Build Coastguard Worker private:
102*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSizeScaledLog;
103*76559068SAndroid Build Coastguard Worker scudo::uptr LastPageReported = 0;
104*76559068SAndroid Build Coastguard Worker };
105*76559068SAndroid Build Coastguard Worker
TEST(ScudoReleaseTest,FreePagesRangeTracker)106*76559068SAndroid Build Coastguard Worker TEST(ScudoReleaseTest, FreePagesRangeTracker) {
107*76559068SAndroid Build Coastguard Worker // 'x' denotes a page to be released, '.' denotes a page to be kept around.
108*76559068SAndroid Build Coastguard Worker const char *TestCases[] = {
109*76559068SAndroid Build Coastguard Worker "",
110*76559068SAndroid Build Coastguard Worker ".",
111*76559068SAndroid Build Coastguard Worker "x",
112*76559068SAndroid Build Coastguard Worker "........",
113*76559068SAndroid Build Coastguard Worker "xxxxxxxxxxx",
114*76559068SAndroid Build Coastguard Worker "..............xxxxx",
115*76559068SAndroid Build Coastguard Worker "xxxxxxxxxxxxxxxxxx.....",
116*76559068SAndroid Build Coastguard Worker "......xxxxxxxx........",
117*76559068SAndroid Build Coastguard Worker "xxx..........xxxxxxxxxxxxxxx",
118*76559068SAndroid Build Coastguard Worker "......xxxx....xxxx........",
119*76559068SAndroid Build Coastguard Worker "xxx..........xxxxxxxx....xxxxxxx",
120*76559068SAndroid Build Coastguard Worker "x.x.x.x.x.x.x.x.x.x.x.x.",
121*76559068SAndroid Build Coastguard Worker ".x.x.x.x.x.x.x.x.x.x.x.x",
122*76559068SAndroid Build Coastguard Worker ".x.x.x.x.x.x.x.x.x.x.x.x.",
123*76559068SAndroid Build Coastguard Worker "x.x.x.x.x.x.x.x.x.x.x.x.x",
124*76559068SAndroid Build Coastguard Worker };
125*76559068SAndroid Build Coastguard Worker typedef scudo::FreePagesRangeTracker<StringRangeRecorder> RangeTracker;
126*76559068SAndroid Build Coastguard Worker
127*76559068SAndroid Build Coastguard Worker for (auto TestCase : TestCases) {
128*76559068SAndroid Build Coastguard Worker StringRangeRecorder Recorder;
129*76559068SAndroid Build Coastguard Worker RangeTracker Tracker(Recorder);
130*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; TestCase[I] != 0; I++)
131*76559068SAndroid Build Coastguard Worker Tracker.processNextPage(TestCase[I] == 'x');
132*76559068SAndroid Build Coastguard Worker Tracker.finish();
133*76559068SAndroid Build Coastguard Worker // Strip trailing '.'-pages before comparing the results as they are not
134*76559068SAndroid Build Coastguard Worker // going to be reported to range_recorder anyway.
135*76559068SAndroid Build Coastguard Worker const char *LastX = strrchr(TestCase, 'x');
136*76559068SAndroid Build Coastguard Worker std::string Expected(
137*76559068SAndroid Build Coastguard Worker TestCase,
138*76559068SAndroid Build Coastguard Worker LastX == nullptr ? 0U : static_cast<size_t>(LastX - TestCase + 1));
139*76559068SAndroid Build Coastguard Worker EXPECT_STREQ(Expected.c_str(), Recorder.ReportedPages.c_str());
140*76559068SAndroid Build Coastguard Worker }
141*76559068SAndroid Build Coastguard Worker }
142*76559068SAndroid Build Coastguard Worker
143*76559068SAndroid Build Coastguard Worker class ReleasedPagesRecorder {
144*76559068SAndroid Build Coastguard Worker public:
145*76559068SAndroid Build Coastguard Worker ReleasedPagesRecorder() = default;
ReleasedPagesRecorder(scudo::uptr Base)146*76559068SAndroid Build Coastguard Worker explicit ReleasedPagesRecorder(scudo::uptr Base) : Base(Base) {}
147*76559068SAndroid Build Coastguard Worker std::set<scudo::uptr> ReportedPages;
148*76559068SAndroid Build Coastguard Worker
releasePageRangeToOS(scudo::uptr From,scudo::uptr To)149*76559068SAndroid Build Coastguard Worker void releasePageRangeToOS(scudo::uptr From, scudo::uptr To) {
150*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSize = scudo::getPageSizeCached();
151*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = From; I < To; I += PageSize)
152*76559068SAndroid Build Coastguard Worker ReportedPages.insert(I + getBase());
153*76559068SAndroid Build Coastguard Worker }
154*76559068SAndroid Build Coastguard Worker
getBase() const155*76559068SAndroid Build Coastguard Worker scudo::uptr getBase() const { return Base; }
156*76559068SAndroid Build Coastguard Worker scudo::uptr Base = 0;
157*76559068SAndroid Build Coastguard Worker };
158*76559068SAndroid Build Coastguard Worker
159*76559068SAndroid Build Coastguard Worker // Simplified version of a TransferBatch.
160*76559068SAndroid Build Coastguard Worker template <class SizeClassMap> struct FreeBatch {
161*76559068SAndroid Build Coastguard Worker static const scudo::u16 MaxCount = SizeClassMap::MaxNumCachedHint;
clearFreeBatch162*76559068SAndroid Build Coastguard Worker void clear() { Count = 0; }
addFreeBatch163*76559068SAndroid Build Coastguard Worker void add(scudo::uptr P) {
164*76559068SAndroid Build Coastguard Worker DCHECK_LT(Count, MaxCount);
165*76559068SAndroid Build Coastguard Worker Batch[Count++] = P;
166*76559068SAndroid Build Coastguard Worker }
getCountFreeBatch167*76559068SAndroid Build Coastguard Worker scudo::u16 getCount() const { return Count; }
getFreeBatch168*76559068SAndroid Build Coastguard Worker scudo::uptr get(scudo::u16 I) const {
169*76559068SAndroid Build Coastguard Worker DCHECK_LE(I, Count);
170*76559068SAndroid Build Coastguard Worker return Batch[I];
171*76559068SAndroid Build Coastguard Worker }
172*76559068SAndroid Build Coastguard Worker FreeBatch *Next;
173*76559068SAndroid Build Coastguard Worker
174*76559068SAndroid Build Coastguard Worker private:
175*76559068SAndroid Build Coastguard Worker scudo::uptr Batch[MaxCount];
176*76559068SAndroid Build Coastguard Worker scudo::u16 Count;
177*76559068SAndroid Build Coastguard Worker };
178*76559068SAndroid Build Coastguard Worker
testReleaseFreeMemoryToOS()179*76559068SAndroid Build Coastguard Worker template <class SizeClassMap> void testReleaseFreeMemoryToOS() {
180*76559068SAndroid Build Coastguard Worker typedef FreeBatch<SizeClassMap> Batch;
181*76559068SAndroid Build Coastguard Worker const scudo::uptr PagesCount = 1024;
182*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSize = scudo::getPageSizeCached();
183*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSizeLog = scudo::getLog2(PageSize);
184*76559068SAndroid Build Coastguard Worker std::mt19937 R;
185*76559068SAndroid Build Coastguard Worker scudo::u32 RandState = 42;
186*76559068SAndroid Build Coastguard Worker
187*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 1; I <= SizeClassMap::LargestClassId; I++) {
188*76559068SAndroid Build Coastguard Worker const scudo::uptr BlockSize = SizeClassMap::getSizeByClassId(I);
189*76559068SAndroid Build Coastguard Worker const scudo::uptr MaxBlocks = PagesCount * PageSize / BlockSize;
190*76559068SAndroid Build Coastguard Worker
191*76559068SAndroid Build Coastguard Worker // Generate the random free list.
192*76559068SAndroid Build Coastguard Worker std::vector<scudo::uptr> FreeArray;
193*76559068SAndroid Build Coastguard Worker bool InFreeRange = false;
194*76559068SAndroid Build Coastguard Worker scudo::uptr CurrentRangeEnd = 0;
195*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < MaxBlocks; I++) {
196*76559068SAndroid Build Coastguard Worker if (I == CurrentRangeEnd) {
197*76559068SAndroid Build Coastguard Worker InFreeRange = (scudo::getRandomU32(&RandState) & 1U) == 1;
198*76559068SAndroid Build Coastguard Worker CurrentRangeEnd += (scudo::getRandomU32(&RandState) & 0x7f) + 1;
199*76559068SAndroid Build Coastguard Worker }
200*76559068SAndroid Build Coastguard Worker if (InFreeRange)
201*76559068SAndroid Build Coastguard Worker FreeArray.push_back(I * BlockSize);
202*76559068SAndroid Build Coastguard Worker }
203*76559068SAndroid Build Coastguard Worker if (FreeArray.empty())
204*76559068SAndroid Build Coastguard Worker continue;
205*76559068SAndroid Build Coastguard Worker // Shuffle the array to ensure that the order is irrelevant.
206*76559068SAndroid Build Coastguard Worker std::shuffle(FreeArray.begin(), FreeArray.end(), R);
207*76559068SAndroid Build Coastguard Worker
208*76559068SAndroid Build Coastguard Worker // Build the FreeList from the FreeArray.
209*76559068SAndroid Build Coastguard Worker scudo::SinglyLinkedList<Batch> FreeList;
210*76559068SAndroid Build Coastguard Worker FreeList.clear();
211*76559068SAndroid Build Coastguard Worker Batch *CurrentBatch = nullptr;
212*76559068SAndroid Build Coastguard Worker for (auto const &Block : FreeArray) {
213*76559068SAndroid Build Coastguard Worker if (!CurrentBatch) {
214*76559068SAndroid Build Coastguard Worker CurrentBatch = new Batch;
215*76559068SAndroid Build Coastguard Worker CurrentBatch->clear();
216*76559068SAndroid Build Coastguard Worker FreeList.push_back(CurrentBatch);
217*76559068SAndroid Build Coastguard Worker }
218*76559068SAndroid Build Coastguard Worker CurrentBatch->add(Block);
219*76559068SAndroid Build Coastguard Worker if (CurrentBatch->getCount() == Batch::MaxCount)
220*76559068SAndroid Build Coastguard Worker CurrentBatch = nullptr;
221*76559068SAndroid Build Coastguard Worker }
222*76559068SAndroid Build Coastguard Worker
223*76559068SAndroid Build Coastguard Worker // Release the memory.
224*76559068SAndroid Build Coastguard Worker auto SkipRegion = [](UNUSED scudo::uptr RegionIndex) { return false; };
225*76559068SAndroid Build Coastguard Worker auto DecompactPtr = [](scudo::uptr P) { return P; };
226*76559068SAndroid Build Coastguard Worker ReleasedPagesRecorder Recorder;
227*76559068SAndroid Build Coastguard Worker scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
228*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/MaxBlocks * BlockSize);
229*76559068SAndroid Build Coastguard Worker ASSERT_FALSE(Context.hasBlockMarked());
230*76559068SAndroid Build Coastguard Worker Context.markFreeBlocksInRegion(FreeList, DecompactPtr, Recorder.getBase(),
231*76559068SAndroid Build Coastguard Worker /*RegionIndex=*/0, MaxBlocks * BlockSize,
232*76559068SAndroid Build Coastguard Worker /*MayContainLastBlockInRegion=*/true);
233*76559068SAndroid Build Coastguard Worker ASSERT_TRUE(Context.hasBlockMarked());
234*76559068SAndroid Build Coastguard Worker releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
235*76559068SAndroid Build Coastguard Worker scudo::RegionPageMap &PageMap = Context.PageMap;
236*76559068SAndroid Build Coastguard Worker
237*76559068SAndroid Build Coastguard Worker // Verify that there are no released pages touched by used chunks and all
238*76559068SAndroid Build Coastguard Worker // ranges of free chunks big enough to contain the entire memory pages had
239*76559068SAndroid Build Coastguard Worker // these pages released.
240*76559068SAndroid Build Coastguard Worker scudo::uptr VerifiedReleasedPages = 0;
241*76559068SAndroid Build Coastguard Worker std::set<scudo::uptr> FreeBlocks(FreeArray.begin(), FreeArray.end());
242*76559068SAndroid Build Coastguard Worker
243*76559068SAndroid Build Coastguard Worker scudo::uptr CurrentBlock = 0;
244*76559068SAndroid Build Coastguard Worker InFreeRange = false;
245*76559068SAndroid Build Coastguard Worker scudo::uptr CurrentFreeRangeStart = 0;
246*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < MaxBlocks; I++) {
247*76559068SAndroid Build Coastguard Worker const bool IsFreeBlock =
248*76559068SAndroid Build Coastguard Worker FreeBlocks.find(CurrentBlock) != FreeBlocks.end();
249*76559068SAndroid Build Coastguard Worker if (IsFreeBlock) {
250*76559068SAndroid Build Coastguard Worker if (!InFreeRange) {
251*76559068SAndroid Build Coastguard Worker InFreeRange = true;
252*76559068SAndroid Build Coastguard Worker CurrentFreeRangeStart = CurrentBlock;
253*76559068SAndroid Build Coastguard Worker }
254*76559068SAndroid Build Coastguard Worker } else {
255*76559068SAndroid Build Coastguard Worker // Verify that this used chunk does not touch any released page.
256*76559068SAndroid Build Coastguard Worker const scudo::uptr StartPage = CurrentBlock / PageSize;
257*76559068SAndroid Build Coastguard Worker const scudo::uptr EndPage = (CurrentBlock + BlockSize - 1) / PageSize;
258*76559068SAndroid Build Coastguard Worker for (scudo::uptr J = StartPage; J <= EndPage; J++) {
259*76559068SAndroid Build Coastguard Worker const bool PageReleased = Recorder.ReportedPages.find(J * PageSize) !=
260*76559068SAndroid Build Coastguard Worker Recorder.ReportedPages.end();
261*76559068SAndroid Build Coastguard Worker EXPECT_EQ(false, PageReleased);
262*76559068SAndroid Build Coastguard Worker EXPECT_EQ(false,
263*76559068SAndroid Build Coastguard Worker PageMap.isAllCounted(0, (J * PageSize) >> PageSizeLog));
264*76559068SAndroid Build Coastguard Worker }
265*76559068SAndroid Build Coastguard Worker
266*76559068SAndroid Build Coastguard Worker if (InFreeRange) {
267*76559068SAndroid Build Coastguard Worker InFreeRange = false;
268*76559068SAndroid Build Coastguard Worker // Verify that all entire memory pages covered by this range of free
269*76559068SAndroid Build Coastguard Worker // chunks were released.
270*76559068SAndroid Build Coastguard Worker scudo::uptr P = scudo::roundUp(CurrentFreeRangeStart, PageSize);
271*76559068SAndroid Build Coastguard Worker while (P + PageSize <= CurrentBlock) {
272*76559068SAndroid Build Coastguard Worker const bool PageReleased =
273*76559068SAndroid Build Coastguard Worker Recorder.ReportedPages.find(P) != Recorder.ReportedPages.end();
274*76559068SAndroid Build Coastguard Worker EXPECT_EQ(true, PageReleased);
275*76559068SAndroid Build Coastguard Worker EXPECT_EQ(true, PageMap.isAllCounted(0, P >> PageSizeLog));
276*76559068SAndroid Build Coastguard Worker VerifiedReleasedPages++;
277*76559068SAndroid Build Coastguard Worker P += PageSize;
278*76559068SAndroid Build Coastguard Worker }
279*76559068SAndroid Build Coastguard Worker }
280*76559068SAndroid Build Coastguard Worker }
281*76559068SAndroid Build Coastguard Worker
282*76559068SAndroid Build Coastguard Worker CurrentBlock += BlockSize;
283*76559068SAndroid Build Coastguard Worker }
284*76559068SAndroid Build Coastguard Worker
285*76559068SAndroid Build Coastguard Worker if (InFreeRange) {
286*76559068SAndroid Build Coastguard Worker scudo::uptr P = scudo::roundUp(CurrentFreeRangeStart, PageSize);
287*76559068SAndroid Build Coastguard Worker const scudo::uptr EndPage =
288*76559068SAndroid Build Coastguard Worker scudo::roundUp(MaxBlocks * BlockSize, PageSize);
289*76559068SAndroid Build Coastguard Worker while (P + PageSize <= EndPage) {
290*76559068SAndroid Build Coastguard Worker const bool PageReleased =
291*76559068SAndroid Build Coastguard Worker Recorder.ReportedPages.find(P) != Recorder.ReportedPages.end();
292*76559068SAndroid Build Coastguard Worker EXPECT_EQ(true, PageReleased);
293*76559068SAndroid Build Coastguard Worker EXPECT_EQ(true, PageMap.isAllCounted(0, P >> PageSizeLog));
294*76559068SAndroid Build Coastguard Worker VerifiedReleasedPages++;
295*76559068SAndroid Build Coastguard Worker P += PageSize;
296*76559068SAndroid Build Coastguard Worker }
297*76559068SAndroid Build Coastguard Worker }
298*76559068SAndroid Build Coastguard Worker
299*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Recorder.ReportedPages.size(), VerifiedReleasedPages);
300*76559068SAndroid Build Coastguard Worker
301*76559068SAndroid Build Coastguard Worker while (!FreeList.empty()) {
302*76559068SAndroid Build Coastguard Worker CurrentBatch = FreeList.front();
303*76559068SAndroid Build Coastguard Worker FreeList.pop_front();
304*76559068SAndroid Build Coastguard Worker delete CurrentBatch;
305*76559068SAndroid Build Coastguard Worker }
306*76559068SAndroid Build Coastguard Worker }
307*76559068SAndroid Build Coastguard Worker }
308*76559068SAndroid Build Coastguard Worker
testPageMapMarkRange()309*76559068SAndroid Build Coastguard Worker template <class SizeClassMap> void testPageMapMarkRange() {
310*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSize = scudo::getPageSizeCached();
311*76559068SAndroid Build Coastguard Worker
312*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 1; I <= SizeClassMap::LargestClassId; I++) {
313*76559068SAndroid Build Coastguard Worker const scudo::uptr BlockSize = SizeClassMap::getSizeByClassId(I);
314*76559068SAndroid Build Coastguard Worker
315*76559068SAndroid Build Coastguard Worker const scudo::uptr GroupNum = 2;
316*76559068SAndroid Build Coastguard Worker const scudo::uptr GroupSize = scudo::roundUp(BlockSize, PageSize) * 2;
317*76559068SAndroid Build Coastguard Worker const scudo::uptr RegionSize =
318*76559068SAndroid Build Coastguard Worker scudo::roundUpSlow(GroupSize * GroupNum, BlockSize);
319*76559068SAndroid Build Coastguard Worker const scudo::uptr RoundedRegionSize = scudo::roundUp(RegionSize, PageSize);
320*76559068SAndroid Build Coastguard Worker
321*76559068SAndroid Build Coastguard Worker std::vector<scudo::uptr> Pages(RoundedRegionSize / PageSize, 0);
322*76559068SAndroid Build Coastguard Worker for (scudo::uptr Block = 0; Block < RoundedRegionSize; Block += BlockSize) {
323*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = Block / PageSize;
324*76559068SAndroid Build Coastguard Worker Page <= (Block + BlockSize - 1) / PageSize &&
325*76559068SAndroid Build Coastguard Worker Page < RoundedRegionSize / PageSize;
326*76559068SAndroid Build Coastguard Worker ++Page) {
327*76559068SAndroid Build Coastguard Worker ASSERT_LT(Page, Pages.size());
328*76559068SAndroid Build Coastguard Worker ++Pages[Page];
329*76559068SAndroid Build Coastguard Worker }
330*76559068SAndroid Build Coastguard Worker }
331*76559068SAndroid Build Coastguard Worker
332*76559068SAndroid Build Coastguard Worker for (scudo::uptr GroupId = 0; GroupId < GroupNum; ++GroupId) {
333*76559068SAndroid Build Coastguard Worker const scudo::uptr GroupBeg = GroupId * GroupSize;
334*76559068SAndroid Build Coastguard Worker const scudo::uptr GroupEnd = GroupBeg + GroupSize;
335*76559068SAndroid Build Coastguard Worker
336*76559068SAndroid Build Coastguard Worker scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
337*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/RegionSize);
338*76559068SAndroid Build Coastguard Worker Context.markRangeAsAllCounted(GroupBeg, GroupEnd, /*Base=*/0U,
339*76559068SAndroid Build Coastguard Worker /*RegionIndex=*/0, RegionSize);
340*76559068SAndroid Build Coastguard Worker
341*76559068SAndroid Build Coastguard Worker scudo::uptr FirstBlock =
342*76559068SAndroid Build Coastguard Worker ((GroupBeg + BlockSize - 1) / BlockSize) * BlockSize;
343*76559068SAndroid Build Coastguard Worker
344*76559068SAndroid Build Coastguard Worker // All the pages before first block page are not supposed to be marked.
345*76559068SAndroid Build Coastguard Worker if (FirstBlock / PageSize > 0) {
346*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = 0; Page <= FirstBlock / PageSize - 1; ++Page)
347*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Context.PageMap.get(/*Region=*/0, Page), 0U);
348*76559068SAndroid Build Coastguard Worker }
349*76559068SAndroid Build Coastguard Worker
350*76559068SAndroid Build Coastguard Worker // Verify the pages used by the blocks in the group except that if the
351*76559068SAndroid Build Coastguard Worker // end of the last block is not aligned with `GroupEnd`, it'll be verified
352*76559068SAndroid Build Coastguard Worker // later.
353*76559068SAndroid Build Coastguard Worker scudo::uptr Block;
354*76559068SAndroid Build Coastguard Worker for (Block = FirstBlock; Block + BlockSize <= GroupEnd;
355*76559068SAndroid Build Coastguard Worker Block += BlockSize) {
356*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = Block / PageSize;
357*76559068SAndroid Build Coastguard Worker Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
358*76559068SAndroid Build Coastguard Worker // First used page in the group has two cases, which are w/ and w/o
359*76559068SAndroid Build Coastguard Worker // block sitting across the boundary.
360*76559068SAndroid Build Coastguard Worker if (Page == FirstBlock / PageSize) {
361*76559068SAndroid Build Coastguard Worker if (FirstBlock % PageSize == 0) {
362*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0U, Page));
363*76559068SAndroid Build Coastguard Worker } else {
364*76559068SAndroid Build Coastguard Worker // There's a block straddling `GroupBeg`, it's supposed to only
365*76559068SAndroid Build Coastguard Worker // increment the counter and we expect it should be 1 less
366*76559068SAndroid Build Coastguard Worker // (exclude the straddling one) than the total blocks on the page.
367*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Context.PageMap.get(/*Region=*/0U, Page),
368*76559068SAndroid Build Coastguard Worker Pages[Page] - 1);
369*76559068SAndroid Build Coastguard Worker }
370*76559068SAndroid Build Coastguard Worker } else {
371*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0, Page));
372*76559068SAndroid Build Coastguard Worker }
373*76559068SAndroid Build Coastguard Worker }
374*76559068SAndroid Build Coastguard Worker }
375*76559068SAndroid Build Coastguard Worker
376*76559068SAndroid Build Coastguard Worker if (Block == GroupEnd)
377*76559068SAndroid Build Coastguard Worker continue;
378*76559068SAndroid Build Coastguard Worker
379*76559068SAndroid Build Coastguard Worker // Examine the last block which sits across the group boundary.
380*76559068SAndroid Build Coastguard Worker if (Block + BlockSize == RegionSize) {
381*76559068SAndroid Build Coastguard Worker // This is the last block in the region, it's supposed to mark all the
382*76559068SAndroid Build Coastguard Worker // pages as all counted.
383*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = Block / PageSize;
384*76559068SAndroid Build Coastguard Worker Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
385*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0, Page));
386*76559068SAndroid Build Coastguard Worker }
387*76559068SAndroid Build Coastguard Worker } else {
388*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = Block / PageSize;
389*76559068SAndroid Build Coastguard Worker Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
390*76559068SAndroid Build Coastguard Worker if (Page <= (GroupEnd - 1) / PageSize)
391*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0, Page));
392*76559068SAndroid Build Coastguard Worker else
393*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Context.PageMap.get(/*Region=*/0U, Page), 1U);
394*76559068SAndroid Build Coastguard Worker }
395*76559068SAndroid Build Coastguard Worker }
396*76559068SAndroid Build Coastguard Worker
397*76559068SAndroid Build Coastguard Worker const scudo::uptr FirstUncountedPage =
398*76559068SAndroid Build Coastguard Worker scudo::roundUp(Block + BlockSize, PageSize);
399*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = FirstUncountedPage;
400*76559068SAndroid Build Coastguard Worker Page <= RoundedRegionSize / PageSize; ++Page) {
401*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Context.PageMap.get(/*Region=*/0U, Page), 0U);
402*76559068SAndroid Build Coastguard Worker }
403*76559068SAndroid Build Coastguard Worker } // Iterate each Group
404*76559068SAndroid Build Coastguard Worker
405*76559068SAndroid Build Coastguard Worker // Release the entire region. This is to ensure the last page is counted.
406*76559068SAndroid Build Coastguard Worker scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
407*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/RegionSize);
408*76559068SAndroid Build Coastguard Worker Context.markRangeAsAllCounted(/*From=*/0U, /*To=*/RegionSize, /*Base=*/0,
409*76559068SAndroid Build Coastguard Worker /*RegionIndex=*/0, RegionSize);
410*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = 0; Page < RoundedRegionSize / PageSize; ++Page)
411*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0, Page));
412*76559068SAndroid Build Coastguard Worker } // Iterate each size class
413*76559068SAndroid Build Coastguard Worker }
414*76559068SAndroid Build Coastguard Worker
testReleasePartialRegion()415*76559068SAndroid Build Coastguard Worker template <class SizeClassMap> void testReleasePartialRegion() {
416*76559068SAndroid Build Coastguard Worker typedef FreeBatch<SizeClassMap> Batch;
417*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSize = scudo::getPageSizeCached();
418*76559068SAndroid Build Coastguard Worker
419*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 1; I <= SizeClassMap::LargestClassId; I++) {
420*76559068SAndroid Build Coastguard Worker // In the following, we want to ensure the region includes at least 2 pages
421*76559068SAndroid Build Coastguard Worker // and we will release all the pages except the first one. The handling of
422*76559068SAndroid Build Coastguard Worker // the last block is tricky, so we always test the case that includes the
423*76559068SAndroid Build Coastguard Worker // last block.
424*76559068SAndroid Build Coastguard Worker const scudo::uptr BlockSize = SizeClassMap::getSizeByClassId(I);
425*76559068SAndroid Build Coastguard Worker const scudo::uptr ReleaseBase = scudo::roundUp(BlockSize, PageSize);
426*76559068SAndroid Build Coastguard Worker const scudo::uptr BasePageOffset = ReleaseBase / PageSize;
427*76559068SAndroid Build Coastguard Worker const scudo::uptr RegionSize =
428*76559068SAndroid Build Coastguard Worker scudo::roundUpSlow(scudo::roundUp(BlockSize, PageSize) + ReleaseBase,
429*76559068SAndroid Build Coastguard Worker BlockSize) +
430*76559068SAndroid Build Coastguard Worker BlockSize;
431*76559068SAndroid Build Coastguard Worker const scudo::uptr RoundedRegionSize = scudo::roundUp(RegionSize, PageSize);
432*76559068SAndroid Build Coastguard Worker
433*76559068SAndroid Build Coastguard Worker scudo::SinglyLinkedList<Batch> FreeList;
434*76559068SAndroid Build Coastguard Worker FreeList.clear();
435*76559068SAndroid Build Coastguard Worker
436*76559068SAndroid Build Coastguard Worker // Skip the blocks in the first page and add the remaining.
437*76559068SAndroid Build Coastguard Worker std::vector<scudo::uptr> Pages(RoundedRegionSize / PageSize, 0);
438*76559068SAndroid Build Coastguard Worker for (scudo::uptr Block = scudo::roundUpSlow(ReleaseBase, BlockSize);
439*76559068SAndroid Build Coastguard Worker Block + BlockSize <= RoundedRegionSize; Block += BlockSize) {
440*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = Block / PageSize;
441*76559068SAndroid Build Coastguard Worker Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
442*76559068SAndroid Build Coastguard Worker ASSERT_LT(Page, Pages.size());
443*76559068SAndroid Build Coastguard Worker ++Pages[Page];
444*76559068SAndroid Build Coastguard Worker }
445*76559068SAndroid Build Coastguard Worker }
446*76559068SAndroid Build Coastguard Worker
447*76559068SAndroid Build Coastguard Worker // This follows the logic how we count the last page. It should be
448*76559068SAndroid Build Coastguard Worker // consistent with how markFreeBlocksInRegion() handles the last block.
449*76559068SAndroid Build Coastguard Worker if (RoundedRegionSize % BlockSize != 0)
450*76559068SAndroid Build Coastguard Worker ++Pages.back();
451*76559068SAndroid Build Coastguard Worker
452*76559068SAndroid Build Coastguard Worker Batch *CurrentBatch = nullptr;
453*76559068SAndroid Build Coastguard Worker for (scudo::uptr Block = scudo::roundUpSlow(ReleaseBase, BlockSize);
454*76559068SAndroid Build Coastguard Worker Block < RegionSize; Block += BlockSize) {
455*76559068SAndroid Build Coastguard Worker if (CurrentBatch == nullptr ||
456*76559068SAndroid Build Coastguard Worker CurrentBatch->getCount() == Batch::MaxCount) {
457*76559068SAndroid Build Coastguard Worker CurrentBatch = new Batch;
458*76559068SAndroid Build Coastguard Worker CurrentBatch->clear();
459*76559068SAndroid Build Coastguard Worker FreeList.push_back(CurrentBatch);
460*76559068SAndroid Build Coastguard Worker }
461*76559068SAndroid Build Coastguard Worker CurrentBatch->add(Block);
462*76559068SAndroid Build Coastguard Worker }
463*76559068SAndroid Build Coastguard Worker
464*76559068SAndroid Build Coastguard Worker auto VerifyReleaseToOs = [&](scudo::PageReleaseContext &Context) {
465*76559068SAndroid Build Coastguard Worker auto SkipRegion = [](UNUSED scudo::uptr RegionIndex) { return false; };
466*76559068SAndroid Build Coastguard Worker ReleasedPagesRecorder Recorder(ReleaseBase);
467*76559068SAndroid Build Coastguard Worker releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
468*76559068SAndroid Build Coastguard Worker const scudo::uptr FirstBlock = scudo::roundUpSlow(ReleaseBase, BlockSize);
469*76559068SAndroid Build Coastguard Worker
470*76559068SAndroid Build Coastguard Worker for (scudo::uptr P = 0; P < RoundedRegionSize; P += PageSize) {
471*76559068SAndroid Build Coastguard Worker if (P < FirstBlock) {
472*76559068SAndroid Build Coastguard Worker // If FirstBlock is not aligned with page boundary, the first touched
473*76559068SAndroid Build Coastguard Worker // page will not be released either.
474*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Recorder.ReportedPages.find(P) ==
475*76559068SAndroid Build Coastguard Worker Recorder.ReportedPages.end());
476*76559068SAndroid Build Coastguard Worker } else {
477*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Recorder.ReportedPages.find(P) !=
478*76559068SAndroid Build Coastguard Worker Recorder.ReportedPages.end());
479*76559068SAndroid Build Coastguard Worker }
480*76559068SAndroid Build Coastguard Worker }
481*76559068SAndroid Build Coastguard Worker };
482*76559068SAndroid Build Coastguard Worker
483*76559068SAndroid Build Coastguard Worker // Test marking by visiting each block.
484*76559068SAndroid Build Coastguard Worker {
485*76559068SAndroid Build Coastguard Worker auto DecompactPtr = [](scudo::uptr P) { return P; };
486*76559068SAndroid Build Coastguard Worker scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
487*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/RegionSize - PageSize,
488*76559068SAndroid Build Coastguard Worker ReleaseBase);
489*76559068SAndroid Build Coastguard Worker Context.markFreeBlocksInRegion(FreeList, DecompactPtr, /*Base=*/0U,
490*76559068SAndroid Build Coastguard Worker /*RegionIndex=*/0, RegionSize,
491*76559068SAndroid Build Coastguard Worker /*MayContainLastBlockInRegion=*/true);
492*76559068SAndroid Build Coastguard Worker for (const Batch &It : FreeList) {
493*76559068SAndroid Build Coastguard Worker for (scudo::u16 I = 0; I < It.getCount(); I++) {
494*76559068SAndroid Build Coastguard Worker scudo::uptr Block = It.get(I);
495*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = Block / PageSize;
496*76559068SAndroid Build Coastguard Worker Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
497*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Pages[Page], Context.PageMap.get(/*Region=*/0U,
498*76559068SAndroid Build Coastguard Worker Page - BasePageOffset));
499*76559068SAndroid Build Coastguard Worker }
500*76559068SAndroid Build Coastguard Worker }
501*76559068SAndroid Build Coastguard Worker }
502*76559068SAndroid Build Coastguard Worker
503*76559068SAndroid Build Coastguard Worker VerifyReleaseToOs(Context);
504*76559068SAndroid Build Coastguard Worker }
505*76559068SAndroid Build Coastguard Worker
506*76559068SAndroid Build Coastguard Worker // Test range marking.
507*76559068SAndroid Build Coastguard Worker {
508*76559068SAndroid Build Coastguard Worker scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
509*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/RegionSize - PageSize,
510*76559068SAndroid Build Coastguard Worker ReleaseBase);
511*76559068SAndroid Build Coastguard Worker Context.markRangeAsAllCounted(ReleaseBase, RegionSize, /*Base=*/0U,
512*76559068SAndroid Build Coastguard Worker /*RegionIndex=*/0, RegionSize);
513*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = ReleaseBase / PageSize;
514*76559068SAndroid Build Coastguard Worker Page < RoundedRegionSize / PageSize; ++Page) {
515*76559068SAndroid Build Coastguard Worker if (Context.PageMap.get(/*Region=*/0, Page - BasePageOffset) !=
516*76559068SAndroid Build Coastguard Worker Pages[Page]) {
517*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0,
518*76559068SAndroid Build Coastguard Worker Page - BasePageOffset));
519*76559068SAndroid Build Coastguard Worker }
520*76559068SAndroid Build Coastguard Worker }
521*76559068SAndroid Build Coastguard Worker
522*76559068SAndroid Build Coastguard Worker VerifyReleaseToOs(Context);
523*76559068SAndroid Build Coastguard Worker }
524*76559068SAndroid Build Coastguard Worker
525*76559068SAndroid Build Coastguard Worker // Check the buffer size of PageMap.
526*76559068SAndroid Build Coastguard Worker {
527*76559068SAndroid Build Coastguard Worker scudo::PageReleaseContext Full(BlockSize, /*NumberOfRegions=*/1U,
528*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/RegionSize);
529*76559068SAndroid Build Coastguard Worker Full.ensurePageMapAllocated();
530*76559068SAndroid Build Coastguard Worker scudo::PageReleaseContext Partial(BlockSize, /*NumberOfRegions=*/1U,
531*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/RegionSize - PageSize,
532*76559068SAndroid Build Coastguard Worker ReleaseBase);
533*76559068SAndroid Build Coastguard Worker Partial.ensurePageMapAllocated();
534*76559068SAndroid Build Coastguard Worker
535*76559068SAndroid Build Coastguard Worker EXPECT_GE(Full.PageMap.getBufferNumElements(),
536*76559068SAndroid Build Coastguard Worker Partial.PageMap.getBufferNumElements());
537*76559068SAndroid Build Coastguard Worker }
538*76559068SAndroid Build Coastguard Worker
539*76559068SAndroid Build Coastguard Worker while (!FreeList.empty()) {
540*76559068SAndroid Build Coastguard Worker CurrentBatch = FreeList.front();
541*76559068SAndroid Build Coastguard Worker FreeList.pop_front();
542*76559068SAndroid Build Coastguard Worker delete CurrentBatch;
543*76559068SAndroid Build Coastguard Worker }
544*76559068SAndroid Build Coastguard Worker } // Iterate each size class
545*76559068SAndroid Build Coastguard Worker }
546*76559068SAndroid Build Coastguard Worker
TEST(ScudoReleaseTest,ReleaseFreeMemoryToOSDefault)547*76559068SAndroid Build Coastguard Worker TEST(ScudoReleaseTest, ReleaseFreeMemoryToOSDefault) {
548*76559068SAndroid Build Coastguard Worker testReleaseFreeMemoryToOS<scudo::DefaultSizeClassMap>();
549*76559068SAndroid Build Coastguard Worker }
550*76559068SAndroid Build Coastguard Worker
TEST(ScudoReleaseTest,ReleaseFreeMemoryToOSAndroid)551*76559068SAndroid Build Coastguard Worker TEST(ScudoReleaseTest, ReleaseFreeMemoryToOSAndroid) {
552*76559068SAndroid Build Coastguard Worker testReleaseFreeMemoryToOS<scudo::AndroidSizeClassMap>();
553*76559068SAndroid Build Coastguard Worker }
554*76559068SAndroid Build Coastguard Worker
TEST(ScudoReleaseTest,PageMapMarkRange)555*76559068SAndroid Build Coastguard Worker TEST(ScudoReleaseTest, PageMapMarkRange) {
556*76559068SAndroid Build Coastguard Worker testPageMapMarkRange<scudo::DefaultSizeClassMap>();
557*76559068SAndroid Build Coastguard Worker testPageMapMarkRange<scudo::AndroidSizeClassMap>();
558*76559068SAndroid Build Coastguard Worker testPageMapMarkRange<scudo::FuchsiaSizeClassMap>();
559*76559068SAndroid Build Coastguard Worker }
560*76559068SAndroid Build Coastguard Worker
TEST(ScudoReleaseTest,ReleasePartialRegion)561*76559068SAndroid Build Coastguard Worker TEST(ScudoReleaseTest, ReleasePartialRegion) {
562*76559068SAndroid Build Coastguard Worker testReleasePartialRegion<scudo::DefaultSizeClassMap>();
563*76559068SAndroid Build Coastguard Worker testReleasePartialRegion<scudo::AndroidSizeClassMap>();
564*76559068SAndroid Build Coastguard Worker testReleasePartialRegion<scudo::FuchsiaSizeClassMap>();
565*76559068SAndroid Build Coastguard Worker }
566*76559068SAndroid Build Coastguard Worker
testReleaseRangeWithSingleBlock()567*76559068SAndroid Build Coastguard Worker template <class SizeClassMap> void testReleaseRangeWithSingleBlock() {
568*76559068SAndroid Build Coastguard Worker const scudo::uptr PageSize = scudo::getPageSizeCached();
569*76559068SAndroid Build Coastguard Worker
570*76559068SAndroid Build Coastguard Worker // We want to test if a memory group only contains single block that will be
571*76559068SAndroid Build Coastguard Worker // handled properly. The case is like:
572*76559068SAndroid Build Coastguard Worker //
573*76559068SAndroid Build Coastguard Worker // From To
574*76559068SAndroid Build Coastguard Worker // +----------------------+
575*76559068SAndroid Build Coastguard Worker // +------------+------------+
576*76559068SAndroid Build Coastguard Worker // | | |
577*76559068SAndroid Build Coastguard Worker // +------------+------------+
578*76559068SAndroid Build Coastguard Worker // ^
579*76559068SAndroid Build Coastguard Worker // RegionSize
580*76559068SAndroid Build Coastguard Worker //
581*76559068SAndroid Build Coastguard Worker // Note that `From` will be page aligned.
582*76559068SAndroid Build Coastguard Worker //
583*76559068SAndroid Build Coastguard Worker // If the second from the last block is aligned at `From`, then we expect all
584*76559068SAndroid Build Coastguard Worker // the pages after `From` will be marked as can-be-released. Otherwise, the
585*76559068SAndroid Build Coastguard Worker // pages only touched by the last blocks will be marked as can-be-released.
586*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 1; I <= SizeClassMap::LargestClassId; I++) {
587*76559068SAndroid Build Coastguard Worker const scudo::uptr BlockSize = SizeClassMap::getSizeByClassId(I);
588*76559068SAndroid Build Coastguard Worker const scudo::uptr From = scudo::roundUp(BlockSize, PageSize);
589*76559068SAndroid Build Coastguard Worker const scudo::uptr To =
590*76559068SAndroid Build Coastguard Worker From % BlockSize == 0
591*76559068SAndroid Build Coastguard Worker ? From + BlockSize
592*76559068SAndroid Build Coastguard Worker : scudo::roundDownSlow(From + BlockSize, BlockSize) + BlockSize;
593*76559068SAndroid Build Coastguard Worker const scudo::uptr RoundedRegionSize = scudo::roundUp(To, PageSize);
594*76559068SAndroid Build Coastguard Worker
595*76559068SAndroid Build Coastguard Worker std::vector<scudo::uptr> Pages(RoundedRegionSize / PageSize, 0);
596*76559068SAndroid Build Coastguard Worker for (scudo::uptr Block = (To - BlockSize); Block < RoundedRegionSize;
597*76559068SAndroid Build Coastguard Worker Block += BlockSize) {
598*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = Block / PageSize;
599*76559068SAndroid Build Coastguard Worker Page <= (Block + BlockSize - 1) / PageSize &&
600*76559068SAndroid Build Coastguard Worker Page < RoundedRegionSize / PageSize;
601*76559068SAndroid Build Coastguard Worker ++Page) {
602*76559068SAndroid Build Coastguard Worker ASSERT_LT(Page, Pages.size());
603*76559068SAndroid Build Coastguard Worker ++Pages[Page];
604*76559068SAndroid Build Coastguard Worker }
605*76559068SAndroid Build Coastguard Worker }
606*76559068SAndroid Build Coastguard Worker
607*76559068SAndroid Build Coastguard Worker scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
608*76559068SAndroid Build Coastguard Worker /*ReleaseSize=*/To,
609*76559068SAndroid Build Coastguard Worker /*ReleaseBase=*/0U);
610*76559068SAndroid Build Coastguard Worker Context.markRangeAsAllCounted(From, To, /*Base=*/0U, /*RegionIndex=*/0,
611*76559068SAndroid Build Coastguard Worker /*RegionSize=*/To);
612*76559068SAndroid Build Coastguard Worker
613*76559068SAndroid Build Coastguard Worker for (scudo::uptr Page = 0; Page < RoundedRegionSize; Page += PageSize) {
614*76559068SAndroid Build Coastguard Worker if (Context.PageMap.get(/*Region=*/0U, Page / PageSize) !=
615*76559068SAndroid Build Coastguard Worker Pages[Page / PageSize]) {
616*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(
617*76559068SAndroid Build Coastguard Worker Context.PageMap.isAllCounted(/*Region=*/0U, Page / PageSize));
618*76559068SAndroid Build Coastguard Worker }
619*76559068SAndroid Build Coastguard Worker }
620*76559068SAndroid Build Coastguard Worker } // for each size class
621*76559068SAndroid Build Coastguard Worker }
622*76559068SAndroid Build Coastguard Worker
TEST(ScudoReleaseTest,RangeReleaseRegionWithSingleBlock)623*76559068SAndroid Build Coastguard Worker TEST(ScudoReleaseTest, RangeReleaseRegionWithSingleBlock) {
624*76559068SAndroid Build Coastguard Worker testReleaseRangeWithSingleBlock<scudo::DefaultSizeClassMap>();
625*76559068SAndroid Build Coastguard Worker testReleaseRangeWithSingleBlock<scudo::AndroidSizeClassMap>();
626*76559068SAndroid Build Coastguard Worker testReleaseRangeWithSingleBlock<scudo::FuchsiaSizeClassMap>();
627*76559068SAndroid Build Coastguard Worker }
628*76559068SAndroid Build Coastguard Worker
TEST(ScudoReleaseTest,BufferPool)629*76559068SAndroid Build Coastguard Worker TEST(ScudoReleaseTest, BufferPool) {
630*76559068SAndroid Build Coastguard Worker constexpr scudo::uptr StaticBufferCount = SCUDO_WORDSIZE - 1;
631*76559068SAndroid Build Coastguard Worker constexpr scudo::uptr StaticBufferNumElements = 512U;
632*76559068SAndroid Build Coastguard Worker
633*76559068SAndroid Build Coastguard Worker // Allocate the buffer pool on the heap because it is quite large (slightly
634*76559068SAndroid Build Coastguard Worker // more than StaticBufferCount * StaticBufferNumElements * sizeof(uptr)) and
635*76559068SAndroid Build Coastguard Worker // it may not fit in the stack on some platforms.
636*76559068SAndroid Build Coastguard Worker using BufferPool =
637*76559068SAndroid Build Coastguard Worker scudo::BufferPool<StaticBufferCount, StaticBufferNumElements>;
638*76559068SAndroid Build Coastguard Worker std::unique_ptr<BufferPool> Pool(new BufferPool());
639*76559068SAndroid Build Coastguard Worker
640*76559068SAndroid Build Coastguard Worker std::vector<BufferPool::Buffer> Buffers;
641*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < StaticBufferCount; ++I) {
642*76559068SAndroid Build Coastguard Worker BufferPool::Buffer Buffer = Pool->getBuffer(StaticBufferNumElements);
643*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Pool->isStaticBufferTestOnly(Buffer));
644*76559068SAndroid Build Coastguard Worker Buffers.push_back(Buffer);
645*76559068SAndroid Build Coastguard Worker }
646*76559068SAndroid Build Coastguard Worker
647*76559068SAndroid Build Coastguard Worker // The static buffer is supposed to be used up.
648*76559068SAndroid Build Coastguard Worker BufferPool::Buffer Buffer = Pool->getBuffer(StaticBufferNumElements);
649*76559068SAndroid Build Coastguard Worker EXPECT_FALSE(Pool->isStaticBufferTestOnly(Buffer));
650*76559068SAndroid Build Coastguard Worker
651*76559068SAndroid Build Coastguard Worker Pool->releaseBuffer(Buffer);
652*76559068SAndroid Build Coastguard Worker for (auto &Buffer : Buffers)
653*76559068SAndroid Build Coastguard Worker Pool->releaseBuffer(Buffer);
654*76559068SAndroid Build Coastguard Worker }
655