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 "secondary.h"
10 
11 #include "gtest/gtest.h"
12 
13 #include <stdio.h>
14 
15 #include <condition_variable>
16 #include <mutex>
17 #include <thread>
18 
19 TEST(ScudoSecondaryTest, SecondaryBasic) {
20   scudo::GlobalStats S;
21   S.init();
22   scudo::MapAllocator *L = new scudo::MapAllocator;
23   L->init(&S);
24   const scudo::uptr Size = 1U << 16;
25   void *P = L->allocate(Size);
26   EXPECT_NE(P, nullptr);
27   memset(P, 'A', Size);
28   EXPECT_GE(scudo::MapAllocator::getBlockSize(P), Size);
29   L->deallocate(P);
30   EXPECT_DEATH(memset(P, 'A', Size), "");
31 
32   const scudo::uptr Align = 1U << 16;
33   P = L->allocate(Size + Align, Align);
34   EXPECT_NE(P, nullptr);
35   void *AlignedP = reinterpret_cast<void *>(
36       scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
37   memset(AlignedP, 'A', Size);
38   L->deallocate(P);
39 
40   std::vector<void *> V;
41   for (scudo::uptr I = 0; I < 32U; I++)
42     V.push_back(L->allocate(Size));
43   std::random_shuffle(V.begin(), V.end());
44   while (!V.empty()) {
45     L->deallocate(V.back());
46     V.pop_back();
47   }
48   L->printStats();
49 }
50 
51 // This exercises a variety of combinations of size and alignment for the
52 // MapAllocator. The size computation done here mimic the ones done by the
53 // combined allocator.
54 TEST(ScudoSecondaryTest, SecondaryCombinations) {
55   constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16);
56   constexpr scudo::uptr HeaderSize = scudo::roundUpTo(8, MinAlign);
57   scudo::MapAllocator *L = new scudo::MapAllocator;
58   L->init(nullptr);
59   for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) {
60     for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16;
61          AlignLog++) {
62       const scudo::uptr Align = 1U << AlignLog;
63       for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) {
64         if (static_cast<scudo::sptr>(1U << SizeLog) + Delta <= 0)
65           continue;
66         const scudo::uptr UserSize =
67             scudo::roundUpTo((1U << SizeLog) + Delta, MinAlign);
68         const scudo::uptr Size =
69             HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0);
70         void *P = L->allocate(Size, Align);
71         EXPECT_NE(P, nullptr);
72         void *AlignedP = reinterpret_cast<void *>(
73             scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
74         memset(AlignedP, 0xff, UserSize);
75         L->deallocate(P);
76       }
77     }
78   }
79   L->printStats();
80 }
81 
82 TEST(ScudoSecondaryTest, SecondaryIterate) {
83   scudo::MapAllocator *L = new scudo::MapAllocator;
84   L->init(nullptr);
85   std::vector<void *> V;
86   const scudo::uptr PageSize = scudo::getPageSizeCached();
87   for (scudo::uptr I = 0; I < 32U; I++)
88     V.push_back(L->allocate((std::rand() % 16) * PageSize));
89   auto Lambda = [V](scudo::uptr Block) {
90     EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)),
91               V.end());
92   };
93   L->disable();
94   L->iterateOverBlocks(Lambda);
95   L->enable();
96   while (!V.empty()) {
97     L->deallocate(V.back());
98     V.pop_back();
99   }
100   L->printStats();
101 }
102 
103 static std::mutex Mutex;
104 static std::condition_variable Cv;
105 static bool Ready = false;
106 
107 static void performAllocations(scudo::MapAllocator *L) {
108   std::vector<void *> V;
109   const scudo::uptr PageSize = scudo::getPageSizeCached();
110   {
111     std::unique_lock<std::mutex> Lock(Mutex);
112     while (!Ready)
113       Cv.wait(Lock);
114   }
115   for (scudo::uptr I = 0; I < 32U; I++)
116     V.push_back(L->allocate((std::rand() % 16) * PageSize));
117   while (!V.empty()) {
118     L->deallocate(V.back());
119     V.pop_back();
120   }
121 }
122 
123 TEST(ScudoSecondaryTest, SecondaryThreadsRace) {
124   scudo::MapAllocator *L = new scudo::MapAllocator;
125   L->init(nullptr);
126   std::thread Threads[10];
127   for (scudo::uptr I = 0; I < 10U; I++)
128     Threads[I] = std::thread(performAllocations, L);
129   {
130     std::unique_lock<std::mutex> Lock(Mutex);
131     Ready = true;
132     Cv.notify_all();
133   }
134   for (auto &T : Threads)
135     T.join();
136   L->printStats();
137 }
138