xref: /llvm-project/compiler-rt/lib/scudo/standalone/tests/release_test.cpp (revision 4b189961c579c512f0034b1aa9ae69715948fecf)
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