Skip to content

Commit 2f78d0d

Browse files
committed
[scudo] Support the mode of disabling primary cache
In this mode, no primary blocks will be cahced except the batch class. It remains the same page releasing strategy as SizeClassAllocatorLocalCache so we are expecting the same page releasing frequency in primary allocator.
1 parent 5db5dd7 commit 2f78d0d

File tree

5 files changed

+198
-7
lines changed

5 files changed

+198
-7
lines changed

compiler-rt/lib/scudo/standalone/allocator_config.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ PRIMARY_REQUIRED(const s32, MaxReleaseToOsIntervalMs)
7878

7979
// PRIMARY_OPTIONAL(TYPE, NAME, DEFAULT)
8080
//
81+
PRIMARY_OPTIONAL(const bool, EnableCache, true)
82+
8183
// The scale of a compact pointer. E.g., Ptr = Base + (CompactPtr << Scale).
8284
PRIMARY_OPTIONAL(const uptr, CompactPtrScale, SCUDO_MIN_ALIGNMENT_LOG)
8385

compiler-rt/lib/scudo/standalone/local_cache.h

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,6 @@ template <class SizeClassAllocator> struct SizeClassAllocatorLocalCache {
161161
}
162162
}
163163

164-
void destroyBatch(uptr ClassId, void *B) {
165-
if (ClassId != BatchClassId)
166-
deallocate(BatchClassId, B);
167-
}
168-
169164
NOINLINE bool refill(PerClass *C, uptr ClassId, u16 MaxRefill) {
170165
const u16 NumBlocksRefilled =
171166
Allocator->popBlocks(this, ClassId, C->Chunks, MaxRefill);
@@ -184,6 +179,148 @@ template <class SizeClassAllocator> struct SizeClassAllocatorLocalCache {
184179
}
185180
};
186181

