13fa38318SNico Weber //===-- release_test.cpp ----------------------------------------*- C++ -*-===//
23fa38318SNico Weber //
33fa38318SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43fa38318SNico Weber // See https://llvm.org/LICENSE.txt for license information.
53fa38318SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63fa38318SNico Weber //
73fa38318SNico Weber //===----------------------------------------------------------------------===//
83fa38318SNico Weber
90d3d4d3bSKostya Kortchinsky #include "tests/scudo_unit_test.h"
100d3d4d3bSKostya Kortchinsky
113fa38318SNico Weber #include "list.h"
123fa38318SNico Weber #include "release.h"
133fa38318SNico Weber #include "size_class_map.h"
143fa38318SNico Weber
153fa38318SNico Weber #include <string.h>
163fa38318SNico Weber
173fa38318SNico Weber #include <algorithm>
183fa38318SNico Weber #include <random>
190d3d4d3bSKostya Kortchinsky #include <set>
203fa38318SNico Weber
TEST(ScudoReleaseTest,RegionPageMap)2156dafd02SChia-hung Duan TEST(ScudoReleaseTest, RegionPageMap) {
223fa38318SNico Weber for (scudo::uptr I = 0; I < SCUDO_WORDSIZE; I++) {
233fa38318SNico Weber // Various valid counter's max values packed into one word.
2456dafd02SChia-hung Duan scudo::RegionPageMap PageMap2N(1U, 1U, 1UL << I);
2511ea40cfSChia-hung Duan ASSERT_TRUE(PageMap2N.isAllocated());
262bfcecf4SVitaly Buka EXPECT_EQ(1U, PageMap2N.getBufferNumElements());
273fa38318SNico Weber // Check the "all bit set" values too.
2856dafd02SChia-hung Duan scudo::RegionPageMap PageMap2N1_1(1U, 1U, ~0UL >> I);
2911ea40cfSChia-hung Duan ASSERT_TRUE(PageMap2N1_1.isAllocated());
302bfcecf4SVitaly Buka EXPECT_EQ(1U, PageMap2N1_1.getBufferNumElements());
313fa38318SNico Weber // Verify the packing ratio, the counter is Expected to be packed into the
323fa38318SNico Weber // closest power of 2 bits.
3356dafd02SChia-hung Duan scudo::RegionPageMap PageMap(1U, SCUDO_WORDSIZE, 1UL << I);
3411ea40cfSChia-hung Duan ASSERT_TRUE(PageMap.isAllocated());
3576a5602fSFabio D'Urso EXPECT_EQ(scudo::roundUpPowerOfTwo(I + 1), PageMap.getBufferNumElements());
363fa38318SNico Weber }
373fa38318SNico Weber
383fa38318SNico Weber // Go through 1, 2, 4, 8, .. {32,64} bits per counter.
393fa38318SNico Weber for (scudo::uptr I = 0; (SCUDO_WORDSIZE >> I) != 0; I++) {
403fa38318SNico Weber // Make sure counters request one memory page for the buffer.
413fa38318SNico Weber const scudo::uptr NumCounters =
423fa38318SNico Weber (scudo::getPageSizeCached() / 8) * (SCUDO_WORDSIZE >> I);
4356dafd02SChia-hung Duan scudo::RegionPageMap PageMap(1U, NumCounters,
44998334daSKostya Kortchinsky 1UL << ((1UL << I) - 1));
4511ea40cfSChia-hung Duan ASSERT_TRUE(PageMap.isAllocated());
4656dafd02SChia-hung Duan PageMap.inc(0U, 0U);
473fa38318SNico Weber for (scudo::uptr C = 1; C < NumCounters - 1; C++) {
4856dafd02SChia-hung Duan EXPECT_EQ(0UL, PageMap.get(0U, C));
4956dafd02SChia-hung Duan PageMap.inc(0U, C);
5056dafd02SChia-hung Duan EXPECT_EQ(1UL, PageMap.get(0U, C - 1));
513fa38318SNico Weber }
5256dafd02SChia-hung Duan EXPECT_EQ(0UL, PageMap.get(0U, NumCounters - 1));
5356dafd02SChia-hung Duan PageMap.inc(0U, NumCounters - 1);
543fa38318SNico Weber if (I > 0) {
5556dafd02SChia-hung Duan PageMap.incRange(0u, 0U, NumCounters - 1);
563fa38318SNico Weber for (scudo::uptr C = 0; C < NumCounters; C++)
5756dafd02SChia-hung Duan EXPECT_EQ(2UL, PageMap.get(0U, C));
583fa38318SNico Weber }
593fa38318SNico Weber }
60a78805f3SChia-hung Duan
61a78805f3SChia-hung Duan // Similar to the above except that we are using incN().
62a78805f3SChia-hung Duan for (scudo::uptr I = 0; (SCUDO_WORDSIZE >> I) != 0; I++) {
63a78805f3SChia-hung Duan // Make sure counters request one memory page for the buffer.
64a78805f3SChia-hung Duan const scudo::uptr NumCounters =
65a78805f3SChia-hung Duan (scudo::getPageSizeCached() / 8) * (SCUDO_WORDSIZE >> I);
66a78805f3SChia-hung Duan scudo::uptr MaxValue = 1UL << ((1UL << I) - 1);
67a78805f3SChia-hung Duan if (MaxValue <= 1U)
68a78805f3SChia-hung Duan continue;
69a78805f3SChia-hung Duan
70a78805f3SChia-hung Duan scudo::RegionPageMap PageMap(1U, NumCounters, MaxValue);
71a78805f3SChia-hung Duan
72a78805f3SChia-hung Duan scudo::uptr N = MaxValue / 2;
73a78805f3SChia-hung Duan PageMap.incN(0U, 0, N);
74a78805f3SChia-hung Duan for (scudo::uptr C = 1; C < NumCounters; C++) {
75a78805f3SChia-hung Duan EXPECT_EQ(0UL, PageMap.get(0U, C));
76a78805f3SChia-hung Duan PageMap.incN(0U, C, N);
77a78805f3SChia-hung Duan EXPECT_EQ(N, PageMap.get(0U, C - 1));
78a78805f3SChia-hung Duan }
79a78805f3SChia-hung Duan EXPECT_EQ(N, PageMap.get(0U, NumCounters - 1));
80a78805f3SChia-hung Duan }
813fa38318SNico Weber }
823fa38318SNico Weber
833fa38318SNico Weber class StringRangeRecorder {
843fa38318SNico Weber public:
853fa38318SNico Weber std::string ReportedPages;
863fa38318SNico Weber
StringRangeRecorder()873fa38318SNico Weber StringRangeRecorder()
883fa38318SNico Weber : PageSizeScaledLog(scudo::getLog2(scudo::getPageSizeCached())) {}
893fa38318SNico Weber
releasePageRangeToOS(scudo::uptr From,scudo::uptr To)903fa38318SNico Weber void releasePageRangeToOS(scudo::uptr From, scudo::uptr To) {
913fa38318SNico Weber From >>= PageSizeScaledLog;
923fa38318SNico Weber To >>= PageSizeScaledLog;
933fa38318SNico Weber EXPECT_LT(From, To);
943fa38318SNico Weber if (!ReportedPages.empty())
953fa38318SNico Weber EXPECT_LT(LastPageReported, From);
963fa38318SNico Weber ReportedPages.append(From - LastPageReported, '.');
973fa38318SNico Weber ReportedPages.append(To - From, 'x');
983fa38318SNico Weber LastPageReported = To;
993fa38318SNico Weber }
1003fa38318SNico Weber
1013fa38318SNico Weber private:
1023fa38318SNico Weber const scudo::uptr PageSizeScaledLog;
1033fa38318SNico Weber scudo::uptr LastPageReported = 0;
1043fa38318SNico Weber };
1053fa38318SNico Weber
TEST(ScudoReleaseTest,FreePagesRangeTracker)1063fa38318SNico Weber TEST(ScudoReleaseTest, FreePagesRangeTracker) {
1073fa38318SNico Weber // 'x' denotes a page to be released, '.' denotes a page to be kept around.
1083fa38318SNico Weber const char *TestCases[] = {
1093fa38318SNico Weber "",
1103fa38318SNico Weber ".",
1113fa38318SNico Weber "x",
1123fa38318SNico Weber "........",
1133fa38318SNico Weber "xxxxxxxxxxx",
1143fa38318SNico Weber "..............xxxxx",
1153fa38318SNico Weber "xxxxxxxxxxxxxxxxxx.....",
1163fa38318SNico Weber "......xxxxxxxx........",
1173fa38318SNico Weber "xxx..........xxxxxxxxxxxxxxx",
1183fa38318SNico Weber "......xxxx....xxxx........",
1193fa38318SNico Weber "xxx..........xxxxxxxx....xxxxxxx",
1203fa38318SNico Weber "x.x.x.x.x.x.x.x.x.x.x.x.",
1213fa38318SNico Weber ".x.x.x.x.x.x.x.x.x.x.x.x",
1223fa38318SNico Weber ".x.x.x.x.x.x.x.x.x.x.x.x.",
1233fa38318SNico Weber "x.x.x.x.x.x.x.x.x.x.x.x.x",
1243fa38318SNico Weber };
1253fa38318SNico Weber typedef scudo::FreePagesRangeTracker<StringRangeRecorder> RangeTracker;
1263fa38318SNico Weber
1273fa38318SNico Weber for (auto TestCase : TestCases) {
1283fa38318SNico Weber StringRangeRecorder Recorder;
12956dafd02SChia-hung Duan RangeTracker Tracker(Recorder);
1303fa38318SNico Weber for (scudo::uptr I = 0; TestCase[I] != 0; I++)
1313fa38318SNico Weber Tracker.processNextPage(TestCase[I] == 'x');
1323fa38318SNico Weber Tracker.finish();
1333fa38318SNico Weber // Strip trailing '.'-pages before comparing the results as they are not
1343fa38318SNico Weber // going to be reported to range_recorder anyway.
1353fa38318SNico Weber const char *LastX = strrchr(TestCase, 'x');
136af41f79fSChristopher Ferris std::string Expected(
137af41f79fSChristopher Ferris TestCase,
138af41f79fSChristopher Ferris LastX == nullptr ? 0U : static_cast<size_t>(LastX - TestCase + 1));
1393fa38318SNico Weber EXPECT_STREQ(Expected.c_str(), Recorder.ReportedPages.c_str());
1403fa38318SNico Weber }
1413fa38318SNico Weber }
1423fa38318SNico Weber
1433fa38318SNico Weber class ReleasedPagesRecorder {
1443fa38318SNico Weber public:
1455b9d6097SChia-hung Duan ReleasedPagesRecorder() = default;
ReleasedPagesRecorder(scudo::uptr Base)1465b9d6097SChia-hung Duan explicit ReleasedPagesRecorder(scudo::uptr Base) : Base(Base) {}
1473fa38318SNico Weber std::set<scudo::uptr> ReportedPages;
1483fa38318SNico Weber
releasePageRangeToOS(scudo::uptr From,scudo::uptr To)1493fa38318SNico Weber void releasePageRangeToOS(scudo::uptr From, scudo::uptr To) {
1503fa38318SNico Weber const scudo::uptr PageSize = scudo::getPageSizeCached();
1513fa38318SNico Weber for (scudo::uptr I = From; I < To; I += PageSize)
1525b9d6097SChia-hung Duan ReportedPages.insert(I + getBase());
1533fa38318SNico Weber }
1542c56776aSKostya Kortchinsky
getBase() const1555b9d6097SChia-hung Duan scudo::uptr getBase() const { return Base; }
1565b9d6097SChia-hung Duan scudo::uptr Base = 0;
1573fa38318SNico Weber };
1583fa38318SNico Weber
1593fa38318SNico Weber // Simplified version of a TransferBatch.
1603fa38318SNico Weber template <class SizeClassMap> struct FreeBatch {
1610fb2aeefSChia-hung Duan static const scudo::u16 MaxCount = SizeClassMap::MaxNumCachedHint;
clearFreeBatch1623fa38318SNico Weber void clear() { Count = 0; }
addFreeBatch1633fa38318SNico Weber void add(scudo::uptr P) {
1643fa38318SNico Weber DCHECK_LT(Count, MaxCount);
1653fa38318SNico Weber Batch[Count++] = P;
1663fa38318SNico Weber }
getCountFreeBatch1670fb2aeefSChia-hung Duan scudo::u16 getCount() const { return Count; }
getFreeBatch1680fb2aeefSChia-hung Duan scudo::uptr get(scudo::u16 I) const {
1693fa38318SNico Weber DCHECK_LE(I, Count);
1703fa38318SNico Weber return Batch[I];
1713fa38318SNico Weber }
1723fa38318SNico Weber FreeBatch *Next;
1733fa38318SNico Weber
1743fa38318SNico Weber private:
1753fa38318SNico Weber scudo::uptr Batch[MaxCount];
1760fb2aeefSChia-hung Duan scudo::u16 Count;
1773fa38318SNico Weber };
1783fa38318SNico Weber
testReleaseFreeMemoryToOS()1793fa38318SNico Weber template <class SizeClassMap> void testReleaseFreeMemoryToOS() {
1803fa38318SNico Weber typedef FreeBatch<SizeClassMap> Batch;
181c753a306SKostya Kortchinsky const scudo::uptr PagesCount = 1024;
1823fa38318SNico Weber const scudo::uptr PageSize = scudo::getPageSizeCached();
18356dafd02SChia-hung Duan const scudo::uptr PageSizeLog = scudo::getLog2(PageSize);
1843fa38318SNico Weber std::mt19937 R;
1853fa38318SNico Weber scudo::u32 RandState = 42;
1863fa38318SNico Weber
1873fa38318SNico Weber for (scudo::uptr I = 1; I <= SizeClassMap::LargestClassId; I++) {
1883fa38318SNico Weber const scudo::uptr BlockSize = SizeClassMap::getSizeByClassId(I);
189c753a306SKostya Kortchinsky const scudo::uptr MaxBlocks = PagesCount * PageSize / BlockSize;
1903fa38318SNico Weber
1913fa38318SNico Weber // Generate the random free list.
1923fa38318SNico Weber std::vector<scudo::uptr> FreeArray;
1933fa38318SNico Weber bool InFreeRange = false;
1943fa38318SNico Weber scudo::uptr CurrentRangeEnd = 0;
1953fa38318SNico Weber for (scudo::uptr I = 0; I < MaxBlocks; I++) {
1963fa38318SNico Weber if (I == CurrentRangeEnd) {
1973fa38318SNico Weber InFreeRange = (scudo::getRandomU32(&RandState) & 1U) == 1;
1983fa38318SNico Weber CurrentRangeEnd += (scudo::getRandomU32(&RandState) & 0x7f) + 1;
1993fa38318SNico Weber }
2003fa38318SNico Weber if (InFreeRange)
2013fa38318SNico Weber FreeArray.push_back(I * BlockSize);
2023fa38318SNico Weber }
2033fa38318SNico Weber if (FreeArray.empty())
2043fa38318SNico Weber continue;
2053fa38318SNico Weber // Shuffle the array to ensure that the order is irrelevant.
2063fa38318SNico Weber std::shuffle(FreeArray.begin(), FreeArray.end(), R);
2073fa38318SNico Weber
2083fa38318SNico Weber // Build the FreeList from the FreeArray.
2096f2de9cbSKostya Kortchinsky scudo::SinglyLinkedList<Batch> FreeList;
2103fa38318SNico Weber FreeList.clear();
2113fa38318SNico Weber Batch *CurrentBatch = nullptr;
2123fa38318SNico Weber for (auto const &Block : FreeArray) {
2133fa38318SNico Weber if (!CurrentBatch) {
2143fa38318SNico Weber CurrentBatch = new Batch;
2153fa38318SNico Weber CurrentBatch->clear();
2163fa38318SNico Weber FreeList.push_back(CurrentBatch);
2173fa38318SNico Weber }
2183fa38318SNico Weber CurrentBatch->add(Block);
2193fa38318SNico Weber if (CurrentBatch->getCount() == Batch::MaxCount)
2203fa38318SNico Weber CurrentBatch = nullptr;
2213fa38318SNico Weber }
2223fa38318SNico Weber
2233fa38318SNico Weber // Release the memory.
224bd5ca4f0SKostya Kortchinsky auto SkipRegion = [](UNUSED scudo::uptr RegionIndex) { return false; };
2252c56776aSKostya Kortchinsky auto DecompactPtr = [](scudo::uptr P) { return P; };
2263fa38318SNico Weber ReleasedPagesRecorder Recorder;
227657d297aSChia-hung Duan scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
2285b9d6097SChia-hung Duan /*ReleaseSize=*/MaxBlocks * BlockSize);
229a1e325ceSChia-hung Duan ASSERT_FALSE(Context.hasBlockMarked());
230657d297aSChia-hung Duan Context.markFreeBlocksInRegion(FreeList, DecompactPtr, Recorder.getBase(),
231657d297aSChia-hung Duan /*RegionIndex=*/0, MaxBlocks * BlockSize,
232657d297aSChia-hung Duan /*MayContainLastBlockInRegion=*/true);
233a1e325ceSChia-hung Duan ASSERT_TRUE(Context.hasBlockMarked());
23456dafd02SChia-hung Duan releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
23556dafd02SChia-hung Duan scudo::RegionPageMap &PageMap = Context.PageMap;
2363fa38318SNico Weber
2373fa38318SNico Weber // Verify that there are no released pages touched by used chunks and all
2383fa38318SNico Weber // ranges of free chunks big enough to contain the entire memory pages had
2393fa38318SNico Weber // these pages released.
2403fa38318SNico Weber scudo::uptr VerifiedReleasedPages = 0;
2413fa38318SNico Weber std::set<scudo::uptr> FreeBlocks(FreeArray.begin(), FreeArray.end());
2423fa38318SNico Weber
2433fa38318SNico Weber scudo::uptr CurrentBlock = 0;
2443fa38318SNico Weber InFreeRange = false;
2453fa38318SNico Weber scudo::uptr CurrentFreeRangeStart = 0;
246c753a306SKostya Kortchinsky for (scudo::uptr I = 0; I < MaxBlocks; I++) {
2473fa38318SNico Weber const bool IsFreeBlock =
2483fa38318SNico Weber FreeBlocks.find(CurrentBlock) != FreeBlocks.end();
2493fa38318SNico Weber if (IsFreeBlock) {
2503fa38318SNico Weber if (!InFreeRange) {
2513fa38318SNico Weber InFreeRange = true;
2523fa38318SNico Weber CurrentFreeRangeStart = CurrentBlock;
2533fa38318SNico Weber }
2543fa38318SNico Weber } else {
2553fa38318SNico Weber // Verify that this used chunk does not touch any released page.
2563fa38318SNico Weber const scudo::uptr StartPage = CurrentBlock / PageSize;
2573fa38318SNico Weber const scudo::uptr EndPage = (CurrentBlock + BlockSize - 1) / PageSize;
2583fa38318SNico Weber for (scudo::uptr J = StartPage; J <= EndPage; J++) {
2593fa38318SNico Weber const bool PageReleased = Recorder.ReportedPages.find(J * PageSize) !=
2603fa38318SNico Weber Recorder.ReportedPages.end();
2613fa38318SNico Weber EXPECT_EQ(false, PageReleased);
26256dafd02SChia-hung Duan EXPECT_EQ(false,
26356dafd02SChia-hung Duan PageMap.isAllCounted(0, (J * PageSize) >> PageSizeLog));
2643fa38318SNico Weber }
2653fa38318SNico Weber
2663fa38318SNico Weber if (InFreeRange) {
2673fa38318SNico Weber InFreeRange = false;
2683fa38318SNico Weber // Verify that all entire memory pages covered by this range of free
2693fa38318SNico Weber // chunks were released.
270a9269773SChia-hung Duan scudo::uptr P = scudo::roundUp(CurrentFreeRangeStart, PageSize);
2713fa38318SNico Weber while (P + PageSize <= CurrentBlock) {
2723fa38318SNico Weber const bool PageReleased =
2733fa38318SNico Weber Recorder.ReportedPages.find(P) != Recorder.ReportedPages.end();
2743fa38318SNico Weber EXPECT_EQ(true, PageReleased);
27556dafd02SChia-hung Duan EXPECT_EQ(true, PageMap.isAllCounted(0, P >> PageSizeLog));
2763fa38318SNico Weber VerifiedReleasedPages++;
2773fa38318SNico Weber P += PageSize;
2783fa38318SNico Weber }
2793fa38318SNico Weber }
2803fa38318SNico Weber }
2813fa38318SNico Weber
2823fa38318SNico Weber CurrentBlock += BlockSize;
2833fa38318SNico Weber }
2843fa38318SNico Weber
285c753a306SKostya Kortchinsky if (InFreeRange) {
286a9269773SChia-hung Duan scudo::uptr P = scudo::roundUp(CurrentFreeRangeStart, PageSize);
287c753a306SKostya Kortchinsky const scudo::uptr EndPage =
288a9269773SChia-hung Duan scudo::roundUp(MaxBlocks * BlockSize, PageSize);
289c753a306SKostya Kortchinsky while (P + PageSize <= EndPage) {
290c753a306SKostya Kortchinsky const bool PageReleased =
291c753a306SKostya Kortchinsky Recorder.ReportedPages.find(P) != Recorder.ReportedPages.end();
292c753a306SKostya Kortchinsky EXPECT_EQ(true, PageReleased);
29356dafd02SChia-hung Duan EXPECT_EQ(true, PageMap.isAllCounted(0, P >> PageSizeLog));
294c753a306SKostya Kortchinsky VerifiedReleasedPages++;
295c753a306SKostya Kortchinsky P += PageSize;
296c753a306SKostya Kortchinsky }
297c753a306SKostya Kortchinsky }
298c753a306SKostya Kortchinsky
2993fa38318SNico Weber EXPECT_EQ(Recorder.ReportedPages.size(), VerifiedReleasedPages);
3003fa38318SNico Weber
3013fa38318SNico Weber while (!FreeList.empty()) {
3023fa38318SNico Weber CurrentBatch = FreeList.front();
3033fa38318SNico Weber FreeList.pop_front();
3043fa38318SNico Weber delete CurrentBatch;
3053fa38318SNico Weber }
3063fa38318SNico Weber }
3073fa38318SNico Weber }
3083fa38318SNico Weber
testPageMapMarkRange()309a78805f3SChia-hung Duan template <class SizeClassMap> void testPageMapMarkRange() {
310a78805f3SChia-hung Duan const scudo::uptr PageSize = scudo::getPageSizeCached();
311a78805f3SChia-hung Duan
312a78805f3SChia-hung Duan for (scudo::uptr I = 1; I <= SizeClassMap::LargestClassId; I++) {
313a78805f3SChia-hung Duan const scudo::uptr BlockSize = SizeClassMap::getSizeByClassId(I);
314a78805f3SChia-hung Duan
315a78805f3SChia-hung Duan const scudo::uptr GroupNum = 2;
316a78805f3SChia-hung Duan const scudo::uptr GroupSize = scudo::roundUp(BlockSize, PageSize) * 2;
317a78805f3SChia-hung Duan const scudo::uptr RegionSize =
318a78805f3SChia-hung Duan scudo::roundUpSlow(GroupSize * GroupNum, BlockSize);
319a78805f3SChia-hung Duan const scudo::uptr RoundedRegionSize = scudo::roundUp(RegionSize, PageSize);
320a78805f3SChia-hung Duan
321a78805f3SChia-hung Duan std::vector<scudo::uptr> Pages(RoundedRegionSize / PageSize, 0);
3226fb70a8aSChia-hung Duan for (scudo::uptr Block = 0; Block < RoundedRegionSize; Block += BlockSize) {
3236fb70a8aSChia-hung Duan for (scudo::uptr Page = Block / PageSize;
3246fb70a8aSChia-hung Duan Page <= (Block + BlockSize - 1) / PageSize &&
3256fb70a8aSChia-hung Duan Page < RoundedRegionSize / PageSize;
3266fb70a8aSChia-hung Duan ++Page) {
3276fb70a8aSChia-hung Duan ASSERT_LT(Page, Pages.size());
3286fb70a8aSChia-hung Duan ++Pages[Page];
329a78805f3SChia-hung Duan }
330a78805f3SChia-hung Duan }
331a78805f3SChia-hung Duan
332a78805f3SChia-hung Duan for (scudo::uptr GroupId = 0; GroupId < GroupNum; ++GroupId) {
333a78805f3SChia-hung Duan const scudo::uptr GroupBeg = GroupId * GroupSize;
334a78805f3SChia-hung Duan const scudo::uptr GroupEnd = GroupBeg + GroupSize;
335a78805f3SChia-hung Duan
336657d297aSChia-hung Duan scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
3375b9d6097SChia-hung Duan /*ReleaseSize=*/RegionSize);
338657d297aSChia-hung Duan Context.markRangeAsAllCounted(GroupBeg, GroupEnd, /*Base=*/0U,
339657d297aSChia-hung Duan /*RegionIndex=*/0, RegionSize);
340a78805f3SChia-hung Duan
341a78805f3SChia-hung Duan scudo::uptr FirstBlock =
342a78805f3SChia-hung Duan ((GroupBeg + BlockSize - 1) / BlockSize) * BlockSize;
343a78805f3SChia-hung Duan
344a78805f3SChia-hung Duan // All the pages before first block page are not supposed to be marked.
345a78805f3SChia-hung Duan if (FirstBlock / PageSize > 0) {
346a78805f3SChia-hung Duan for (scudo::uptr Page = 0; Page <= FirstBlock / PageSize - 1; ++Page)
347a78805f3SChia-hung Duan EXPECT_EQ(Context.PageMap.get(/*Region=*/0, Page), 0U);
348a78805f3SChia-hung Duan }
349a78805f3SChia-hung Duan
350a78805f3SChia-hung Duan // Verify the pages used by the blocks in the group except that if the
351a78805f3SChia-hung Duan // end of the last block is not aligned with `GroupEnd`, it'll be verified
352a78805f3SChia-hung Duan // later.
353a78805f3SChia-hung Duan scudo::uptr Block;
354a78805f3SChia-hung Duan for (Block = FirstBlock; Block + BlockSize <= GroupEnd;
355a78805f3SChia-hung Duan Block += BlockSize) {
356a78805f3SChia-hung Duan for (scudo::uptr Page = Block / PageSize;
357a78805f3SChia-hung Duan Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
358a78805f3SChia-hung Duan // First used page in the group has two cases, which are w/ and w/o
359a78805f3SChia-hung Duan // block sitting across the boundary.
360a78805f3SChia-hung Duan if (Page == FirstBlock / PageSize) {
361a78805f3SChia-hung Duan if (FirstBlock % PageSize == 0) {
362a78805f3SChia-hung Duan EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0U, Page));
363a78805f3SChia-hung Duan } else {
364a78805f3SChia-hung Duan // There's a block straddling `GroupBeg`, it's supposed to only
365a78805f3SChia-hung Duan // increment the counter and we expect it should be 1 less
366a78805f3SChia-hung Duan // (exclude the straddling one) than the total blocks on the page.
367a78805f3SChia-hung Duan EXPECT_EQ(Context.PageMap.get(/*Region=*/0U, Page),
368a78805f3SChia-hung Duan Pages[Page] - 1);
369a78805f3SChia-hung Duan }
370a78805f3SChia-hung Duan } else {
371a78805f3SChia-hung Duan EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0, Page));
372a78805f3SChia-hung Duan }
373a78805f3SChia-hung Duan }
374a78805f3SChia-hung Duan }
375a78805f3SChia-hung Duan
376a78805f3SChia-hung Duan if (Block == GroupEnd)
377a78805f3SChia-hung Duan continue;
378a78805f3SChia-hung Duan
379a78805f3SChia-hung Duan // Examine the last block which sits across the group boundary.
380a78805f3SChia-hung Duan if (Block + BlockSize == RegionSize) {
381a78805f3SChia-hung Duan // This is the last block in the region, it's supposed to mark all the
382a78805f3SChia-hung Duan // pages as all counted.
383a78805f3SChia-hung Duan for (scudo::uptr Page = Block / PageSize;
384a78805f3SChia-hung Duan Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
385a78805f3SChia-hung Duan EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0, Page));
386a78805f3SChia-hung Duan }
387a78805f3SChia-hung Duan } else {
388a78805f3SChia-hung Duan for (scudo::uptr Page = Block / PageSize;
389a78805f3SChia-hung Duan Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
390a78805f3SChia-hung Duan if (Page <= (GroupEnd - 1) / PageSize)
391a78805f3SChia-hung Duan EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0, Page));
392a78805f3SChia-hung Duan else
393a78805f3SChia-hung Duan EXPECT_EQ(Context.PageMap.get(/*Region=*/0U, Page), 1U);
394a78805f3SChia-hung Duan }
395a78805f3SChia-hung Duan }
396a78805f3SChia-hung Duan
397a78805f3SChia-hung Duan const scudo::uptr FirstUncountedPage =
398a78805f3SChia-hung Duan scudo::roundUp(Block + BlockSize, PageSize);
399a78805f3SChia-hung Duan for (scudo::uptr Page = FirstUncountedPage;
400a78805f3SChia-hung Duan Page <= RoundedRegionSize / PageSize; ++Page) {
401a78805f3SChia-hung Duan EXPECT_EQ(Context.PageMap.get(/*Region=*/0U, Page), 0U);
402a78805f3SChia-hung Duan }
403a78805f3SChia-hung Duan } // Iterate each Group
404a78805f3SChia-hung Duan
405a78805f3SChia-hung Duan // Release the entire region. This is to ensure the last page is counted.
406657d297aSChia-hung Duan scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
4075b9d6097SChia-hung Duan /*ReleaseSize=*/RegionSize);
408657d297aSChia-hung Duan Context.markRangeAsAllCounted(/*From=*/0U, /*To=*/RegionSize, /*Base=*/0,
409657d297aSChia-hung Duan /*RegionIndex=*/0, RegionSize);
410a78805f3SChia-hung Duan for (scudo::uptr Page = 0; Page < RoundedRegionSize / PageSize; ++Page)
411a78805f3SChia-hung Duan EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0, Page));
412a78805f3SChia-hung Duan } // Iterate each size class
413a78805f3SChia-hung Duan }
414a78805f3SChia-hung Duan
testReleasePartialRegion()4155b9d6097SChia-hung Duan template <class SizeClassMap> void testReleasePartialRegion() {
4165b9d6097SChia-hung Duan typedef FreeBatch<SizeClassMap> Batch;
4175b9d6097SChia-hung Duan const scudo::uptr PageSize = scudo::getPageSizeCached();
4185b9d6097SChia-hung Duan
4195b9d6097SChia-hung Duan for (scudo::uptr I = 1; I <= SizeClassMap::LargestClassId; I++) {
4205b9d6097SChia-hung Duan // In the following, we want to ensure the region includes at least 2 pages
4215b9d6097SChia-hung Duan // and we will release all the pages except the first one. The handling of
4225b9d6097SChia-hung Duan // the last block is tricky, so we always test the case that includes the
4235b9d6097SChia-hung Duan // last block.
4245b9d6097SChia-hung Duan const scudo::uptr BlockSize = SizeClassMap::getSizeByClassId(I);
4256fb70a8aSChia-hung Duan const scudo::uptr ReleaseBase = scudo::roundUp(BlockSize, PageSize);
4266fb70a8aSChia-hung Duan const scudo::uptr BasePageOffset = ReleaseBase / PageSize;
4275b9d6097SChia-hung Duan const scudo::uptr RegionSize =
4286fb70a8aSChia-hung Duan scudo::roundUpSlow(scudo::roundUp(BlockSize, PageSize) + ReleaseBase,
4296fb70a8aSChia-hung Duan BlockSize) +
4305b9d6097SChia-hung Duan BlockSize;
4315b9d6097SChia-hung Duan const scudo::uptr RoundedRegionSize = scudo::roundUp(RegionSize, PageSize);
4325b9d6097SChia-hung Duan
4335b9d6097SChia-hung Duan scudo::SinglyLinkedList<Batch> FreeList;
4345b9d6097SChia-hung Duan FreeList.clear();
4355b9d6097SChia-hung Duan
4365b9d6097SChia-hung Duan // Skip the blocks in the first page and add the remaining.
4375b9d6097SChia-hung Duan std::vector<scudo::uptr> Pages(RoundedRegionSize / PageSize, 0);
4386fb70a8aSChia-hung Duan for (scudo::uptr Block = scudo::roundUpSlow(ReleaseBase, BlockSize);
4395b9d6097SChia-hung Duan Block + BlockSize <= RoundedRegionSize; Block += BlockSize) {
4405b9d6097SChia-hung Duan for (scudo::uptr Page = Block / PageSize;
4415b9d6097SChia-hung Duan Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
4425b9d6097SChia-hung Duan ASSERT_LT(Page, Pages.size());
4435b9d6097SChia-hung Duan ++Pages[Page];
4445b9d6097SChia-hung Duan }
4455b9d6097SChia-hung Duan }
4465b9d6097SChia-hung Duan
4475b9d6097SChia-hung Duan // This follows the logic how we count the last page. It should be
448657d297aSChia-hung Duan // consistent with how markFreeBlocksInRegion() handles the last block.
4495b9d6097SChia-hung Duan if (RoundedRegionSize % BlockSize != 0)
4505b9d6097SChia-hung Duan ++Pages.back();
4515b9d6097SChia-hung Duan
4525b9d6097SChia-hung Duan Batch *CurrentBatch = nullptr;
4536fb70a8aSChia-hung Duan for (scudo::uptr Block = scudo::roundUpSlow(ReleaseBase, BlockSize);
4545b9d6097SChia-hung Duan Block < RegionSize; Block += BlockSize) {
4555b9d6097SChia-hung Duan if (CurrentBatch == nullptr ||
4565b9d6097SChia-hung Duan CurrentBatch->getCount() == Batch::MaxCount) {
4575b9d6097SChia-hung Duan CurrentBatch = new Batch;
4585b9d6097SChia-hung Duan CurrentBatch->clear();
4595b9d6097SChia-hung Duan FreeList.push_back(CurrentBatch);
4605b9d6097SChia-hung Duan }
4615b9d6097SChia-hung Duan CurrentBatch->add(Block);
4625b9d6097SChia-hung Duan }
4635b9d6097SChia-hung Duan
4645b9d6097SChia-hung Duan auto VerifyReleaseToOs = [&](scudo::PageReleaseContext &Context) {
4655b9d6097SChia-hung Duan auto SkipRegion = [](UNUSED scudo::uptr RegionIndex) { return false; };
4665b9d6097SChia-hung Duan ReleasedPagesRecorder Recorder(ReleaseBase);
4675b9d6097SChia-hung Duan releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
4686fb70a8aSChia-hung Duan const scudo::uptr FirstBlock = scudo::roundUpSlow(ReleaseBase, BlockSize);
4695b9d6097SChia-hung Duan
4705b9d6097SChia-hung Duan for (scudo::uptr P = 0; P < RoundedRegionSize; P += PageSize) {
4715b9d6097SChia-hung Duan if (P < FirstBlock) {
4725b9d6097SChia-hung Duan // If FirstBlock is not aligned with page boundary, the first touched
4735b9d6097SChia-hung Duan // page will not be released either.
4745b9d6097SChia-hung Duan EXPECT_TRUE(Recorder.ReportedPages.find(P) ==
4755b9d6097SChia-hung Duan Recorder.ReportedPages.end());
4765b9d6097SChia-hung Duan } else {
4775b9d6097SChia-hung Duan EXPECT_TRUE(Recorder.ReportedPages.find(P) !=
4785b9d6097SChia-hung Duan Recorder.ReportedPages.end());
4795b9d6097SChia-hung Duan }
4805b9d6097SChia-hung Duan }
4815b9d6097SChia-hung Duan };
4825b9d6097SChia-hung Duan
4835b9d6097SChia-hung Duan // Test marking by visiting each block.
4845b9d6097SChia-hung Duan {
4855b9d6097SChia-hung Duan auto DecompactPtr = [](scudo::uptr P) { return P; };
486657d297aSChia-hung Duan scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
487657d297aSChia-hung Duan /*ReleaseSize=*/RegionSize - PageSize,
488657d297aSChia-hung Duan ReleaseBase);
489657d297aSChia-hung Duan Context.markFreeBlocksInRegion(FreeList, DecompactPtr, /*Base=*/0U,
490657d297aSChia-hung Duan /*RegionIndex=*/0, RegionSize,
491657d297aSChia-hung Duan /*MayContainLastBlockInRegion=*/true);
4925b9d6097SChia-hung Duan for (const Batch &It : FreeList) {
4935b9d6097SChia-hung Duan for (scudo::u16 I = 0; I < It.getCount(); I++) {
4945b9d6097SChia-hung Duan scudo::uptr Block = It.get(I);
4955b9d6097SChia-hung Duan for (scudo::uptr Page = Block / PageSize;
4965b9d6097SChia-hung Duan Page <= (Block + BlockSize - 1) / PageSize; ++Page) {
4975b9d6097SChia-hung Duan EXPECT_EQ(Pages[Page], Context.PageMap.get(/*Region=*/0U,
4985b9d6097SChia-hung Duan Page - BasePageOffset));
4995b9d6097SChia-hung Duan }
5005b9d6097SChia-hung Duan }
5015b9d6097SChia-hung Duan }
5025b9d6097SChia-hung Duan
5035b9d6097SChia-hung Duan VerifyReleaseToOs(Context);
5045b9d6097SChia-hung Duan }
5055b9d6097SChia-hung Duan
5065b9d6097SChia-hung Duan // Test range marking.
5075b9d6097SChia-hung Duan {
508657d297aSChia-hung Duan scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
509657d297aSChia-hung Duan /*ReleaseSize=*/RegionSize - PageSize,
510657d297aSChia-hung Duan ReleaseBase);
511657d297aSChia-hung Duan Context.markRangeAsAllCounted(ReleaseBase, RegionSize, /*Base=*/0U,
512657d297aSChia-hung Duan /*RegionIndex=*/0, RegionSize);
5135b9d6097SChia-hung Duan for (scudo::uptr Page = ReleaseBase / PageSize;
5145b9d6097SChia-hung Duan Page < RoundedRegionSize / PageSize; ++Page) {
5155b9d6097SChia-hung Duan if (Context.PageMap.get(/*Region=*/0, Page - BasePageOffset) !=
5165b9d6097SChia-hung Duan Pages[Page]) {
5175b9d6097SChia-hung Duan EXPECT_TRUE(Context.PageMap.isAllCounted(/*Region=*/0,
5185b9d6097SChia-hung Duan Page - BasePageOffset));
5195b9d6097SChia-hung Duan }
5205b9d6097SChia-hung Duan }
5215b9d6097SChia-hung Duan
5225b9d6097SChia-hung Duan VerifyReleaseToOs(Context);
5235b9d6097SChia-hung Duan }
5245b9d6097SChia-hung Duan
5255b9d6097SChia-hung Duan // Check the buffer size of PageMap.
5265b9d6097SChia-hung Duan {
527657d297aSChia-hung Duan scudo::PageReleaseContext Full(BlockSize, /*NumberOfRegions=*/1U,
5285b9d6097SChia-hung Duan /*ReleaseSize=*/RegionSize);
5295b9d6097SChia-hung Duan Full.ensurePageMapAllocated();
530657d297aSChia-hung Duan scudo::PageReleaseContext Partial(BlockSize, /*NumberOfRegions=*/1U,
531657d297aSChia-hung Duan /*ReleaseSize=*/RegionSize - PageSize,
532657d297aSChia-hung Duan ReleaseBase);
5335b9d6097SChia-hung Duan Partial.ensurePageMapAllocated();
5345b9d6097SChia-hung Duan
53576a5602fSFabio D'Urso EXPECT_GE(Full.PageMap.getBufferNumElements(),
53676a5602fSFabio D'Urso Partial.PageMap.getBufferNumElements());
5375b9d6097SChia-hung Duan }
5385b9d6097SChia-hung Duan
5395b9d6097SChia-hung Duan while (!FreeList.empty()) {
5405b9d6097SChia-hung Duan CurrentBatch = FreeList.front();
5415b9d6097SChia-hung Duan FreeList.pop_front();
5425b9d6097SChia-hung Duan delete CurrentBatch;
5435b9d6097SChia-hung Duan }
5445b9d6097SChia-hung Duan } // Iterate each size class
5455b9d6097SChia-hung Duan }
5465b9d6097SChia-hung Duan
TEST(ScudoReleaseTest,ReleaseFreeMemoryToOSDefault)5473fa38318SNico Weber TEST(ScudoReleaseTest, ReleaseFreeMemoryToOSDefault) {
5483fa38318SNico Weber testReleaseFreeMemoryToOS<scudo::DefaultSizeClassMap>();
5493fa38318SNico Weber }
5503fa38318SNico Weber
TEST(ScudoReleaseTest,ReleaseFreeMemoryToOSAndroid)5513fa38318SNico Weber TEST(ScudoReleaseTest, ReleaseFreeMemoryToOSAndroid) {
5523fa38318SNico Weber testReleaseFreeMemoryToOS<scudo::AndroidSizeClassMap>();
5533fa38318SNico Weber }
5543fa38318SNico Weber
TEST(ScudoReleaseTest,PageMapMarkRange)555a78805f3SChia-hung Duan TEST(ScudoReleaseTest, PageMapMarkRange) {
556a78805f3SChia-hung Duan testPageMapMarkRange<scudo::DefaultSizeClassMap>();
557a78805f3SChia-hung Duan testPageMapMarkRange<scudo::AndroidSizeClassMap>();
558a78805f3SChia-hung Duan testPageMapMarkRange<scudo::FuchsiaSizeClassMap>();
559a78805f3SChia-hung Duan }
5605b9d6097SChia-hung Duan
TEST(ScudoReleaseTest,ReleasePartialRegion)5615b9d6097SChia-hung Duan TEST(ScudoReleaseTest, ReleasePartialRegion) {
5625b9d6097SChia-hung Duan testReleasePartialRegion<scudo::DefaultSizeClassMap>();
5635b9d6097SChia-hung Duan testReleasePartialRegion<scudo::AndroidSizeClassMap>();
5645b9d6097SChia-hung Duan testReleasePartialRegion<scudo::FuchsiaSizeClassMap>();
5655b9d6097SChia-hung Duan }
566c514198eSChia-hung Duan
testReleaseRangeWithSingleBlock()5676fb70a8aSChia-hung Duan template <class SizeClassMap> void testReleaseRangeWithSingleBlock() {
5686fb70a8aSChia-hung Duan const scudo::uptr PageSize = scudo::getPageSizeCached();
5696fb70a8aSChia-hung Duan
5706fb70a8aSChia-hung Duan // We want to test if a memory group only contains single block that will be
5716fb70a8aSChia-hung Duan // handled properly. The case is like:
5726fb70a8aSChia-hung Duan //
5736fb70a8aSChia-hung Duan // From To
5746fb70a8aSChia-hung Duan // +----------------------+
5756fb70a8aSChia-hung Duan // +------------+------------+
5766fb70a8aSChia-hung Duan // | | |
5776fb70a8aSChia-hung Duan // +------------+------------+
5786fb70a8aSChia-hung Duan // ^
5796fb70a8aSChia-hung Duan // RegionSize
5806fb70a8aSChia-hung Duan //
5816fb70a8aSChia-hung Duan // Note that `From` will be page aligned.
5826fb70a8aSChia-hung Duan //
5836fb70a8aSChia-hung Duan // If the second from the last block is aligned at `From`, then we expect all
5846fb70a8aSChia-hung Duan // the pages after `From` will be marked as can-be-released. Otherwise, the
5856fb70a8aSChia-hung Duan // pages only touched by the last blocks will be marked as can-be-released.
5866fb70a8aSChia-hung Duan for (scudo::uptr I = 1; I <= SizeClassMap::LargestClassId; I++) {
5876fb70a8aSChia-hung Duan const scudo::uptr BlockSize = SizeClassMap::getSizeByClassId(I);
5886fb70a8aSChia-hung Duan const scudo::uptr From = scudo::roundUp(BlockSize, PageSize);
5896fb70a8aSChia-hung Duan const scudo::uptr To =
5906fb70a8aSChia-hung Duan From % BlockSize == 0
5916fb70a8aSChia-hung Duan ? From + BlockSize
5926fb70a8aSChia-hung Duan : scudo::roundDownSlow(From + BlockSize, BlockSize) + BlockSize;
5936fb70a8aSChia-hung Duan const scudo::uptr RoundedRegionSize = scudo::roundUp(To, PageSize);
5946fb70a8aSChia-hung Duan
5956fb70a8aSChia-hung Duan std::vector<scudo::uptr> Pages(RoundedRegionSize / PageSize, 0);
5966fb70a8aSChia-hung Duan for (scudo::uptr Block = (To - BlockSize); Block < RoundedRegionSize;
5976fb70a8aSChia-hung Duan Block += BlockSize) {
5986fb70a8aSChia-hung Duan for (scudo::uptr Page = Block / PageSize;
5996fb70a8aSChia-hung Duan Page <= (Block + BlockSize - 1) / PageSize &&
6006fb70a8aSChia-hung Duan Page < RoundedRegionSize / PageSize;
6016fb70a8aSChia-hung Duan ++Page) {
6026fb70a8aSChia-hung Duan ASSERT_LT(Page, Pages.size());
6036fb70a8aSChia-hung Duan ++Pages[Page];
6046fb70a8aSChia-hung Duan }
6056fb70a8aSChia-hung Duan }
6066fb70a8aSChia-hung Duan
6076fb70a8aSChia-hung Duan scudo::PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
6086fb70a8aSChia-hung Duan /*ReleaseSize=*/To,
6096fb70a8aSChia-hung Duan /*ReleaseBase=*/0U);
6106fb70a8aSChia-hung Duan Context.markRangeAsAllCounted(From, To, /*Base=*/0U, /*RegionIndex=*/0,
6116fb70a8aSChia-hung Duan /*RegionSize=*/To);
6126fb70a8aSChia-hung Duan
6136fb70a8aSChia-hung Duan for (scudo::uptr Page = 0; Page < RoundedRegionSize; Page += PageSize) {
6146fb70a8aSChia-hung Duan if (Context.PageMap.get(/*Region=*/0U, Page / PageSize) !=
6156fb70a8aSChia-hung Duan Pages[Page / PageSize]) {
6166fb70a8aSChia-hung Duan EXPECT_TRUE(
6176fb70a8aSChia-hung Duan Context.PageMap.isAllCounted(/*Region=*/0U, Page / PageSize));
6186fb70a8aSChia-hung Duan }
6196fb70a8aSChia-hung Duan }
6206fb70a8aSChia-hung Duan } // for each size class
6216fb70a8aSChia-hung Duan }
6226fb70a8aSChia-hung Duan
TEST(ScudoReleaseTest,RangeReleaseRegionWithSingleBlock)6236fb70a8aSChia-hung Duan TEST(ScudoReleaseTest, RangeReleaseRegionWithSingleBlock) {
6246fb70a8aSChia-hung Duan testReleaseRangeWithSingleBlock<scudo::DefaultSizeClassMap>();
6256fb70a8aSChia-hung Duan testReleaseRangeWithSingleBlock<scudo::AndroidSizeClassMap>();
6266fb70a8aSChia-hung Duan testReleaseRangeWithSingleBlock<scudo::FuchsiaSizeClassMap>();
6276fb70a8aSChia-hung Duan }
6286fb70a8aSChia-hung Duan
TEST(ScudoReleaseTest,BufferPool)629c514198eSChia-hung Duan TEST(ScudoReleaseTest, BufferPool) {
630c514198eSChia-hung Duan constexpr scudo::uptr StaticBufferCount = SCUDO_WORDSIZE - 1;
63176a5602fSFabio D'Urso constexpr scudo::uptr StaticBufferNumElements = 512U;
632248ee65aSFabio D'Urso
633248ee65aSFabio D'Urso // Allocate the buffer pool on the heap because it is quite large (slightly
63476a5602fSFabio D'Urso // more than StaticBufferCount * StaticBufferNumElements * sizeof(uptr)) and
63576a5602fSFabio D'Urso // it may not fit in the stack on some platforms.
63676a5602fSFabio D'Urso using BufferPool =
63776a5602fSFabio D'Urso scudo::BufferPool<StaticBufferCount, StaticBufferNumElements>;
638248ee65aSFabio D'Urso std::unique_ptr<BufferPool> Pool(new BufferPool());
639c514198eSChia-hung Duan
640*4b189961SFabio D'Urso std::vector<BufferPool::Buffer> Buffers;
641c514198eSChia-hung Duan for (scudo::uptr I = 0; I < StaticBufferCount; ++I) {
642*4b189961SFabio D'Urso BufferPool::Buffer Buffer = Pool->getBuffer(StaticBufferNumElements);
643*4b189961SFabio D'Urso EXPECT_TRUE(Pool->isStaticBufferTestOnly(Buffer));
644*4b189961SFabio D'Urso Buffers.push_back(Buffer);
645c514198eSChia-hung Duan }
646c514198eSChia-hung Duan
647c514198eSChia-hung Duan // The static buffer is supposed to be used up.
648*4b189961SFabio D'Urso BufferPool::Buffer Buffer = Pool->getBuffer(StaticBufferNumElements);
649*4b189961SFabio D'Urso EXPECT_FALSE(Pool->isStaticBufferTestOnly(Buffer));
650c514198eSChia-hung Duan
651*4b189961SFabio D'Urso Pool->releaseBuffer(Buffer);
652c514198eSChia-hung Duan for (auto &Buffer : Buffers)
653*4b189961SFabio D'Urso Pool->releaseBuffer(Buffer);
654c514198eSChia-hung Duan }
655