blob: 2dc041b94a8c0b94debed8c6afbca5de1c3dbea1 [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;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080030 void *P = L->allocate(scudo::Options{}, Size);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000031 EXPECT_NE(P, nullptr);
32 memset(P, 'A', Size);
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070033 EXPECT_GE(SecondaryT::getBlockSize(P), Size);
Peter Collingbournecc3d4932020-12-21 18:39:03 -080034 L->deallocate(scudo::Options{}, P);
Mitch Phillipsa99b7792021-05-20 10:56:47 -070035
Dynamic Tools Teamc5d5abc2020-01-27 14:03:21 -080036 // If the Secondary can't cache that pointer, it will be unmapped.
Vitaly Buka5cb96362021-05-20 16:17:25 -070037 if (!L->canCache(Size)) {
38 EXPECT_DEATH(
39 {
40 // Repeat few time to avoid missing crash if it's mmaped by unrelated
41 // code.
42 for (int i = 0; i < 10; ++i) {
43 P = L->allocate(scudo::Options{}, Size);
44 L->deallocate(scudo::Options{}, P);
45 memset(P, 'A', Size);
46 }
47 },
48 "");
Vitaly Buka5cb96362021-05-20 16:17:25 -070049 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +000050
51 const scudo::uptr Align = 1U << 16;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080052 P = L->allocate(scudo::Options{}, Size + Align, Align);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000053 EXPECT_NE(P, nullptr);
54 void *AlignedP = reinterpret_cast<void *>(
55 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
56 memset(AlignedP, 'A', Size);
Peter Collingbournecc3d4932020-12-21 18:39:03 -080057 L->deallocate(scudo::Options{}, P);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000058
59 std::vector<void *> V;
60 for (scudo::uptr I = 0; I < 32U; I++)
Peter Collingbournecc3d4932020-12-21 18:39:03 -080061 V.push_back(L->allocate(scudo::Options{}, Size));
Dynamic Tools Teamd2740c52019-11-19 13:58:06 -080062 std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()()));
Dynamic Tools Team517193e2019-09-11 14:48:41 +000063 while (!V.empty()) {
Peter Collingbournecc3d4932020-12-21 18:39:03 -080064 L->deallocate(scudo::Options{}, V.back());
Dynamic Tools Team517193e2019-09-11 14:48:41 +000065 V.pop_back();
66 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -070067 scudo::ScopedString Str;
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +000068 L->getStats(&Str);
69 Str.output();
Mitch Phillipsda86a542021-05-24 16:08:57 -070070 L->unmapTestOnly();
Dynamic Tools Team517193e2019-09-11 14:48:41 +000071}
72
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080073struct NoCacheConfig {
74 typedef scudo::MapAllocatorNoCache SecondaryCache;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080075 static const bool MaySupportMemoryTagging = false;
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080076};
77
Peter Collingbourne7488a172020-12-14 13:57:59 -080078struct TestConfig {
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080079 typedef scudo::MapAllocatorCache<TestConfig> SecondaryCache;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080080 static const bool MaySupportMemoryTagging = false;
Peter Collingbourne7488a172020-12-14 13:57:59 -080081 static const scudo::u32 SecondaryCacheEntriesArraySize = 128U;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080082 static const scudo::u32 SecondaryCacheQuarantineSize = 0U;
Peter Collingbourne7488a172020-12-14 13:57:59 -080083 static const scudo::u32 SecondaryCacheDefaultMaxEntriesCount = 64U;
84 static const scudo::uptr SecondaryCacheDefaultMaxEntrySize = 1UL << 20;
85 static const scudo::s32 SecondaryCacheMinReleaseToOsIntervalMs = INT32_MIN;
86 static const scudo::s32 SecondaryCacheMaxReleaseToOsIntervalMs = INT32_MAX;
87};
88
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070089TEST(ScudoSecondaryTest, SecondaryBasic) {
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080090 testSecondaryBasic<NoCacheConfig>();
91 testSecondaryBasic<scudo::DefaultConfig>();
92 testSecondaryBasic<TestConfig>();
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070093}
94
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080095using LargeAllocator = scudo::MapAllocator<scudo::DefaultConfig>;
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070096
Dynamic Tools Team517193e2019-09-11 14:48:41 +000097// This exercises a variety of combinations of size and alignment for the
98// MapAllocator. The size computation done here mimic the ones done by the
99// combined allocator.
100TEST(ScudoSecondaryTest, SecondaryCombinations) {
101 constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16);
102 constexpr scudo::uptr HeaderSize = scudo::roundUpTo(8, MinAlign);
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700103 std::unique_ptr<LargeAllocator> L(new LargeAllocator);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000104 L->init(nullptr);
105 for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) {
106 for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16;
107 AlignLog++) {
108 const scudo::uptr Align = 1U << AlignLog;
109 for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) {
110 if (static_cast<scudo::sptr>(1U << SizeLog) + Delta <= 0)
111 continue;
112 const scudo::uptr UserSize =
113 scudo::roundUpTo((1U << SizeLog) + Delta, MinAlign);
114 const scudo::uptr Size =
115 HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0);
Peter Collingbournecc3d4932020-12-21 18:39:03 -0800116 void *P = L->allocate(scudo::Options{}, Size, Align);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000117 EXPECT_NE(P, nullptr);
118 void *AlignedP = reinterpret_cast<void *>(
119 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
120 memset(AlignedP, 0xff, UserSize);
Peter Collingbournecc3d4932020-12-21 18:39:03 -0800121 L->deallocate(scudo::Options{}, P);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000122 }
123 }
124 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700125 scudo::ScopedString Str;
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000126 L->getStats(&Str);
127 Str.output();
Mitch Phillipsda86a542021-05-24 16:08:57 -0700128 L->unmapTestOnly();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000129}
130
131TEST(ScudoSecondaryTest, SecondaryIterate) {
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700132 std::unique_ptr<LargeAllocator> L(new LargeAllocator);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000133 L->init(nullptr);
134 std::vector<void *> V;
135 const scudo::uptr PageSize = scudo::getPageSizeCached();
136 for (scudo::uptr I = 0; I < 32U; I++)
Peter Collingbournecc3d4932020-12-21 18:39:03 -0800137 V.push_back(L->allocate(scudo::Options{}, (std::rand() % 16) * PageSize));
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000138 auto Lambda = [V](scudo::uptr Block) {
139 EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)),
140 V.end());
141 };
142 L->disable();
143 L->iterateOverBlocks(Lambda);
144 L->enable();
145 while (!V.empty()) {
Peter Collingbournecc3d4932020-12-21 18:39:03 -0800146 L->deallocate(scudo::Options{}, V.back());
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000147 V.pop_back();
148 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700149 scudo::ScopedString Str;
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000150 L->getStats(&Str);
151 Str.output();
Mitch Phillipsda86a542021-05-24 16:08:57 -0700152 L->unmapTestOnly();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000153}
154
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700155TEST(ScudoSecondaryTest, SecondaryOptions) {
156 std::unique_ptr<LargeAllocator> L(new LargeAllocator);
157 L->init(nullptr);
158 // Attempt to set a maximum number of entries higher than the array size.
159 EXPECT_FALSE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4096U));
160 // A negative number will be cast to a scudo::u32, and fail.
161 EXPECT_FALSE(L->setOption(scudo::Option::MaxCacheEntriesCount, -1));
162 if (L->canCache(0U)) {
163 // Various valid combinations.
164 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
165 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
166 EXPECT_TRUE(L->canCache(1UL << 18));
167 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17));
168 EXPECT_FALSE(L->canCache(1UL << 18));
169 EXPECT_TRUE(L->canCache(1UL << 16));
170 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 0U));
171 EXPECT_FALSE(L->canCache(1UL << 16));
172 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
173 EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
174 EXPECT_TRUE(L->canCache(1UL << 16));
175 }
Mitch Phillipsda86a542021-05-24 16:08:57 -0700176 L->unmapTestOnly();
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700177}
178
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000179static std::mutex Mutex;
180static std::condition_variable Cv;
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700181static bool Ready;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000182
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -0700183static void performAllocations(LargeAllocator *L) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000184 std::vector<void *> V;
185 const scudo::uptr PageSize = scudo::getPageSizeCached();
186 {
187 std::unique_lock<std::mutex> Lock(Mutex);
188 while (!Ready)
189 Cv.wait(Lock);
190 }
Dynamic Tools Teamfa69c702020-02-05 09:58:52 -0800191 for (scudo::uptr I = 0; I < 128U; I++) {
192 // Deallocate 75% of the blocks.
193 const bool Deallocate = (rand() & 3) != 0;
Peter Collingbournecc3d4932020-12-21 18:39:03 -0800194 void *P = L->allocate(scudo::Options{}, (std::rand() % 16) * PageSize);
Dynamic Tools Teamfa69c702020-02-05 09:58:52 -0800195 if (Deallocate)
Peter Collingbournecc3d4932020-12-21 18:39:03 -0800196 L->deallocate(scudo::Options{}, P);
Dynamic Tools Teamfa69c702020-02-05 09:58:52 -0800197 else
198 V.push_back(P);
199 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000200 while (!V.empty()) {
Peter Collingbournecc3d4932020-12-21 18:39:03 -0800201 L->deallocate(scudo::Options{}, V.back());
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000202 V.pop_back();
203 }
204}
205
206TEST(ScudoSecondaryTest, SecondaryThreadsRace) {
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700207 Ready = false;
208 std::unique_ptr<LargeAllocator> L(new LargeAllocator);
Dynamic Tools Teamfa69c702020-02-05 09:58:52 -0800209 L->init(nullptr, /*ReleaseToOsInterval=*/0);
210 std::thread Threads[16];
211 for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++)
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700212 Threads[I] = std::thread(performAllocations, L.get());
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000213 {
214 std::unique_lock<std::mutex> Lock(Mutex);
215 Ready = true;
216 Cv.notify_all();
217 }
218 for (auto &T : Threads)
219 T.join();
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700220 scudo::ScopedString Str;
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000221 L->getStats(&Str);
222 Str.output();
Mitch Phillipsda86a542021-05-24 16:08:57 -0700223 L->unmapTestOnly();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000224}