182+
template <class SizeClassAllocator> struct NoCache {
183+
typedef typename SizeClassAllocator::SizeClassMap SizeClassMap;
184+
typedef typename SizeClassAllocator::CompactPtrT CompactPtrT;
185+
186+
void init(GlobalStats *S, SizeClassAllocator *A) {
187+
Stats.init();
188+
if (LIKELY(S))
189+
S->link(&Stats);
190+
Allocator = A;
191+
initCache();
192+
}
193+
194+
void destroy(GlobalStats *S) {
195+
if (LIKELY(S))
196+
S->unlink(&Stats);
197+
}
198+
199+
void *allocate(uptr ClassId) {
200+
CompactPtrT CompactPtr;
201+
uptr NumBlocksPopped = Allocator->popBlocks(this, ClassId, &CompactPtr, 1U);
202+
if (NumBlocksPopped == 0)
203+
return nullptr;
204+
DCHECK_EQ(NumBlocksPopped, 1U);
205+
const PerClass *C = &PerClassArray[ClassId];
206+
Stats.add(StatAllocated, C->ClassSize);
207+
Stats.sub(StatFree, C->ClassSize);
208+
return Allocator->decompactPtr(ClassId, CompactPtr);
209+
}
210+
211+
bool deallocate(uptr ClassId, void *P) {
212+
CHECK_LT(ClassId, NumClasses);
213+
214+
if (ClassId == BatchClassId)
215+
return deallocateBatchClassBlock(P);
216+
217+
CompactPtrT CompactPtr =
218+
Allocator->compactPtr(ClassId, reinterpret_cast<uptr>(P));
219+
Allocator->pushBlocks(this, ClassId, &CompactPtr, 1U);
220+
PerClass *C = &PerClassArray[ClassId];
221+
Stats.sub(StatAllocated, C->ClassSize);
222+
Stats.add(StatFree, C->ClassSize);
223+
224+
// The following adopts the same strategy of allocator draining as
225+
// SizeClassAllocatorLocalCache so that they have the same hint for doing
226+
// page release.
227+
++C->Count;
228+
const bool SuggestDraining = C->Count == C->MaxCount;
229+
if (SuggestDraining)
230+
C->Count = 0;
231+
return SuggestDraining;
232+
}
233+
234+
void *getBatchClassBlock() {
235+
PerClass *C = &PerClassArray[BatchClassId];
236+
if (C->Count == 0) {
237+
const u16 NumBlocksRefilled = Allocator->popBlocks(
238+
this, BatchClassId, BatchClassStorage, C->MaxCount);
239+
if (NumBlocksRefilled == 0)
240+
reportOutOfMemory(SizeClassAllocator::getSizeByClassId(BatchClassId));
241+
DCHECK_LE(NumBlocksRefilled, SizeClassMap::MaxNumCachedHint);
242+
C->Count = NumBlocksRefilled;
243+
}
244+
245+
const uptr ClassSize = C->ClassSize;
246+
CompactPtrT CompactP = BatchClassStorage[--C->Count];
247+
Stats.add(StatAllocated, ClassSize);
248+
Stats.sub(StatFree, ClassSize);
249+
250+
return Allocator->decompactPtr(BatchClassId, CompactP);
251+
}
252+
253+
LocalStats &getStats() { return Stats; }
254+
255+
void getStats(ScopedString *Str) { Str->append(" No block is cached.\n"); }
256+
257+
bool isEmpty() const {
258+
const PerClass *C = &PerClassArray[BatchClassId];
259+
return C->Count == 0;
260+
}
261+
void drain() {
262+
PerClass *C = &PerClassArray[BatchClassId];
263+
if (C->Count > 0) {
264+
Allocator->pushBlocks(this, BatchClassId, BatchClassStorage, C->Count);
265+
C->Count = 0;
266+
}
267+
}
268+
269+
static u16 getMaxCached(uptr Size) {
270+
return Min(SizeClassMap::MaxNumCachedHint,
271+
SizeClassMap::getMaxCachedHint(Size));
272+
}
273+
274+
private:
275+
static const uptr NumClasses = SizeClassMap::NumClasses;
276+
static const uptr BatchClassId = SizeClassMap::BatchClassId;
277+
struct alignas(SCUDO_CACHE_LINE_SIZE) PerClass {
278+
u16 Count = 0;
279+
u16 MaxCount;
280+
// Note: ClassSize is zero for the transfer batch.
281+
uptr ClassSize;
282+
};
283+
PerClass PerClassArray[NumClasses] = {};
284+
// Popping BatchClass blocks requires taking a certain amount of blocks at
285+
// once. This restriction comes from how we manage the storing of BatchClass
286+
// in the primary allocator. See more details in `popBlocksImpl` in the
287+
// primary allocator.
288+
CompactPtrT BatchClassStorage[SizeClassMap::MaxNumCachedHint];
289+
LocalStats Stats;
290+
SizeClassAllocator *Allocator = nullptr;
291+
292+
bool deallocateBatchClassBlock(void *P) {
293+
PerClass *C = &PerClassArray[BatchClassId];
294+
// Drain all the blocks.
295+
if (C->Count == C->MaxCount) {
296+
Allocator->pushBlocks(this, BatchClassId, BatchClassStorage, C->Count);
297+
C->Count = 0;
298+
}
299+
BatchClassStorage[C->Count++] =
300+
Allocator->compactPtr(BatchClassId, reinterpret_cast<uptr>(P));
301+
302+
// Currently, BatchClass doesn't support page releasing, so we always return
303+
// false.
304+
return false;
305+
}
306+
307+
NOINLINE void initCache() {
308+
for (uptr I = 0; I < NumClasses; I++) {
309+
PerClass *P = &PerClassArray[I];
310+
const uptr Size = SizeClassAllocator::getSizeByClassId(I);
311+
if (I != BatchClassId) {
312+
P->ClassSize = Size;
313+
P->MaxCount = static_cast<u16>(2 * getMaxCached(Size));
314+
} else {
315+
// ClassSize in this struct is only used for malloc/free stats, which
316+
// should only track user allocations, not internal movements.
317+
P->ClassSize = 0;
318+
P->MaxCount = SizeClassMap::MaxNumCachedHint;
319+
}
320+
}
321+
}
322+
};
323+
187324
} // namespace scudo
188325

189326
#endif // SCUDO_LOCAL_CACHE_H_

