1*76559068SAndroid Build Coastguard Worker //===-- primary_test.cpp ----------------------------------------*- C++ -*-===//
2*76559068SAndroid Build Coastguard Worker //
3*76559068SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*76559068SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*76559068SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*76559068SAndroid Build Coastguard Worker //
7*76559068SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*76559068SAndroid Build Coastguard Worker
9*76559068SAndroid Build Coastguard Worker #include "tests/scudo_unit_test.h"
10*76559068SAndroid Build Coastguard Worker
11*76559068SAndroid Build Coastguard Worker #include "allocator_config.h"
12*76559068SAndroid Build Coastguard Worker #include "allocator_config_wrapper.h"
13*76559068SAndroid Build Coastguard Worker #include "condition_variable.h"
14*76559068SAndroid Build Coastguard Worker #include "primary32.h"
15*76559068SAndroid Build Coastguard Worker #include "primary64.h"
16*76559068SAndroid Build Coastguard Worker #include "size_class_map.h"
17*76559068SAndroid Build Coastguard Worker
18*76559068SAndroid Build Coastguard Worker #include <algorithm>
19*76559068SAndroid Build Coastguard Worker #include <chrono>
20*76559068SAndroid Build Coastguard Worker #include <condition_variable>
21*76559068SAndroid Build Coastguard Worker #include <mutex>
22*76559068SAndroid Build Coastguard Worker #include <random>
23*76559068SAndroid Build Coastguard Worker #include <stdlib.h>
24*76559068SAndroid Build Coastguard Worker #include <thread>
25*76559068SAndroid Build Coastguard Worker #include <vector>
26*76559068SAndroid Build Coastguard Worker
27*76559068SAndroid Build Coastguard Worker // Note that with small enough regions, the SizeClassAllocator64 also works on
28*76559068SAndroid Build Coastguard Worker // 32-bit architectures. It's not something we want to encourage, but we still
29*76559068SAndroid Build Coastguard Worker // should ensure the tests pass.
30*76559068SAndroid Build Coastguard Worker
31*76559068SAndroid Build Coastguard Worker template <typename SizeClassMapT> struct TestConfig1 {
32*76559068SAndroid Build Coastguard Worker static const bool MaySupportMemoryTagging = false;
33*76559068SAndroid Build Coastguard Worker template <typename> using TSDRegistryT = void;
34*76559068SAndroid Build Coastguard Worker template <typename> using PrimaryT = void;
35*76559068SAndroid Build Coastguard Worker template <typename> using SecondaryT = void;
36*76559068SAndroid Build Coastguard Worker
37*76559068SAndroid Build Coastguard Worker struct Primary {
38*76559068SAndroid Build Coastguard Worker using SizeClassMap = SizeClassMapT;
39*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 18U;
40*76559068SAndroid Build Coastguard Worker static const scudo::uptr GroupSizeLog = 18U;
41*76559068SAndroid Build Coastguard Worker static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;
42*76559068SAndroid Build Coastguard Worker static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
43*76559068SAndroid Build Coastguard Worker typedef scudo::uptr CompactPtrT;
44*76559068SAndroid Build Coastguard Worker static const scudo::uptr CompactPtrScale = 0;
45*76559068SAndroid Build Coastguard Worker static const bool EnableRandomOffset = true;
46*76559068SAndroid Build Coastguard Worker static const scudo::uptr MapSizeIncrement = 1UL << 18;
47*76559068SAndroid Build Coastguard Worker };
48*76559068SAndroid Build Coastguard Worker };
49*76559068SAndroid Build Coastguard Worker
50*76559068SAndroid Build Coastguard Worker template <typename SizeClassMapT> struct TestConfig2 {
51*76559068SAndroid Build Coastguard Worker static const bool MaySupportMemoryTagging = false;
52*76559068SAndroid Build Coastguard Worker template <typename> using TSDRegistryT = void;
53*76559068SAndroid Build Coastguard Worker template <typename> using PrimaryT = void;
54*76559068SAndroid Build Coastguard Worker template <typename> using SecondaryT = void;
55*76559068SAndroid Build Coastguard Worker
56*76559068SAndroid Build Coastguard Worker struct Primary {
57*76559068SAndroid Build Coastguard Worker using SizeClassMap = SizeClassMapT;
58*76559068SAndroid Build Coastguard Worker #if defined(__mips__)
59*76559068SAndroid Build Coastguard Worker // Unable to allocate greater size on QEMU-user.
60*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 23U;
61*76559068SAndroid Build Coastguard Worker #else
62*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 24U;
63*76559068SAndroid Build Coastguard Worker #endif
64*76559068SAndroid Build Coastguard Worker static const scudo::uptr GroupSizeLog = 20U;
65*76559068SAndroid Build Coastguard Worker static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;
66*76559068SAndroid Build Coastguard Worker static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
67*76559068SAndroid Build Coastguard Worker typedef scudo::uptr CompactPtrT;
68*76559068SAndroid Build Coastguard Worker static const scudo::uptr CompactPtrScale = 0;
69*76559068SAndroid Build Coastguard Worker static const bool EnableRandomOffset = true;
70*76559068SAndroid Build Coastguard Worker static const scudo::uptr MapSizeIncrement = 1UL << 18;
71*76559068SAndroid Build Coastguard Worker };
72*76559068SAndroid Build Coastguard Worker };
73*76559068SAndroid Build Coastguard Worker
74*76559068SAndroid Build Coastguard Worker template <typename SizeClassMapT> struct TestConfig3 {
75*76559068SAndroid Build Coastguard Worker static const bool MaySupportMemoryTagging = true;
76*76559068SAndroid Build Coastguard Worker template <typename> using TSDRegistryT = void;
77*76559068SAndroid Build Coastguard Worker template <typename> using PrimaryT = void;
78*76559068SAndroid Build Coastguard Worker template <typename> using SecondaryT = void;
79*76559068SAndroid Build Coastguard Worker
80*76559068SAndroid Build Coastguard Worker struct Primary {
81*76559068SAndroid Build Coastguard Worker using SizeClassMap = SizeClassMapT;
82*76559068SAndroid Build Coastguard Worker #if defined(__mips__)
83*76559068SAndroid Build Coastguard Worker // Unable to allocate greater size on QEMU-user.
84*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 23U;
85*76559068SAndroid Build Coastguard Worker #else
86*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 24U;
87*76559068SAndroid Build Coastguard Worker #endif
88*76559068SAndroid Build Coastguard Worker static const scudo::uptr GroupSizeLog = 20U;
89*76559068SAndroid Build Coastguard Worker static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;
90*76559068SAndroid Build Coastguard Worker static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
91*76559068SAndroid Build Coastguard Worker typedef scudo::uptr CompactPtrT;
92*76559068SAndroid Build Coastguard Worker static const scudo::uptr CompactPtrScale = 0;
93*76559068SAndroid Build Coastguard Worker static const bool EnableContiguousRegions = false;
94*76559068SAndroid Build Coastguard Worker static const bool EnableRandomOffset = true;
95*76559068SAndroid Build Coastguard Worker static const scudo::uptr MapSizeIncrement = 1UL << 18;
96*76559068SAndroid Build Coastguard Worker };
97*76559068SAndroid Build Coastguard Worker };
98*76559068SAndroid Build Coastguard Worker
99*76559068SAndroid Build Coastguard Worker template <typename SizeClassMapT> struct TestConfig4 {
100*76559068SAndroid Build Coastguard Worker static const bool MaySupportMemoryTagging = true;
101*76559068SAndroid Build Coastguard Worker template <typename> using TSDRegistryT = void;
102*76559068SAndroid Build Coastguard Worker template <typename> using PrimaryT = void;
103*76559068SAndroid Build Coastguard Worker template <typename> using SecondaryT = void;
104*76559068SAndroid Build Coastguard Worker
105*76559068SAndroid Build Coastguard Worker struct Primary {
106*76559068SAndroid Build Coastguard Worker using SizeClassMap = SizeClassMapT;
107*76559068SAndroid Build Coastguard Worker #if defined(__mips__)
108*76559068SAndroid Build Coastguard Worker // Unable to allocate greater size on QEMU-user.
109*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 23U;
110*76559068SAndroid Build Coastguard Worker #else
111*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 24U;
112*76559068SAndroid Build Coastguard Worker #endif
113*76559068SAndroid Build Coastguard Worker static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;
114*76559068SAndroid Build Coastguard Worker static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
115*76559068SAndroid Build Coastguard Worker static const scudo::uptr CompactPtrScale = 3U;
116*76559068SAndroid Build Coastguard Worker static const scudo::uptr GroupSizeLog = 20U;
117*76559068SAndroid Build Coastguard Worker typedef scudo::u32 CompactPtrT;
118*76559068SAndroid Build Coastguard Worker static const bool EnableRandomOffset = true;
119*76559068SAndroid Build Coastguard Worker static const scudo::uptr MapSizeIncrement = 1UL << 18;
120*76559068SAndroid Build Coastguard Worker };
121*76559068SAndroid Build Coastguard Worker };
122*76559068SAndroid Build Coastguard Worker
123*76559068SAndroid Build Coastguard Worker // This is the only test config that enables the condition variable.
124*76559068SAndroid Build Coastguard Worker template <typename SizeClassMapT> struct TestConfig5 {
125*76559068SAndroid Build Coastguard Worker static const bool MaySupportMemoryTagging = true;
126*76559068SAndroid Build Coastguard Worker template <typename> using TSDRegistryT = void;
127*76559068SAndroid Build Coastguard Worker template <typename> using PrimaryT = void;
128*76559068SAndroid Build Coastguard Worker template <typename> using SecondaryT = void;
129*76559068SAndroid Build Coastguard Worker
130*76559068SAndroid Build Coastguard Worker struct Primary {
131*76559068SAndroid Build Coastguard Worker using SizeClassMap = SizeClassMapT;
132*76559068SAndroid Build Coastguard Worker #if defined(__mips__)
133*76559068SAndroid Build Coastguard Worker // Unable to allocate greater size on QEMU-user.
134*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 23U;
135*76559068SAndroid Build Coastguard Worker #else
136*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 24U;
137*76559068SAndroid Build Coastguard Worker #endif
138*76559068SAndroid Build Coastguard Worker static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;
139*76559068SAndroid Build Coastguard Worker static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
140*76559068SAndroid Build Coastguard Worker static const scudo::uptr CompactPtrScale = SCUDO_MIN_ALIGNMENT_LOG;
141*76559068SAndroid Build Coastguard Worker static const scudo::uptr GroupSizeLog = 18U;
142*76559068SAndroid Build Coastguard Worker typedef scudo::u32 CompactPtrT;
143*76559068SAndroid Build Coastguard Worker static const bool EnableRandomOffset = true;
144*76559068SAndroid Build Coastguard Worker static const scudo::uptr MapSizeIncrement = 1UL << 18;
145*76559068SAndroid Build Coastguard Worker #if SCUDO_LINUX
146*76559068SAndroid Build Coastguard Worker using ConditionVariableT = scudo::ConditionVariableLinux;
147*76559068SAndroid Build Coastguard Worker #else
148*76559068SAndroid Build Coastguard Worker using ConditionVariableT = scudo::ConditionVariableDummy;
149*76559068SAndroid Build Coastguard Worker #endif
150*76559068SAndroid Build Coastguard Worker };
151*76559068SAndroid Build Coastguard Worker };
152*76559068SAndroid Build Coastguard Worker
153*76559068SAndroid Build Coastguard Worker template <template <typename> class BaseConfig, typename SizeClassMapT>
154*76559068SAndroid Build Coastguard Worker struct Config : public BaseConfig<SizeClassMapT> {};
155*76559068SAndroid Build Coastguard Worker
156*76559068SAndroid Build Coastguard Worker template <template <typename> class BaseConfig, typename SizeClassMapT>
157*76559068SAndroid Build Coastguard Worker struct SizeClassAllocator
158*76559068SAndroid Build Coastguard Worker : public scudo::SizeClassAllocator64<
159*76559068SAndroid Build Coastguard Worker scudo::PrimaryConfig<Config<BaseConfig, SizeClassMapT>>> {};
160*76559068SAndroid Build Coastguard Worker template <typename SizeClassMapT>
161*76559068SAndroid Build Coastguard Worker struct SizeClassAllocator<TestConfig1, SizeClassMapT>
162*76559068SAndroid Build Coastguard Worker : public scudo::SizeClassAllocator32<
163*76559068SAndroid Build Coastguard Worker scudo::PrimaryConfig<Config<TestConfig1, SizeClassMapT>>> {};
164*76559068SAndroid Build Coastguard Worker
165*76559068SAndroid Build Coastguard Worker template <template <typename> class BaseConfig, typename SizeClassMapT>
166*76559068SAndroid Build Coastguard Worker struct TestAllocator : public SizeClassAllocator<BaseConfig, SizeClassMapT> {
~TestAllocatorTestAllocator167*76559068SAndroid Build Coastguard Worker ~TestAllocator() {
168*76559068SAndroid Build Coastguard Worker this->verifyAllBlocksAreReleasedTestOnly();
169*76559068SAndroid Build Coastguard Worker this->unmapTestOnly();
170*76559068SAndroid Build Coastguard Worker }
171*76559068SAndroid Build Coastguard Worker
operator newTestAllocator172*76559068SAndroid Build Coastguard Worker void *operator new(size_t size) {
173*76559068SAndroid Build Coastguard Worker void *p = nullptr;
174*76559068SAndroid Build Coastguard Worker EXPECT_EQ(0, posix_memalign(&p, alignof(TestAllocator), size));
175*76559068SAndroid Build Coastguard Worker return p;
176*76559068SAndroid Build Coastguard Worker }
177*76559068SAndroid Build Coastguard Worker
operator deleteTestAllocator178*76559068SAndroid Build Coastguard Worker void operator delete(void *ptr) { free(ptr); }
179*76559068SAndroid Build Coastguard Worker };
180*76559068SAndroid Build Coastguard Worker
181*76559068SAndroid Build Coastguard Worker template <template <typename> class BaseConfig>
182*76559068SAndroid Build Coastguard Worker struct ScudoPrimaryTest : public Test {};
183*76559068SAndroid Build Coastguard Worker
184*76559068SAndroid Build Coastguard Worker #if SCUDO_FUCHSIA
185*76559068SAndroid Build Coastguard Worker #define SCUDO_TYPED_TEST_ALL_TYPES(FIXTURE, NAME) \
186*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConfig2) \
187*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConfig3)
188*76559068SAndroid Build Coastguard Worker #else
189*76559068SAndroid Build Coastguard Worker #define SCUDO_TYPED_TEST_ALL_TYPES(FIXTURE, NAME) \
190*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConfig1) \
191*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConfig2) \
192*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConfig3) \
193*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConfig4) \
194*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConfig5)
195*76559068SAndroid Build Coastguard Worker #endif
196*76559068SAndroid Build Coastguard Worker
197*76559068SAndroid Build Coastguard Worker #define SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TYPE) \
198*76559068SAndroid Build Coastguard Worker using FIXTURE##NAME##_##TYPE = FIXTURE##NAME<TYPE>; \
199*76559068SAndroid Build Coastguard Worker TEST_F(FIXTURE##NAME##_##TYPE, NAME) { FIXTURE##NAME<TYPE>::Run(); }
200*76559068SAndroid Build Coastguard Worker
201*76559068SAndroid Build Coastguard Worker #define SCUDO_TYPED_TEST(FIXTURE, NAME) \
202*76559068SAndroid Build Coastguard Worker template <template <typename> class TypeParam> \
203*76559068SAndroid Build Coastguard Worker struct FIXTURE##NAME : public FIXTURE<TypeParam> { \
204*76559068SAndroid Build Coastguard Worker void Run(); \
205*76559068SAndroid Build Coastguard Worker }; \
206*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST_ALL_TYPES(FIXTURE, NAME) \
207*76559068SAndroid Build Coastguard Worker template <template <typename> class TypeParam> \
208*76559068SAndroid Build Coastguard Worker void FIXTURE##NAME<TypeParam>::Run()
209*76559068SAndroid Build Coastguard Worker
SCUDO_TYPED_TEST(ScudoPrimaryTest,BasicPrimary)210*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST(ScudoPrimaryTest, BasicPrimary) {
211*76559068SAndroid Build Coastguard Worker using Primary = TestAllocator<TypeParam, scudo::DefaultSizeClassMap>;
212*76559068SAndroid Build Coastguard Worker std::unique_ptr<Primary> Allocator(new Primary);
213*76559068SAndroid Build Coastguard Worker Allocator->init(/*ReleaseToOsInterval=*/-1);
214*76559068SAndroid Build Coastguard Worker typename Primary::CacheT Cache;
215*76559068SAndroid Build Coastguard Worker Cache.init(nullptr, Allocator.get());
216*76559068SAndroid Build Coastguard Worker const scudo::uptr NumberOfAllocations = 32U;
217*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I <= 16U; I++) {
218*76559068SAndroid Build Coastguard Worker const scudo::uptr Size = 1UL << I;
219*76559068SAndroid Build Coastguard Worker if (!Primary::canAllocate(Size))
220*76559068SAndroid Build Coastguard Worker continue;
221*76559068SAndroid Build Coastguard Worker const scudo::uptr ClassId = Primary::SizeClassMap::getClassIdBySize(Size);
222*76559068SAndroid Build Coastguard Worker void *Pointers[NumberOfAllocations];
223*76559068SAndroid Build Coastguard Worker for (scudo::uptr J = 0; J < NumberOfAllocations; J++) {
224*76559068SAndroid Build Coastguard Worker void *P = Cache.allocate(ClassId);
225*76559068SAndroid Build Coastguard Worker memset(P, 'B', Size);
226*76559068SAndroid Build Coastguard Worker Pointers[J] = P;
227*76559068SAndroid Build Coastguard Worker }
228*76559068SAndroid Build Coastguard Worker for (scudo::uptr J = 0; J < NumberOfAllocations; J++)
229*76559068SAndroid Build Coastguard Worker Cache.deallocate(ClassId, Pointers[J]);
230*76559068SAndroid Build Coastguard Worker }
231*76559068SAndroid Build Coastguard Worker Cache.destroy(nullptr);
232*76559068SAndroid Build Coastguard Worker Allocator->releaseToOS(scudo::ReleaseToOS::Force);
233*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
234*76559068SAndroid Build Coastguard Worker Allocator->getStats(&Str);
235*76559068SAndroid Build Coastguard Worker Str.output();
236*76559068SAndroid Build Coastguard Worker }
237*76559068SAndroid Build Coastguard Worker
238*76559068SAndroid Build Coastguard Worker struct SmallRegionsConfig {
239*76559068SAndroid Build Coastguard Worker static const bool MaySupportMemoryTagging = false;
240*76559068SAndroid Build Coastguard Worker template <typename> using TSDRegistryT = void;
241*76559068SAndroid Build Coastguard Worker template <typename> using PrimaryT = void;
242*76559068SAndroid Build Coastguard Worker template <typename> using SecondaryT = void;
243*76559068SAndroid Build Coastguard Worker
244*76559068SAndroid Build Coastguard Worker struct Primary {
245*76559068SAndroid Build Coastguard Worker using SizeClassMap = scudo::DefaultSizeClassMap;
246*76559068SAndroid Build Coastguard Worker static const scudo::uptr RegionSizeLog = 21U;
247*76559068SAndroid Build Coastguard Worker static const scudo::s32 MinReleaseToOsIntervalMs = INT32_MIN;
248*76559068SAndroid Build Coastguard Worker static const scudo::s32 MaxReleaseToOsIntervalMs = INT32_MAX;
249*76559068SAndroid Build Coastguard Worker typedef scudo::uptr CompactPtrT;
250*76559068SAndroid Build Coastguard Worker static const scudo::uptr CompactPtrScale = 0;
251*76559068SAndroid Build Coastguard Worker static const bool EnableRandomOffset = true;
252*76559068SAndroid Build Coastguard Worker static const scudo::uptr MapSizeIncrement = 1UL << 18;
253*76559068SAndroid Build Coastguard Worker static const scudo::uptr GroupSizeLog = 20U;
254*76559068SAndroid Build Coastguard Worker };
255*76559068SAndroid Build Coastguard Worker };
256*76559068SAndroid Build Coastguard Worker
257*76559068SAndroid Build Coastguard Worker // The 64-bit SizeClassAllocator can be easily OOM'd with small region sizes.
258*76559068SAndroid Build Coastguard Worker // For the 32-bit one, it requires actually exhausting memory, so we skip it.
TEST(ScudoPrimaryTest,Primary64OOM)259*76559068SAndroid Build Coastguard Worker TEST(ScudoPrimaryTest, Primary64OOM) {
260*76559068SAndroid Build Coastguard Worker using Primary =
261*76559068SAndroid Build Coastguard Worker scudo::SizeClassAllocator64<scudo::PrimaryConfig<SmallRegionsConfig>>;
262*76559068SAndroid Build Coastguard Worker Primary Allocator;
263*76559068SAndroid Build Coastguard Worker Allocator.init(/*ReleaseToOsInterval=*/-1);
264*76559068SAndroid Build Coastguard Worker typename Primary::CacheT Cache;
265*76559068SAndroid Build Coastguard Worker scudo::GlobalStats Stats;
266*76559068SAndroid Build Coastguard Worker Stats.init();
267*76559068SAndroid Build Coastguard Worker Cache.init(&Stats, &Allocator);
268*76559068SAndroid Build Coastguard Worker bool AllocationFailed = false;
269*76559068SAndroid Build Coastguard Worker std::vector<void *> Blocks;
270*76559068SAndroid Build Coastguard Worker const scudo::uptr ClassId = Primary::SizeClassMap::LargestClassId;
271*76559068SAndroid Build Coastguard Worker const scudo::uptr Size = Primary::getSizeByClassId(ClassId);
272*76559068SAndroid Build Coastguard Worker const scudo::u16 MaxCachedBlockCount = Primary::CacheT::getMaxCached(Size);
273*76559068SAndroid Build Coastguard Worker
274*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < 10000U; I++) {
275*76559068SAndroid Build Coastguard Worker for (scudo::uptr J = 0; J < MaxCachedBlockCount; ++J) {
276*76559068SAndroid Build Coastguard Worker void *Ptr = Cache.allocate(ClassId);
277*76559068SAndroid Build Coastguard Worker if (Ptr == nullptr) {
278*76559068SAndroid Build Coastguard Worker AllocationFailed = true;
279*76559068SAndroid Build Coastguard Worker break;
280*76559068SAndroid Build Coastguard Worker }
281*76559068SAndroid Build Coastguard Worker memset(Ptr, 'B', Size);
282*76559068SAndroid Build Coastguard Worker Blocks.push_back(Ptr);
283*76559068SAndroid Build Coastguard Worker }
284*76559068SAndroid Build Coastguard Worker }
285*76559068SAndroid Build Coastguard Worker
286*76559068SAndroid Build Coastguard Worker for (auto *Ptr : Blocks)
287*76559068SAndroid Build Coastguard Worker Cache.deallocate(ClassId, Ptr);
288*76559068SAndroid Build Coastguard Worker
289*76559068SAndroid Build Coastguard Worker Cache.destroy(nullptr);
290*76559068SAndroid Build Coastguard Worker Allocator.releaseToOS(scudo::ReleaseToOS::Force);
291*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
292*76559068SAndroid Build Coastguard Worker Allocator.getStats(&Str);
293*76559068SAndroid Build Coastguard Worker Str.output();
294*76559068SAndroid Build Coastguard Worker EXPECT_EQ(AllocationFailed, true);
295*76559068SAndroid Build Coastguard Worker Allocator.unmapTestOnly();
296*76559068SAndroid Build Coastguard Worker }
297*76559068SAndroid Build Coastguard Worker
SCUDO_TYPED_TEST(ScudoPrimaryTest,PrimaryIterate)298*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST(ScudoPrimaryTest, PrimaryIterate) {
299*76559068SAndroid Build Coastguard Worker using Primary = TestAllocator<TypeParam, scudo::DefaultSizeClassMap>;
300*76559068SAndroid Build Coastguard Worker std::unique_ptr<Primary> Allocator(new Primary);
301*76559068SAndroid Build Coastguard Worker Allocator->init(/*ReleaseToOsInterval=*/-1);
302*76559068SAndroid Build Coastguard Worker typename Primary::CacheT Cache;
303*76559068SAndroid Build Coastguard Worker Cache.init(nullptr, Allocator.get());
304*76559068SAndroid Build Coastguard Worker std::vector<std::pair<scudo::uptr, void *>> V;
305*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < 64U; I++) {
306*76559068SAndroid Build Coastguard Worker const scudo::uptr Size =
307*76559068SAndroid Build Coastguard Worker static_cast<scudo::uptr>(std::rand()) % Primary::SizeClassMap::MaxSize;
308*76559068SAndroid Build Coastguard Worker const scudo::uptr ClassId = Primary::SizeClassMap::getClassIdBySize(Size);
309*76559068SAndroid Build Coastguard Worker void *P = Cache.allocate(ClassId);
310*76559068SAndroid Build Coastguard Worker V.push_back(std::make_pair(ClassId, P));
311*76559068SAndroid Build Coastguard Worker }
312*76559068SAndroid Build Coastguard Worker scudo::uptr Found = 0;
313*76559068SAndroid Build Coastguard Worker auto Lambda = [&V, &Found](scudo::uptr Block) {
314*76559068SAndroid Build Coastguard Worker for (const auto &Pair : V) {
315*76559068SAndroid Build Coastguard Worker if (Pair.second == reinterpret_cast<void *>(Block))
316*76559068SAndroid Build Coastguard Worker Found++;
317*76559068SAndroid Build Coastguard Worker }
318*76559068SAndroid Build Coastguard Worker };
319*76559068SAndroid Build Coastguard Worker Allocator->disable();
320*76559068SAndroid Build Coastguard Worker Allocator->iterateOverBlocks(Lambda);
321*76559068SAndroid Build Coastguard Worker Allocator->enable();
322*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Found, V.size());
323*76559068SAndroid Build Coastguard Worker while (!V.empty()) {
324*76559068SAndroid Build Coastguard Worker auto Pair = V.back();
325*76559068SAndroid Build Coastguard Worker Cache.deallocate(Pair.first, Pair.second);
326*76559068SAndroid Build Coastguard Worker V.pop_back();
327*76559068SAndroid Build Coastguard Worker }
328*76559068SAndroid Build Coastguard Worker Cache.destroy(nullptr);
329*76559068SAndroid Build Coastguard Worker Allocator->releaseToOS(scudo::ReleaseToOS::Force);
330*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
331*76559068SAndroid Build Coastguard Worker Allocator->getStats(&Str);
332*76559068SAndroid Build Coastguard Worker Str.output();
333*76559068SAndroid Build Coastguard Worker }
334*76559068SAndroid Build Coastguard Worker
SCUDO_TYPED_TEST(ScudoPrimaryTest,PrimaryThreaded)335*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST(ScudoPrimaryTest, PrimaryThreaded) {
336*76559068SAndroid Build Coastguard Worker using Primary = TestAllocator<TypeParam, scudo::Config::Primary::SizeClassMap>;
337*76559068SAndroid Build Coastguard Worker std::unique_ptr<Primary> Allocator(new Primary);
338*76559068SAndroid Build Coastguard Worker Allocator->init(/*ReleaseToOsInterval=*/-1);
339*76559068SAndroid Build Coastguard Worker std::mutex Mutex;
340*76559068SAndroid Build Coastguard Worker std::condition_variable Cv;
341*76559068SAndroid Build Coastguard Worker bool Ready = false;
342*76559068SAndroid Build Coastguard Worker std::thread Threads[32];
343*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++) {
344*76559068SAndroid Build Coastguard Worker Threads[I] = std::thread([&]() {
345*76559068SAndroid Build Coastguard Worker static thread_local typename Primary::CacheT Cache;
346*76559068SAndroid Build Coastguard Worker Cache.init(nullptr, Allocator.get());
347*76559068SAndroid Build Coastguard Worker std::vector<std::pair<scudo::uptr, void *>> V;
348*76559068SAndroid Build Coastguard Worker {
349*76559068SAndroid Build Coastguard Worker std::unique_lock<std::mutex> Lock(Mutex);
350*76559068SAndroid Build Coastguard Worker while (!Ready)
351*76559068SAndroid Build Coastguard Worker Cv.wait(Lock);
352*76559068SAndroid Build Coastguard Worker }
353*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < 256U; I++) {
354*76559068SAndroid Build Coastguard Worker const scudo::uptr Size = static_cast<scudo::uptr>(std::rand()) %
355*76559068SAndroid Build Coastguard Worker Primary::SizeClassMap::MaxSize / 4;
356*76559068SAndroid Build Coastguard Worker const scudo::uptr ClassId =
357*76559068SAndroid Build Coastguard Worker Primary::SizeClassMap::getClassIdBySize(Size);
358*76559068SAndroid Build Coastguard Worker void *P = Cache.allocate(ClassId);
359*76559068SAndroid Build Coastguard Worker if (P)
360*76559068SAndroid Build Coastguard Worker V.push_back(std::make_pair(ClassId, P));
361*76559068SAndroid Build Coastguard Worker }
362*76559068SAndroid Build Coastguard Worker
363*76559068SAndroid Build Coastguard Worker // Try to interleave pushBlocks(), popBlocks() and releaseToOS().
364*76559068SAndroid Build Coastguard Worker Allocator->releaseToOS(scudo::ReleaseToOS::Force);
365*76559068SAndroid Build Coastguard Worker
366*76559068SAndroid Build Coastguard Worker while (!V.empty()) {
367*76559068SAndroid Build Coastguard Worker auto Pair = V.back();
368*76559068SAndroid Build Coastguard Worker Cache.deallocate(Pair.first, Pair.second);
369*76559068SAndroid Build Coastguard Worker V.pop_back();
370*76559068SAndroid Build Coastguard Worker // This increases the chance of having non-full TransferBatches and it
371*76559068SAndroid Build Coastguard Worker // will jump into the code path of merging TransferBatches.
372*76559068SAndroid Build Coastguard Worker if (std::rand() % 8 == 0)
373*76559068SAndroid Build Coastguard Worker Cache.drain();
374*76559068SAndroid Build Coastguard Worker }
375*76559068SAndroid Build Coastguard Worker Cache.destroy(nullptr);
376*76559068SAndroid Build Coastguard Worker });
377*76559068SAndroid Build Coastguard Worker }
378*76559068SAndroid Build Coastguard Worker {
379*76559068SAndroid Build Coastguard Worker std::unique_lock<std::mutex> Lock(Mutex);
380*76559068SAndroid Build Coastguard Worker Ready = true;
381*76559068SAndroid Build Coastguard Worker Cv.notify_all();
382*76559068SAndroid Build Coastguard Worker }
383*76559068SAndroid Build Coastguard Worker for (auto &T : Threads)
384*76559068SAndroid Build Coastguard Worker T.join();
385*76559068SAndroid Build Coastguard Worker Allocator->releaseToOS(scudo::ReleaseToOS::Force);
386*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
387*76559068SAndroid Build Coastguard Worker Allocator->getStats(&Str);
388*76559068SAndroid Build Coastguard Worker Allocator->getFragmentationInfo(&Str);
389*76559068SAndroid Build Coastguard Worker Allocator->getMemoryGroupFragmentationInfo(&Str);
390*76559068SAndroid Build Coastguard Worker Str.output();
391*76559068SAndroid Build Coastguard Worker }
392*76559068SAndroid Build Coastguard Worker
393*76559068SAndroid Build Coastguard Worker // Through a simple allocation that spans two pages, verify that releaseToOS
394*76559068SAndroid Build Coastguard Worker // actually releases some bytes (at least one page worth). This is a regression
395*76559068SAndroid Build Coastguard Worker // test for an error in how the release criteria were computed.
SCUDO_TYPED_TEST(ScudoPrimaryTest,ReleaseToOS)396*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST(ScudoPrimaryTest, ReleaseToOS) {
397*76559068SAndroid Build Coastguard Worker using Primary = TestAllocator<TypeParam, scudo::DefaultSizeClassMap>;
398*76559068SAndroid Build Coastguard Worker std::unique_ptr<Primary> Allocator(new Primary);
399*76559068SAndroid Build Coastguard Worker Allocator->init(/*ReleaseToOsInterval=*/-1);
400*76559068SAndroid Build Coastguard Worker typename Primary::CacheT Cache;
401*76559068SAndroid Build Coastguard Worker Cache.init(nullptr, Allocator.get());
402*76559068SAndroid Build Coastguard Worker const scudo::uptr Size = scudo::getPageSizeCached() * 2;
403*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Primary::canAllocate(Size));
404*76559068SAndroid Build Coastguard Worker const scudo::uptr ClassId = Primary::SizeClassMap::getClassIdBySize(Size);
405*76559068SAndroid Build Coastguard Worker void *P = Cache.allocate(ClassId);
406*76559068SAndroid Build Coastguard Worker EXPECT_NE(P, nullptr);
407*76559068SAndroid Build Coastguard Worker Cache.deallocate(ClassId, P);
408*76559068SAndroid Build Coastguard Worker Cache.destroy(nullptr);
409*76559068SAndroid Build Coastguard Worker EXPECT_GT(Allocator->releaseToOS(scudo::ReleaseToOS::ForceAll), 0U);
410*76559068SAndroid Build Coastguard Worker }
411*76559068SAndroid Build Coastguard Worker
SCUDO_TYPED_TEST(ScudoPrimaryTest,MemoryGroup)412*76559068SAndroid Build Coastguard Worker SCUDO_TYPED_TEST(ScudoPrimaryTest, MemoryGroup) {
413*76559068SAndroid Build Coastguard Worker using Primary = TestAllocator<TypeParam, scudo::DefaultSizeClassMap>;
414*76559068SAndroid Build Coastguard Worker std::unique_ptr<Primary> Allocator(new Primary);
415*76559068SAndroid Build Coastguard Worker Allocator->init(/*ReleaseToOsInterval=*/-1);
416*76559068SAndroid Build Coastguard Worker typename Primary::CacheT Cache;
417*76559068SAndroid Build Coastguard Worker Cache.init(nullptr, Allocator.get());
418*76559068SAndroid Build Coastguard Worker const scudo::uptr Size = 32U;
419*76559068SAndroid Build Coastguard Worker const scudo::uptr ClassId = Primary::SizeClassMap::getClassIdBySize(Size);
420*76559068SAndroid Build Coastguard Worker
421*76559068SAndroid Build Coastguard Worker // We will allocate 4 times the group size memory and release all of them. We
422*76559068SAndroid Build Coastguard Worker // expect the free blocks will be classified with groups. Then we will
423*76559068SAndroid Build Coastguard Worker // allocate the same amount of memory as group size and expect the blocks will
424*76559068SAndroid Build Coastguard Worker // have the max address difference smaller or equal to 2 times the group size.
425*76559068SAndroid Build Coastguard Worker // Note that it isn't necessary to be in the range of single group size
426*76559068SAndroid Build Coastguard Worker // because the way we get the group id is doing compact pointer shifting.
427*76559068SAndroid Build Coastguard Worker // According to configuration, the compact pointer may not align to group
428*76559068SAndroid Build Coastguard Worker // size. As a result, the blocks can cross two groups at most.
429*76559068SAndroid Build Coastguard Worker const scudo::uptr GroupSizeMem = (1ULL << Primary::GroupSizeLog);
430*76559068SAndroid Build Coastguard Worker const scudo::uptr PeakAllocationMem = 4 * GroupSizeMem;
431*76559068SAndroid Build Coastguard Worker const scudo::uptr PeakNumberOfAllocations = PeakAllocationMem / Size;
432*76559068SAndroid Build Coastguard Worker const scudo::uptr FinalNumberOfAllocations = GroupSizeMem / Size;
433*76559068SAndroid Build Coastguard Worker std::vector<scudo::uptr> Blocks;
434*76559068SAndroid Build Coastguard Worker std::mt19937 R;
435*76559068SAndroid Build Coastguard Worker
436*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < PeakNumberOfAllocations; ++I)
437*76559068SAndroid Build Coastguard Worker Blocks.push_back(reinterpret_cast<scudo::uptr>(Cache.allocate(ClassId)));
438*76559068SAndroid Build Coastguard Worker
439*76559068SAndroid Build Coastguard Worker std::shuffle(Blocks.begin(), Blocks.end(), R);
440*76559068SAndroid Build Coastguard Worker
441*76559068SAndroid Build Coastguard Worker // Release all the allocated blocks, including those held by local cache.
442*76559068SAndroid Build Coastguard Worker while (!Blocks.empty()) {
443*76559068SAndroid Build Coastguard Worker Cache.deallocate(ClassId, reinterpret_cast<void *>(Blocks.back()));
444*76559068SAndroid Build Coastguard Worker Blocks.pop_back();
445*76559068SAndroid Build Coastguard Worker }
446*76559068SAndroid Build Coastguard Worker Cache.drain();
447*76559068SAndroid Build Coastguard Worker
448*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < FinalNumberOfAllocations; ++I)
449*76559068SAndroid Build Coastguard Worker Blocks.push_back(reinterpret_cast<scudo::uptr>(Cache.allocate(ClassId)));
450*76559068SAndroid Build Coastguard Worker
451*76559068SAndroid Build Coastguard Worker EXPECT_LE(*std::max_element(Blocks.begin(), Blocks.end()) -
452*76559068SAndroid Build Coastguard Worker *std::min_element(Blocks.begin(), Blocks.end()),
453*76559068SAndroid Build Coastguard Worker GroupSizeMem * 2);
454*76559068SAndroid Build Coastguard Worker
455*76559068SAndroid Build Coastguard Worker while (!Blocks.empty()) {
456*76559068SAndroid Build Coastguard Worker Cache.deallocate(ClassId, reinterpret_cast<void *>(Blocks.back()));
457*76559068SAndroid Build Coastguard Worker Blocks.pop_back();
458*76559068SAndroid Build Coastguard Worker }
459*76559068SAndroid Build Coastguard Worker Cache.drain();
460*76559068SAndroid Build Coastguard Worker }
461