blob: 846ec8f6d6faae494e24288495d0817c13b169cd [file] [log] [blame]
Dynamic Tools Team517193e2019-09-11 14:48:41 +00001//===-- 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
Dynamic Tools Team09e6d482019-11-26 18:18:14 -08009#include "tests/scudo_unit_test.h"
Dynamic Tools Team517193e2019-09-11 14:48:41 +000010
Peter Collingbourne7488a172020-12-14 13:57:59 -080011#include "allocator_config.h"
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080012#include "secondary.h"
Dynamic Tools Team517193e2019-09-11 14:48:41 +000013
14#include <stdio.h>
15
16#include <condition_variable>
17#include <mutex>
Dynamic Tools Teamd2740c52019-11-19 13:58:06 -080018#include <random>
Dynamic Tools Team517193e2019-09-11 14:48:41 +000019#include <thread>
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080020#include <vector>
Dynamic Tools Team517193e2019-09-11 14:48:41 +000021
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080022template <typename Config> static void testSecondaryBasic(void) {
23 using SecondaryT = scudo::MapAllocator<Config>;
24
Dynamic Tools Team517193e2019-09-11 14:48:41 +000025 scudo::GlobalStats S;
26 S.init();
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -070027 std::unique_ptr<SecondaryT> L(new SecondaryT);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000028 L->init(&S);
29 const scudo::uptr Size = 1U << 16;
30 void *P = L->allocate(Size);
31 EXPECT_NE(P, nullptr);
32 memset(P, 'A', Size);
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070033 EXPECT_GE(SecondaryT::getBlockSize(P), Size);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000034 L->deallocate(P);
Dynamic Tools Teamc5d5abc2020-01-27 14:03:21 -080035 // If the Secondary can't cache that pointer, it will be unmapped.
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -070036 if (!L->canCache(Size))
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070037 EXPECT_DEATH(memset(P, 'A', Size), "");
Dynamic Tools Team517193e2019-09-11 14:48:41 +000038
39 const scudo::uptr Align = 1U << 16;
40 P = L->allocate(Size + Align, Align);
41 EXPECT_NE(P, nullptr);
42 void *AlignedP = reinterpret_cast<void *>(
43 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
44 memset(AlignedP, 'A', Size);
45 L->deallocate(P);
46
47 std::vector<void *> V;
48 for (scudo::uptr I = 0; I < 32U; I++)
49 V.push_back(L->allocate(Size));
Dynamic Tools Teamd2740c52019-11-19 13:58:06 -080050 std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()()));
Dynamic Tools Team517193e2019-09-11 14:48:41 +000051 while (!V.empty()) {
52 L->deallocate(V.back());
53 V.pop_back();
54 }
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +000055 scudo::ScopedString Str(1024);
56 L->getStats(&Str);
57 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +000058}
59
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080060struct NoCacheConfig {
61 typedef scudo::MapAllocatorNoCache SecondaryCache;
62};
63
Peter Collingbourne7488a172020-12-14 13:57:59 -080064struct TestConfig {
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080065 typedef scudo::MapAllocatorCache<TestConfig> SecondaryCache;
Peter Collingbourne7488a172020-12-14 13:57:59 -080066 static const scudo::u32 SecondaryCacheEntriesArraySize = 128U;
67 static const scudo::u32 SecondaryCacheDefaultMaxEntriesCount = 64U;
68 static const scudo::uptr SecondaryCacheDefaultMaxEntrySize = 1UL << 20;
69 static const scudo::s32 SecondaryCacheMinReleaseToOsIntervalMs = INT32_MIN;
70 static const scudo::s32 SecondaryCacheMaxReleaseToOsIntervalMs = INT32_MAX;
71};
72
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070073TEST(ScudoSecondaryTest, SecondaryBasic) {
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080074 testSecondaryBasic<NoCacheConfig>();
75 testSecondaryBasic<scudo::DefaultConfig>();
76 testSecondaryBasic<TestConfig>();
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070077}
78
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080079using LargeAllocator = scudo::MapAllocator<scudo::DefaultConfig>;
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070080
Dynamic Tools Team517193e2019-09-11 14:48:41 +000081// This exercises a variety of combinations of size and alignment for the
82// MapAllocator. The size computation done here mimic the ones done by the
83// combined allocator.
84TEST(ScudoSecondaryTest, SecondaryCombinations) {
85 constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16);
86 constexpr scudo::uptr HeaderSize = scudo::roundUpTo(8, MinAlign);
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -070087 std::unique_ptr<LargeAllocator> L(new LargeAllocator);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000088 L->init(nullptr);
89 for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) {
90 for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16;
91 AlignLog++) {
92 const scudo::uptr Align = 1U << AlignLog;
93 for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) {
94 if (static_cast<scudo::sptr>(1U << SizeLog) + Delta <= 0)
95 continue;
96 const scudo::uptr UserSize =
97 scudo::roundUpTo((1U << SizeLog) + Delta, MinAlign);
98 const scudo::uptr Size =
99 HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0);
100 void *P = L->allocate(Size, Align);
101 EXPECT_NE(P, nullptr);
102 void *AlignedP = reinterpret_cast<void *>(
103 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
104 memset(AlignedP, 0xff, UserSize);
105 L->deallocate(P);
106 }
107 }
108 }
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000109 scudo::ScopedString Str(1024);
110 L->getStats(&Str);
111 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000112}
113
114TEST(ScudoSecondaryTest, SecondaryIterate) {
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700115 std::unique_ptr<LargeAllocator> L(new LargeAllocator);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000116 L->init(nullptr);
117 std::vector<void *> V;
118 const scudo::uptr PageSize = scudo::getPageSizeCached();
119 for (scudo::uptr I = 0; I < 32U; I++)
120 V.push_back(L->allocate((std::rand() % 16) * PageSize));
121 auto Lambda = [V](scudo::uptr Block) {
122 EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)),
123 V.end());
124 };
125 L->disable();
126 L->iterateOverBlocks(Lambda);
127 L->enable();
128 while (!V.empty()) {
129 L->deallocate(V.back());
130 V.pop_back();
131 }
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000132 scudo::ScopedString Str(1024);
133 L->getStats(&Str);
134 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000135}
136
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700137TEST(ScudoSecondaryTest, SecondaryOptions) {
138 std::unique_ptr<LargeAllocator> L(new LargeAllocator);
139 L->init(nullptr);
140 // Attempt to set a maximum number of entries higher than the array size.
141 EXPECT_FALSE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4096U));
142 // A negative number will be cast to a scudo::u32, and fail.
143 EXPECT_FALSE(L->setOption(scudo::Option::MaxCacheEntriesCount, -1));
144 if (L->canCache(0U)) {
145 // Various valid combinations.
146 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
147 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
148 EXPECT_TRUE(L->canCache(1UL << 18));
149 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17));
150 EXPECT_FALSE(L->canCache(1UL << 18));
151 EXPECT_TRUE(L->canCache(1UL << 16));
152 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 0U));
153 EXPECT_FALSE(L->canCache(1UL << 16));
154 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
155 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
156 EXPECT_TRUE(L->canCache(1UL << 16));
157 }
158}
159
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000160static std::mutex Mutex;
161static std::condition_variable Cv;
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700162static bool Ready;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000163
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -0700164static void performAllocations(LargeAllocator *L) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000165 std::vector<void *> V;
166 const scudo::uptr PageSize = scudo::getPageSizeCached();
167 {
168 std::unique_lock<std::mutex> Lock(Mutex);
169 while (!Ready)
170 Cv.wait(Lock);
171 }
Dynamic Tools Teamfa69c702020-02-05 09:58:52 -0800172 for (scudo::uptr I = 0; I < 128U; I++) {
173 // Deallocate 75% of the blocks.
174 const bool Deallocate = (rand() & 3) != 0;
175 void *P = L->allocate((std::rand() % 16) * PageSize);
176 if (Deallocate)
177 L->deallocate(P);
178 else
179 V.push_back(P);
180 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000181 while (!V.empty()) {
182 L->deallocate(V.back());
183 V.pop_back();
184 }
185}
186
187TEST(ScudoSecondaryTest, SecondaryThreadsRace) {
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700188 Ready = false;
189 std::unique_ptr<LargeAllocator> L(new LargeAllocator);
Dynamic Tools Teamfa69c702020-02-05 09:58:52 -0800190 L->init(nullptr, /*ReleaseToOsInterval=*/0);
191 std::thread Threads[16];
192 for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++)
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700193 Threads[I] = std::thread(performAllocations, L.get());
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000194 {
195 std::unique_lock<std::mutex> Lock(Mutex);
196 Ready = true;
197 Cv.notify_all();
198 }
199 for (auto &T : Threads)
200 T.join();
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000201 scudo::ScopedString Str(1024);
202 L->getStats(&Str);
203 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000204}