blob: d3b7c486f7c3e21778a740fe4d6c1e70d08a125b [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
Vitaly Bukaace22c72021-06-30 23:39:12 -07009#include "memtag.h"
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080010#include "tests/scudo_unit_test.h"
Dynamic Tools Team517193e2019-09-11 14:48:41 +000011
Peter Collingbourne7488a172020-12-14 13:57:59 -080012#include "allocator_config.h"
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080013#include "secondary.h"
Dynamic Tools Team517193e2019-09-11 14:48:41 +000014
Dynamic Tools Team517193e2019-09-11 14:48:41 +000015#include <condition_variable>
Vitaly Bukad3199322021-07-01 10:22:35 -070016#include <memory>
Dynamic Tools Team517193e2019-09-11 14:48:41 +000017#include <mutex>
Dynamic Tools Teamd2740c52019-11-19 13:58:06 -080018#include <random>
Vitaly Bukad3199322021-07-01 10:22:35 -070019#include <stdio.h>
Dynamic Tools Team517193e2019-09-11 14:48:41 +000020#include <thread>
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080021#include <vector>
Dynamic Tools Team517193e2019-09-11 14:48:41 +000022
Vitaly Buka97742dd2021-07-01 11:56:11 -070023template <typename Config> static scudo::Options getOptionsForConfig() {
Vitaly Bukaace22c72021-06-30 23:39:12 -070024 if (!Config::MaySupportMemoryTagging || !scudo::archSupportsMemoryTagging())
25 return {};
26 scudo::AtomicOptions AO;
27 AO.set(scudo::OptionBit::UseMemoryTagging);
28 return AO.load();
Vitaly Buka97742dd2021-07-01 11:56:11 -070029}
30
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080031template <typename Config> static void testSecondaryBasic(void) {
32 using SecondaryT = scudo::MapAllocator<Config>;
Vitaly Buka97742dd2021-07-01 11:56:11 -070033 scudo::Options Options = getOptionsForConfig<Config>();
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080034
Dynamic Tools Team517193e2019-09-11 14:48:41 +000035 scudo::GlobalStats S;
36 S.init();
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -070037 std::unique_ptr<SecondaryT> L(new SecondaryT);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000038 L->init(&S);
39 const scudo::uptr Size = 1U << 16;
Vitaly Buka97742dd2021-07-01 11:56:11 -070040 void *P = L->allocate(Options, Size);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000041 EXPECT_NE(P, nullptr);
42 memset(P, 'A', Size);
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070043 EXPECT_GE(SecondaryT::getBlockSize(P), Size);
Vitaly Buka97742dd2021-07-01 11:56:11 -070044 L->deallocate(Options, P);
Mitch Phillipsa99b7792021-05-20 10:56:47 -070045
Dynamic Tools Teamc5d5abc2020-01-27 14:03:21 -080046 // If the Secondary can't cache that pointer, it will be unmapped.
Vitaly Buka5cb96362021-05-20 16:17:25 -070047 if (!L->canCache(Size)) {
48 EXPECT_DEATH(
49 {
50 // Repeat few time to avoid missing crash if it's mmaped by unrelated
51 // code.
52 for (int i = 0; i < 10; ++i) {
Vitaly Buka97742dd2021-07-01 11:56:11 -070053 P = L->allocate(Options, Size);
54 L->deallocate(Options, P);
Vitaly Buka5cb96362021-05-20 16:17:25 -070055 memset(P, 'A', Size);
56 }
57 },
58 "");
Vitaly Buka5cb96362021-05-20 16:17:25 -070059 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +000060
61 const scudo::uptr Align = 1U << 16;
Vitaly Buka97742dd2021-07-01 11:56:11 -070062 P = L->allocate(Options, Size + Align, Align);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000063 EXPECT_NE(P, nullptr);
64 void *AlignedP = reinterpret_cast<void *>(
65 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
66 memset(AlignedP, 'A', Size);
Vitaly Buka97742dd2021-07-01 11:56:11 -070067 L->deallocate(Options, P);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000068
69 std::vector<void *> V;
70 for (scudo::uptr I = 0; I < 32U; I++)
Vitaly Buka97742dd2021-07-01 11:56:11 -070071 V.push_back(L->allocate(Options, Size));
Dynamic Tools Teamd2740c52019-11-19 13:58:06 -080072 std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()()));
Dynamic Tools Team517193e2019-09-11 14:48:41 +000073 while (!V.empty()) {
Vitaly Buka97742dd2021-07-01 11:56:11 -070074 L->deallocate(Options, V.back());
Dynamic Tools Team517193e2019-09-11 14:48:41 +000075 V.pop_back();
76 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -070077 scudo::ScopedString Str;
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +000078 L->getStats(&Str);
79 Str.output();
Mitch Phillipsda86a542021-05-24 16:08:57 -070080 L->unmapTestOnly();
Dynamic Tools Team517193e2019-09-11 14:48:41 +000081}
82
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080083struct NoCacheConfig {
84 typedef scudo::MapAllocatorNoCache SecondaryCache;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080085 static const bool MaySupportMemoryTagging = false;
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080086};
87
Peter Collingbourne7488a172020-12-14 13:57:59 -080088struct TestConfig {
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -080089 typedef scudo::MapAllocatorCache<TestConfig> SecondaryCache;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080090 static const bool MaySupportMemoryTagging = false;
Peter Collingbourne7488a172020-12-14 13:57:59 -080091 static const scudo::u32 SecondaryCacheEntriesArraySize = 128U;
Peter Collingbournecc3d4932020-12-21 18:39:03 -080092 static const scudo::u32 SecondaryCacheQuarantineSize = 0U;
Peter Collingbourne7488a172020-12-14 13:57:59 -080093 static const scudo::u32 SecondaryCacheDefaultMaxEntriesCount = 64U;
94 static const scudo::uptr SecondaryCacheDefaultMaxEntrySize = 1UL << 20;
95 static const scudo::s32 SecondaryCacheMinReleaseToOsIntervalMs = INT32_MIN;
96 static const scudo::s32 SecondaryCacheMaxReleaseToOsIntervalMs = INT32_MAX;
97};
98
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -070099TEST(ScudoSecondaryTest, SecondaryBasic) {
Peter Collingbourne4edf4ef2020-12-15 15:32:32 -0800100 testSecondaryBasic<NoCacheConfig>();
101 testSecondaryBasic<scudo::DefaultConfig>();
102 testSecondaryBasic<TestConfig>();
Dynamic Tools Teamd29271f2019-10-31 10:31:49 -0700103}
104
Vitaly Bukad3199322021-07-01 10:22:35 -0700105struct MapAllocatorTest : public Test {
Vitaly Buka97742dd2021-07-01 11:56:11 -0700106 using Config = scudo::DefaultConfig;
107 using LargeAllocator = scudo::MapAllocator<Config>;
108
Vitaly Bukad3199322021-07-01 10:22:35 -0700109 void SetUp() override { Allocator->init(nullptr); }
110
111 void TearDown() override { Allocator->unmapTestOnly(); }
112
113 std::unique_ptr<LargeAllocator> Allocator =
114 std::make_unique<LargeAllocator>();
Vitaly Buka97742dd2021-07-01 11:56:11 -0700115 scudo::Options Options = getOptionsForConfig<Config>();
Vitaly Bukad3199322021-07-01 10:22:35 -0700116};
117
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000118// This exercises a variety of combinations of size and alignment for the
119// MapAllocator. The size computation done here mimic the ones done by the
120// combined allocator.
Vitaly Bukad3199322021-07-01 10:22:35 -0700121TEST_F(MapAllocatorTest, SecondaryCombinations) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000122 constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16);
123 constexpr scudo::uptr HeaderSize = scudo::roundUpTo(8, MinAlign);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000124 for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) {
125 for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16;
126 AlignLog++) {
127 const scudo::uptr Align = 1U << AlignLog;
128 for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) {
129 if (static_cast<scudo::sptr>(1U << SizeLog) + Delta <= 0)
130 continue;
131 const scudo::uptr UserSize =
132 scudo::roundUpTo((1U << SizeLog) + Delta, MinAlign);
133 const scudo::uptr Size =
134 HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0);
Vitaly Bukad3199322021-07-01 10:22:35 -0700135 void *P = Allocator->allocate(Options, Size, Align);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000136 EXPECT_NE(P, nullptr);
137 void *AlignedP = reinterpret_cast<void *>(
138 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align));
139 memset(AlignedP, 0xff, UserSize);
Vitaly Bukad3199322021-07-01 10:22:35 -0700140 Allocator->deallocate(Options, P);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000141 }
142 }
143 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700144 scudo::ScopedString Str;
Vitaly Bukad3199322021-07-01 10:22:35 -0700145 Allocator->getStats(&Str);
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000146 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000147}
148
Vitaly Bukad3199322021-07-01 10:22:35 -0700149TEST_F(MapAllocatorTest, SecondaryIterate) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000150 std::vector<void *> V;
151 const scudo::uptr PageSize = scudo::getPageSizeCached();
152 for (scudo::uptr I = 0; I < 32U; I++)
Vitaly Bukad3199322021-07-01 10:22:35 -0700153 V.push_back(Allocator->allocate(Options, (std::rand() % 16) * PageSize));
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000154 auto Lambda = [V](scudo::uptr Block) {
155 EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)),
156 V.end());
157 };
Vitaly Bukad3199322021-07-01 10:22:35 -0700158 Allocator->disable();
159 Allocator->iterateOverBlocks(Lambda);
160 Allocator->enable();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000161 while (!V.empty()) {
Vitaly Bukad3199322021-07-01 10:22:35 -0700162 Allocator->deallocate(Options, V.back());
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000163 V.pop_back();
164 }
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700165 scudo::ScopedString Str;
Vitaly Bukad3199322021-07-01 10:22:35 -0700166 Allocator->getStats(&Str);
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000167 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000168}
169
Vitaly Bukad3199322021-07-01 10:22:35 -0700170TEST_F(MapAllocatorTest, SecondaryOptions) {
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700171 // Attempt to set a maximum number of entries higher than the array size.
Vitaly Bukad3199322021-07-01 10:22:35 -0700172 EXPECT_FALSE(
173 Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4096U));
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700174 // A negative number will be cast to a scudo::u32, and fail.
Vitaly Bukad3199322021-07-01 10:22:35 -0700175 EXPECT_FALSE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, -1));
176 if (Allocator->canCache(0U)) {
Kostya Kortchinskyc72ca562020-07-27 09:13:42 -0700177 // Various valid combinations.
Vitaly Bukad3199322021-07-01 10:22:35 -0700178 EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
179 EXPECT_TRUE(
180 Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
181 EXPECT_TRUE(Allocator->canCache(1UL << 18));
182 EXPECT_TRUE(
183 Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17));
184 EXPECT_FALSE(Allocator->canCache(1UL << 18));
185 EXPECT_TRUE(Allocator->canCache(1UL << 16));
186 EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 0U));
187 EXPECT_FALSE(Allocator->canCache(1UL << 16));
188 EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U));
189 EXPECT_TRUE(
190 Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20));
191 EXPECT_TRUE(Allocator->canCache(1UL << 16));
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000192 }
193}
194
Vitaly Bukad3199322021-07-01 10:22:35 -0700195struct MapAllocatorWithReleaseTest : public MapAllocatorTest {
196 void SetUp() override { Allocator->init(nullptr, /*ReleaseToOsInterval=*/0); }
197
198 void performAllocations() {
199 std::vector<void *> V;
200 const scudo::uptr PageSize = scudo::getPageSizeCached();
201 {
202 std::unique_lock<std::mutex> Lock(Mutex);
203 while (!Ready)
204 Cv.wait(Lock);
205 }
206 for (scudo::uptr I = 0; I < 128U; I++) {
207 // Deallocate 75% of the blocks.
208 const bool Deallocate = (rand() & 3) != 0;
209 void *P = Allocator->allocate(Options, (std::rand() % 16) * PageSize);
210 if (Deallocate)
211 Allocator->deallocate(Options, P);
212 else
213 V.push_back(P);
214 }
215 while (!V.empty()) {
216 Allocator->deallocate(Options, V.back());
217 V.pop_back();
218 }
219 }
220
221 std::mutex Mutex;
222 std::condition_variable Cv;
223 bool Ready = false;
224};
225
226TEST_F(MapAllocatorWithReleaseTest, SecondaryThreadsRace) {
Dynamic Tools Teamfa69c702020-02-05 09:58:52 -0800227 std::thread Threads[16];
228 for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++)
Vitaly Bukad3199322021-07-01 10:22:35 -0700229 Threads[I] =
230 std::thread(&MapAllocatorWithReleaseTest::performAllocations, this);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000231 {
232 std::unique_lock<std::mutex> Lock(Mutex);
233 Ready = true;
234 Cv.notify_all();
235 }
236 for (auto &T : Threads)
237 T.join();
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700238 scudo::ScopedString Str;
Vitaly Bukad3199322021-07-01 10:22:35 -0700239 Allocator->getStats(&Str);
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000240 Str.output();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000241}