1*76559068SAndroid Build Coastguard Worker //===-- local_cache.h -------------------------------------------*- 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 #ifndef SCUDO_LOCAL_CACHE_H_ 10*76559068SAndroid Build Coastguard Worker #define SCUDO_LOCAL_CACHE_H_ 11*76559068SAndroid Build Coastguard Worker 12*76559068SAndroid Build Coastguard Worker #include "internal_defs.h" 13*76559068SAndroid Build Coastguard Worker #include "list.h" 14*76559068SAndroid Build Coastguard Worker #include "platform.h" 15*76559068SAndroid Build Coastguard Worker #include "report.h" 16*76559068SAndroid Build Coastguard Worker #include "stats.h" 17*76559068SAndroid Build Coastguard Worker #include "string_utils.h" 18*76559068SAndroid Build Coastguard Worker 19*76559068SAndroid Build Coastguard Worker namespace scudo { 20*76559068SAndroid Build Coastguard Worker 21*76559068SAndroid Build Coastguard Worker template <class SizeClassAllocator> struct SizeClassAllocatorLocalCache { 22*76559068SAndroid Build Coastguard Worker typedef typename SizeClassAllocator::SizeClassMap SizeClassMap; 23*76559068SAndroid Build Coastguard Worker typedef typename SizeClassAllocator::CompactPtrT CompactPtrT; 24*76559068SAndroid Build Coastguard Worker initSizeClassAllocatorLocalCache25*76559068SAndroid Build Coastguard Worker void init(GlobalStats *S, SizeClassAllocator *A) { 26*76559068SAndroid Build Coastguard Worker DCHECK(isEmpty()); 27*76559068SAndroid Build Coastguard Worker Stats.init(); 28*76559068SAndroid Build Coastguard Worker if (LIKELY(S)) 29*76559068SAndroid Build Coastguard Worker S->link(&Stats); 30*76559068SAndroid Build Coastguard Worker Allocator = A; 31*76559068SAndroid Build Coastguard Worker initCache(); 32*76559068SAndroid Build Coastguard Worker } 33*76559068SAndroid Build Coastguard Worker destroySizeClassAllocatorLocalCache34*76559068SAndroid Build Coastguard Worker void destroy(GlobalStats *S) { 35*76559068SAndroid Build Coastguard Worker drain(); 36*76559068SAndroid Build Coastguard Worker if (LIKELY(S)) 37*76559068SAndroid Build Coastguard Worker S->unlink(&Stats); 38*76559068SAndroid Build Coastguard Worker } 39*76559068SAndroid Build Coastguard Worker allocateSizeClassAllocatorLocalCache40*76559068SAndroid Build Coastguard Worker void *allocate(uptr ClassId) { 41*76559068SAndroid Build Coastguard Worker DCHECK_LT(ClassId, NumClasses); 42*76559068SAndroid Build Coastguard Worker PerClass *C = &PerClassArray[ClassId]; 43*76559068SAndroid Build Coastguard Worker if (C->Count == 0) { 44*76559068SAndroid Build Coastguard Worker // Refill half of the number of max cached. 45*76559068SAndroid Build Coastguard Worker DCHECK_GT(C->MaxCount / 2, 0U); 46*76559068SAndroid Build Coastguard Worker if (UNLIKELY(!refill(C, ClassId, C->MaxCount / 2))) 47*76559068SAndroid Build Coastguard Worker return nullptr; 48*76559068SAndroid Build Coastguard Worker DCHECK_GT(C->Count, 0); 49*76559068SAndroid Build Coastguard Worker } 50*76559068SAndroid Build Coastguard Worker // We read ClassSize first before accessing Chunks because it's adjacent to 51*76559068SAndroid Build Coastguard Worker // Count, while Chunks might be further off (depending on Count). That keeps 52*76559068SAndroid Build Coastguard Worker // the memory accesses in close quarters. 53*76559068SAndroid Build Coastguard Worker const uptr ClassSize = C->ClassSize; 54*76559068SAndroid Build Coastguard Worker CompactPtrT CompactP = C->Chunks[--C->Count]; 55*76559068SAndroid Build Coastguard Worker Stats.add(StatAllocated, ClassSize); 56*76559068SAndroid Build Coastguard Worker Stats.sub(StatFree, ClassSize); 57*76559068SAndroid Build Coastguard Worker return Allocator->decompactPtr(ClassId, CompactP); 58*76559068SAndroid Build Coastguard Worker } 59*76559068SAndroid Build Coastguard Worker deallocateSizeClassAllocatorLocalCache60*76559068SAndroid Build Coastguard Worker bool deallocate(uptr ClassId, void *P) { 61*76559068SAndroid Build Coastguard Worker CHECK_LT(ClassId, NumClasses); 62*76559068SAndroid Build Coastguard Worker PerClass *C = &PerClassArray[ClassId]; 63*76559068SAndroid Build Coastguard Worker 64*76559068SAndroid Build Coastguard Worker // If the cache is full, drain half of blocks back to the main allocator. 65*76559068SAndroid Build Coastguard Worker const bool NeedToDrainCache = C->Count == C->MaxCount; 66*76559068SAndroid Build Coastguard Worker if (NeedToDrainCache) 67*76559068SAndroid Build Coastguard Worker drain(C, ClassId); 68*76559068SAndroid Build Coastguard Worker // See comment in allocate() about memory accesses. 69*76559068SAndroid Build Coastguard Worker const uptr ClassSize = C->ClassSize; 70*76559068SAndroid Build Coastguard Worker C->Chunks[C->Count++] = 71*76559068SAndroid Build Coastguard Worker Allocator->compactPtr(ClassId, reinterpret_cast<uptr>(P)); 72*76559068SAndroid Build Coastguard Worker Stats.sub(StatAllocated, ClassSize); 73*76559068SAndroid Build Coastguard Worker Stats.add(StatFree, ClassSize); 74*76559068SAndroid Build Coastguard Worker 75*76559068SAndroid Build Coastguard Worker return NeedToDrainCache; 76*76559068SAndroid Build Coastguard Worker } 77*76559068SAndroid Build Coastguard Worker isEmptySizeClassAllocatorLocalCache78*76559068SAndroid Build Coastguard Worker bool isEmpty() const { 79*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; ++I) 80*76559068SAndroid Build Coastguard Worker if (PerClassArray[I].Count) 81*76559068SAndroid Build Coastguard Worker return false; 82*76559068SAndroid Build Coastguard Worker return true; 83*76559068SAndroid Build Coastguard Worker } 84*76559068SAndroid Build Coastguard Worker drainSizeClassAllocatorLocalCache85*76559068SAndroid Build Coastguard Worker void drain() { 86*76559068SAndroid Build Coastguard Worker // Drain BatchClassId last as it may be needed while draining normal blocks. 87*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; ++I) { 88*76559068SAndroid Build Coastguard Worker if (I == BatchClassId) 89*76559068SAndroid Build Coastguard Worker continue; 90*76559068SAndroid Build Coastguard Worker while (PerClassArray[I].Count > 0) 91*76559068SAndroid Build Coastguard Worker drain(&PerClassArray[I], I); 92*76559068SAndroid Build Coastguard Worker } 93*76559068SAndroid Build Coastguard Worker while (PerClassArray[BatchClassId].Count > 0) 94*76559068SAndroid Build Coastguard Worker drain(&PerClassArray[BatchClassId], BatchClassId); 95*76559068SAndroid Build Coastguard Worker DCHECK(isEmpty()); 96*76559068SAndroid Build Coastguard Worker } 97*76559068SAndroid Build Coastguard Worker getBatchClassBlockSizeClassAllocatorLocalCache98*76559068SAndroid Build Coastguard Worker void *getBatchClassBlock() { 99*76559068SAndroid Build Coastguard Worker void *B = allocate(BatchClassId); 100*76559068SAndroid Build Coastguard Worker if (UNLIKELY(!B)) 101*76559068SAndroid Build Coastguard Worker reportOutOfMemory(SizeClassAllocator::getSizeByClassId(BatchClassId)); 102*76559068SAndroid Build Coastguard Worker return B; 103*76559068SAndroid Build Coastguard Worker } 104*76559068SAndroid Build Coastguard Worker getStatsSizeClassAllocatorLocalCache105*76559068SAndroid Build Coastguard Worker LocalStats &getStats() { return Stats; } 106*76559068SAndroid Build Coastguard Worker getStatsSizeClassAllocatorLocalCache107*76559068SAndroid Build Coastguard Worker void getStats(ScopedString *Str) { 108*76559068SAndroid Build Coastguard Worker bool EmptyCache = true; 109*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; ++I) { 110*76559068SAndroid Build Coastguard Worker if (PerClassArray[I].Count == 0) 111*76559068SAndroid Build Coastguard Worker continue; 112*76559068SAndroid Build Coastguard Worker 113*76559068SAndroid Build Coastguard Worker EmptyCache = false; 114*76559068SAndroid Build Coastguard Worker // The size of BatchClass is set to 0 intentionally. See the comment in 115*76559068SAndroid Build Coastguard Worker // initCache() for more details. 116*76559068SAndroid Build Coastguard Worker const uptr ClassSize = I == BatchClassId 117*76559068SAndroid Build Coastguard Worker ? SizeClassAllocator::getSizeByClassId(I) 118*76559068SAndroid Build Coastguard Worker : PerClassArray[I].ClassSize; 119*76559068SAndroid Build Coastguard Worker // Note that the string utils don't support printing u16 thus we cast it 120*76559068SAndroid Build Coastguard Worker // to a common use type uptr. 121*76559068SAndroid Build Coastguard Worker Str->append(" %02zu (%6zu): cached: %4zu max: %4zu\n", I, ClassSize, 122*76559068SAndroid Build Coastguard Worker static_cast<uptr>(PerClassArray[I].Count), 123*76559068SAndroid Build Coastguard Worker static_cast<uptr>(PerClassArray[I].MaxCount)); 124*76559068SAndroid Build Coastguard Worker } 125*76559068SAndroid Build Coastguard Worker 126*76559068SAndroid Build Coastguard Worker if (EmptyCache) 127*76559068SAndroid Build Coastguard Worker Str->append(" No block is cached.\n"); 128*76559068SAndroid Build Coastguard Worker } 129*76559068SAndroid Build Coastguard Worker getMaxCachedSizeClassAllocatorLocalCache130*76559068SAndroid Build Coastguard Worker static u16 getMaxCached(uptr Size) { 131*76559068SAndroid Build Coastguard Worker return Min(SizeClassMap::MaxNumCachedHint, 132*76559068SAndroid Build Coastguard Worker SizeClassMap::getMaxCachedHint(Size)); 133*76559068SAndroid Build Coastguard Worker } 134*76559068SAndroid Build Coastguard Worker 135*76559068SAndroid Build Coastguard Worker private: 136*76559068SAndroid Build Coastguard Worker static const uptr NumClasses = SizeClassMap::NumClasses; 137*76559068SAndroid Build Coastguard Worker static const uptr BatchClassId = SizeClassMap::BatchClassId; 138*76559068SAndroid Build Coastguard Worker struct alignas(SCUDO_CACHE_LINE_SIZE) PerClass { 139*76559068SAndroid Build Coastguard Worker u16 Count; 140*76559068SAndroid Build Coastguard Worker u16 MaxCount; 141*76559068SAndroid Build Coastguard Worker // Note: ClassSize is zero for the transfer batch. 142*76559068SAndroid Build Coastguard Worker uptr ClassSize; 143*76559068SAndroid Build Coastguard Worker CompactPtrT Chunks[2 * SizeClassMap::MaxNumCachedHint]; 144*76559068SAndroid Build Coastguard Worker }; 145*76559068SAndroid Build Coastguard Worker PerClass PerClassArray[NumClasses] = {}; 146*76559068SAndroid Build Coastguard Worker LocalStats Stats; 147*76559068SAndroid Build Coastguard Worker SizeClassAllocator *Allocator = nullptr; 148*76559068SAndroid Build Coastguard Worker initCacheSizeClassAllocatorLocalCache149*76559068SAndroid Build Coastguard Worker NOINLINE void initCache() { 150*76559068SAndroid Build Coastguard Worker for (uptr I = 0; I < NumClasses; I++) { 151*76559068SAndroid Build Coastguard Worker PerClass *P = &PerClassArray[I]; 152*76559068SAndroid Build Coastguard Worker const uptr Size = SizeClassAllocator::getSizeByClassId(I); 153*76559068SAndroid Build Coastguard Worker P->MaxCount = static_cast<u16>(2 * getMaxCached(Size)); 154*76559068SAndroid Build Coastguard Worker if (I != BatchClassId) { 155*76559068SAndroid Build Coastguard Worker P->ClassSize = Size; 156*76559068SAndroid Build Coastguard Worker } else { 157*76559068SAndroid Build Coastguard Worker // ClassSize in this struct is only used for malloc/free stats, which 158*76559068SAndroid Build Coastguard Worker // should only track user allocations, not internal movements. 159*76559068SAndroid Build Coastguard Worker P->ClassSize = 0; 160*76559068SAndroid Build Coastguard Worker } 161*76559068SAndroid Build Coastguard Worker } 162*76559068SAndroid Build Coastguard Worker } 163*76559068SAndroid Build Coastguard Worker destroyBatchSizeClassAllocatorLocalCache164*76559068SAndroid Build Coastguard Worker void destroyBatch(uptr ClassId, void *B) { 165*76559068SAndroid Build Coastguard Worker if (ClassId != BatchClassId) 166*76559068SAndroid Build Coastguard Worker deallocate(BatchClassId, B); 167*76559068SAndroid Build Coastguard Worker } 168*76559068SAndroid Build Coastguard Worker refillSizeClassAllocatorLocalCache169*76559068SAndroid Build Coastguard Worker NOINLINE bool refill(PerClass *C, uptr ClassId, u16 MaxRefill) { 170*76559068SAndroid Build Coastguard Worker const u16 NumBlocksRefilled = 171*76559068SAndroid Build Coastguard Worker Allocator->popBlocks(this, ClassId, C->Chunks, MaxRefill); 172*76559068SAndroid Build Coastguard Worker DCHECK_LE(NumBlocksRefilled, MaxRefill); 173*76559068SAndroid Build Coastguard Worker C->Count = static_cast<u16>(C->Count + NumBlocksRefilled); 174*76559068SAndroid Build Coastguard Worker return NumBlocksRefilled != 0; 175*76559068SAndroid Build Coastguard Worker } 176*76559068SAndroid Build Coastguard Worker drainSizeClassAllocatorLocalCache177*76559068SAndroid Build Coastguard Worker NOINLINE void drain(PerClass *C, uptr ClassId) { 178*76559068SAndroid Build Coastguard Worker const u16 Count = Min(static_cast<u16>(C->MaxCount / 2), C->Count); 179*76559068SAndroid Build Coastguard Worker Allocator->pushBlocks(this, ClassId, &C->Chunks[0], Count); 180*76559068SAndroid Build Coastguard Worker // u16 will be promoted to int by arithmetic type conversion. 181*76559068SAndroid Build Coastguard Worker C->Count = static_cast<u16>(C->Count - Count); 182*76559068SAndroid Build Coastguard Worker for (u16 I = 0; I < C->Count; I++) 183*76559068SAndroid Build Coastguard Worker C->Chunks[I] = C->Chunks[I + Count]; 184*76559068SAndroid Build Coastguard Worker } 185*76559068SAndroid Build Coastguard Worker }; 186*76559068SAndroid Build Coastguard Worker 187*76559068SAndroid Build Coastguard Worker } // namespace scudo 188*76559068SAndroid Build Coastguard Worker 189*76559068SAndroid Build Coastguard Worker #endif // SCUDO_LOCAL_CACHE_H_ 190