compiler-rt/lib/scudo/standalone/primary64.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,11 @@ template <typename Config> class SizeClassAllocator64 {
5757
"Group size shouldn't be greater than the region size");
5858
static const uptr GroupScale = GroupSizeLog - CompactPtrScale;
5959
typedef SizeClassAllocator64<Config> ThisT;
60-
typedef SizeClassAllocatorLocalCache<ThisT> CacheT;
6160
typedef TransferBatch<ThisT> TransferBatchT;
6261
typedef BatchGroup<ThisT> BatchGroupT;
62+
using CacheT = typename Conditional<Config::getEnableCache(),
63+
SizeClassAllocatorLocalCache<ThisT>,
64+
NoCache<ThisT>>::type;
6365

6466
// BachClass is used to store internal metadata so it needs to be at least as
6567
// large as the largest data structure.

compiler-rt/lib/scudo/standalone/tests/combined_test.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,47 @@ struct TestConditionVariableConfig {
210210
};
211211
template <typename Config> using SecondaryT = scudo::MapAllocator<Config>;
212212
};
213+
214+
struct TestNoCacheConfig {
215+
static const bool MaySupportMemoryTagging = true;
216+
template <class A>
217+
using TSDRegistryT =
218+
scudo::TSDRegistrySharedT<A, 8U, 4U>; // Shared, max 8 TSDs.
219+
220+
struct Primary {
221+
using SizeClassMap = scudo::AndroidSizeClassMap;
222+
#if SCUDO_CAN_USE_PRIMARY64
223+
static const scudo::uptr RegionSizeLog = 28U;
224+
typedef scudo::u32 CompactPtrT;
225+
static const scudo::uptr CompactPtrScale = SCUDO_MIN_ALIGNMENT_LOG;
226+
static const scudo::uptr GroupSizeLog = 20U;
227+
static const bool EnableRandomOffset = true;
228+
static const scudo::uptr MapSizeIncrement = 1UL << 18;
229+
#else
230+
static const scudo::uptr RegionSizeLog = 18U;
231+
static const scudo::uptr GroupSizeLog = 18U;
232+
typedef scudo::uptr CompactPtrT;
233+
#endif
234+
static const bool EnableCache = false;
235+
static const scudo::s32 MinReleaseToOsIntervalMs = 1000;
236+
static const scudo::s32 MaxReleaseToOsIntervalMs = 1000;
237+
};
238+
239+
#if SCUDO_CAN_USE_PRIMARY64
240+
template <typename Config>
241+
using PrimaryT = scudo::SizeClassAllocator64<Config>;
242+
#else
243+
template <typename Config>
244+
using PrimaryT = scudo::SizeClassAllocator32<Config>;
245+
#endif
246+
247+
struct Secondary {
248+
template <typename Config>
249+
using CacheT = scudo::MapAllocatorNoCache<Config>;
250+
};
251+
template <typename Config> using SecondaryT = scudo::MapAllocator<Config>;
252+
};
253+
213254
} // namespace scudo
214255

215256
#if SCUDO_FUCHSIA
@@ -219,7 +260,8 @@ struct TestConditionVariableConfig {
219260
#define SCUDO_TYPED_TEST_ALL_TYPES(FIXTURE, NAME) \
220261
SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, DefaultConfig) \
221262
SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, AndroidConfig) \
222-
SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConditionVariableConfig)
263+
SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestConditionVariableConfig) \
264+
SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TestNoCacheConfig)
223265
#endif
224266

225267
#define SCUDO_TYPED_TEST_TYPE(FIXTURE, NAME, TYPE) \

compiler-rt/lib/scudo/standalone/type_traits.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ template <typename T> struct isPointer<T *> {
4242
static constexpr bool value = true;
4343
};
4444

45+
template <bool Cond, typename L, typename R> struct Conditional {
46+
using type = L;
47+
};
48+
49+
template <typename L, typename R> struct Conditional<false, L, R> {
50+
using type = R;
51+
};
52+
4553
} // namespace scudo
4654

4755
#endif // SCUDO_TYPE_TRAITS_H_

0 commit comments

Comments
 (0)