xref: /openbsd-src/gnu/llvm/compiler-rt/lib/scudo/standalone/tests/secondary_test.cpp (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 //===-- secondary_test.cpp --------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "tests/scudo_unit_test.h"
10 
11 #include "secondary.h"
12 
13 #include <stdio.h>
14 
15 #include <condition_variable>
16 #include <mutex>
17 #include <random>
18 #include <thread>
19 #include <vector>
20 
21 template <class SecondaryT> static void testSecondaryBasic(void) {
22   scudo::GlobalStats S;
23   S.init();
24   SecondaryT *L = new SecondaryT;
25   L->init(&S);
26   const scudo::uptr Size = 1U << 16;
27   void *P = L->allocate(Size);
28   EXPECT_NE(P, nullptr);
29   memset(P, 'A', Size);
30   EXPECT_GE(SecondaryT::getBlockSize(P), Size);
31   L->deallocate(P);
32   // If we are not using a free list, blocks are unmapped on deallocation.
33   if (SecondaryT::getMaxFreeListSize() == 0U)
34     EXPECT_DEATH(memset(P, 'A', Size), "");
35 
36   const scudo::uptr Align = 1U << 16;
37   P = L->allocate(Size + Align, Align);
38   EXPECT_NE(P, nullptr);
39   void *AlignedP = reinterpret_cast<void *>(
40       scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
41   memset(AlignedP, 'A', Size);
42   L->deallocate(P);
43 
44   std::vector<void *> V;
45   for (scudo::uptr I = 0; I < 32U; I++)
46     V.push_back(L->allocate(Size));
47   std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()()));
48   while (!V.empty()) {
49     L->deallocate(V.back());
50     V.pop_back();
51   }
52   scudo::ScopedString Str(1024);
53   L->getStats(&Str);
54   Str.output();
55 }
56 
57 TEST(ScudoSecondaryTest, SecondaryBasic) {
58   testSecondaryBasic<scudo::MapAllocator<0U>>();
59 #if !SCUDO_FUCHSIA
60   testSecondaryBasic<scudo::MapAllocator<>>();
61   testSecondaryBasic<scudo::MapAllocator<64U>>();
62 #endif
63 }
64 
65 #if SCUDO_FUCHSIA
66 using LargeAllocator = scudo::MapAllocator<0U>;
67 #else
68 using LargeAllocator = scudo::MapAllocator<>;
69 #endif
70 
71 // This exercises a variety of combinations of size and alignment for the
72 // MapAllocator. The size computation done here mimic the ones done by the
73 // combined allocator.
74 TEST(ScudoSecondaryTest, SecondaryCombinations) {
75   constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16);
76   constexpr scudo::uptr HeaderSize = scudo::roundUpTo(8, MinAlign);
77   LargeAllocator *L = new LargeAllocator;
78   L->init(nullptr);
79   for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) {
80     for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16;
81          AlignLog++) {
82       const scudo::uptr Align = 1U << AlignLog;
83       for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) {
84         if (static_cast<scudo::sptr>(1U << SizeLog) + Delta <= 0)
85           continue;
86         const scudo::uptr UserSize =
87             scudo::roundUpTo((1U << SizeLog) + Delta, MinAlign);
88         const scudo::uptr Size =
89             HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0);
90         void *P = L->allocate(Size, Align);
91         EXPECT_NE(P, nullptr);
92         void *AlignedP = reinterpret_cast<void *>(
93             scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
94         memset(AlignedP, 0xff, UserSize);
95         L->deallocate(P);
96       }
97     }
98   }
99   scudo::ScopedString Str(1024);
100   L->getStats(&Str);
101   Str.output();
102 }
103 
104 TEST(ScudoSecondaryTest, SecondaryIterate) {
105   LargeAllocator *L = new LargeAllocator;
106   L->init(nullptr);
107   std::vector<void *> V;
108   const scudo::uptr PageSize = scudo::getPageSizeCached();
109   for (scudo::uptr I = 0; I < 32U; I++)
110     V.push_back(L->allocate((std::rand() % 16) * PageSize));
111   auto Lambda = [V](scudo::uptr Block) {
112     EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)),
113               V.end());
114   };
115   L->disable();
116   L->iterateOverBlocks(Lambda);
117   L->enable();
118   while (!V.empty()) {
119     L->deallocate(V.back());
120     V.pop_back();
121   }
122   scudo::ScopedString Str(1024);
123   L->getStats(&Str);
124   Str.output();
125 }
126 
127 static std::mutex Mutex;
128 static std::condition_variable Cv;
129 static bool Ready = false;
130 
131 static void performAllocations(LargeAllocator *L) {
132   std::vector<void *> V;
133   const scudo::uptr PageSize = scudo::getPageSizeCached();
134   {
135     std::unique_lock<std::mutex> Lock(Mutex);
136     while (!Ready)
137       Cv.wait(Lock);
138   }
139   for (scudo::uptr I = 0; I < 32U; I++)
140     V.push_back(L->allocate((std::rand() % 16) * PageSize));
141   while (!V.empty()) {
142     L->deallocate(V.back());
143     V.pop_back();
144   }
145 }
146 
147 TEST(ScudoSecondaryTest, SecondaryThreadsRace) {
148   LargeAllocator *L = new LargeAllocator;
149   L->init(nullptr);
150   std::thread Threads[10];
151   for (scudo::uptr I = 0; I < 10U; I++)
152     Threads[I] = std::thread(performAllocations, L);
153   {
154     std::unique_lock<std::mutex> Lock(Mutex);
155     Ready = true;
156     Cv.notify_all();
157   }
158   for (auto &T : Threads)
159     T.join();
160   scudo::ScopedString Str(1024);
161   L->getStats(&Str);
162   Str.output();
163 }
164