xref: /aosp_15_r20/external/scudo/standalone/local_cache.h (revision 76559068c068bd27e82aff38fac3bfc865233bca)
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