blob: f46645f9badf2c083ad293699b2c7d2c4b321797 [file] [log] [blame]
Dynamic Tools Team517193e2019-09-11 14:48:41 +00001//===-- local_cache.h -------------------------------------------*- 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
9#ifndef SCUDO_LOCAL_CACHE_H_
10#define SCUDO_LOCAL_CACHE_H_
11
12#include "internal_defs.h"
13#include "report.h"
14#include "stats.h"
15
16namespace scudo {
17
18template <class SizeClassAllocator> struct SizeClassAllocatorLocalCache {
19 typedef typename SizeClassAllocator::SizeClassMap SizeClassMap;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080020 typedef typename SizeClassAllocator::CompactPtrT CompactPtrT;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000021
22 struct TransferBatch {
23 static const u32 MaxNumCached = SizeClassMap::MaxNumCachedHint;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080024 void setFromArray(CompactPtrT *Array, u32 N) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +000025 DCHECK_LE(N, MaxNumCached);
26 Count = N;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080027 memcpy(Batch, Array, sizeof(Batch[0]) * Count);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000028 }
29 void clear() { Count = 0; }
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080030 void add(CompactPtrT P) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +000031 DCHECK_LT(Count, MaxNumCached);
32 Batch[Count++] = P;
33 }
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080034 void copyToArray(CompactPtrT *Array) const {
35 memcpy(Array, Batch, sizeof(Batch[0]) * Count);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000036 }
37 u32 getCount() const { return Count; }
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080038 CompactPtrT get(u32 I) const {
Dynamic Tools Team517193e2019-09-11 14:48:41 +000039 DCHECK_LE(I, Count);
40 return Batch[I];
41 }
42 static u32 getMaxCached(uptr Size) {
43 return Min(MaxNumCached, SizeClassMap::getMaxCachedHint(Size));
44 }
45 TransferBatch *Next;
46
47 private:
48 u32 Count;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080049 CompactPtrT Batch[MaxNumCached];
Dynamic Tools Team517193e2019-09-11 14:48:41 +000050 };
51
Kostya Kortchinsky4a435d22021-05-25 15:00:58 -070052 void init(GlobalStats *S, SizeClassAllocator *A) {
53 DCHECK(isEmpty());
54 Stats.init();
Dynamic Tools Team517193e2019-09-11 14:48:41 +000055 if (LIKELY(S))
56 S->link(&Stats);
57 Allocator = A;
58 }
59
Dynamic Tools Team517193e2019-09-11 14:48:41 +000060 void destroy(GlobalStats *S) {
61 drain();
62 if (LIKELY(S))
63 S->unlink(&Stats);
64 }
65
66 void *allocate(uptr ClassId) {
67 DCHECK_LT(ClassId, NumClasses);
68 PerClass *C = &PerClassArray[ClassId];
69 if (C->Count == 0) {
70 if (UNLIKELY(!refill(C, ClassId)))
71 return nullptr;
72 DCHECK_GT(C->Count, 0);
73 }
74 // We read ClassSize first before accessing Chunks because it's adjacent to
75 // Count, while Chunks might be further off (depending on Count). That keeps
76 // the memory accesses in close quarters.
77 const uptr ClassSize = C->ClassSize;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080078 CompactPtrT CompactP = C->Chunks[--C->Count];
Dynamic Tools Team517193e2019-09-11 14:48:41 +000079 Stats.add(StatAllocated, ClassSize);
80 Stats.sub(StatFree, ClassSize);
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080081 return Allocator->decompactPtr(ClassId, CompactP);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000082 }
83
84 void deallocate(uptr ClassId, void *P) {
85 CHECK_LT(ClassId, NumClasses);
86 PerClass *C = &PerClassArray[ClassId];
87 // We still have to initialize the cache in the event that the first heap
88 // operation in a thread is a deallocation.
89 initCacheMaybe(C);
90 if (C->Count == C->MaxCount)
91 drain(C, ClassId);
92 // See comment in allocate() about memory accesses.
93 const uptr ClassSize = C->ClassSize;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -080094 C->Chunks[C->Count++] =
95 Allocator->compactPtr(ClassId, reinterpret_cast<uptr>(P));
Dynamic Tools Team517193e2019-09-11 14:48:41 +000096 Stats.sub(StatAllocated, ClassSize);
97 Stats.add(StatFree, ClassSize);
98 }
99
Vitaly Bukaff350932021-04-01 12:44:31 -0700100 bool isEmpty() const {
101 for (uptr I = 0; I < NumClasses; ++I)
102 if (PerClassArray[I].Count)
103 return false;
104 return true;
105 }
106
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000107 void drain() {
Kostya Kortchinsky1b132352021-04-07 12:39:24 -0700108 // Drain BatchClassId last as createBatch can refill it.
109 for (uptr I = 0; I < NumClasses; ++I) {
110 if (I == BatchClassId)
111 continue;
112 while (PerClassArray[I].Count > 0)
113 drain(&PerClassArray[I], I);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000114 }
Kostya Kortchinsky1b132352021-04-07 12:39:24 -0700115 while (PerClassArray[BatchClassId].Count > 0)
116 drain(&PerClassArray[BatchClassId], BatchClassId);
Vitaly Bukaff350932021-04-01 12:44:31 -0700117 DCHECK(isEmpty());
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000118 }
119
120 TransferBatch *createBatch(uptr ClassId, void *B) {
Kostya Kortchinsky1b132352021-04-07 12:39:24 -0700121 if (ClassId != BatchClassId)
122 B = allocate(BatchClassId);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000123 return reinterpret_cast<TransferBatch *>(B);
124 }
125
126 LocalStats &getStats() { return Stats; }
127
128private:
129 static const uptr NumClasses = SizeClassMap::NumClasses;
Kostya Kortchinsky1b132352021-04-07 12:39:24 -0700130 static const uptr BatchClassId = SizeClassMap::BatchClassId;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000131 struct PerClass {
132 u32 Count;
133 u32 MaxCount;
Mitch Phillipsbebf30b2021-05-03 10:42:19 -0700134 // Note: ClassSize is zero for the transfer batch.
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000135 uptr ClassSize;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -0800136 CompactPtrT Chunks[2 * TransferBatch::MaxNumCached];
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000137 };
Vitaly Buka5d3d7272021-04-29 01:19:51 -0700138 PerClass PerClassArray[NumClasses] = {};
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000139 LocalStats Stats;
Vitaly Buka5d3d7272021-04-29 01:19:51 -0700140 SizeClassAllocator *Allocator = nullptr;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000141
142 ALWAYS_INLINE void initCacheMaybe(PerClass *C) {
143 if (LIKELY(C->MaxCount))
144 return;
145 initCache();
146 DCHECK_NE(C->MaxCount, 0U);
147 }
148
149 NOINLINE void initCache() {
150 for (uptr I = 0; I < NumClasses; I++) {
151 PerClass *P = &PerClassArray[I];
152 const uptr Size = SizeClassAllocator::getSizeByClassId(I);
153 P->MaxCount = 2 * TransferBatch::getMaxCached(Size);
Mitch Phillipsbebf30b2021-05-03 10:42:19 -0700154 if (I != BatchClassId) {
155 P->ClassSize = Size;
156 } else {
157 // ClassSize in this struct is only used for malloc/free stats, which
158 // should only track user allocations, not internal movements.
159 P->ClassSize = 0;
160 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000161 }
162 }
163
164 void destroyBatch(uptr ClassId, void *B) {
Kostya Kortchinsky1b132352021-04-07 12:39:24 -0700165 if (ClassId != BatchClassId)
166 deallocate(BatchClassId, B);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000167 }
168
169 NOINLINE bool refill(PerClass *C, uptr ClassId) {
170 initCacheMaybe(C);
171 TransferBatch *B = Allocator->popBatch(this, ClassId);
172 if (UNLIKELY(!B))
173 return false;
174 DCHECK_GT(B->getCount(), 0);
175 C->Count = B->getCount();
176 B->copyToArray(C->Chunks);
Kostya Kortchinsky394cc822020-06-17 10:31:53 -0700177 B->clear();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000178 destroyBatch(ClassId, B);
179 return true;
180 }
181
182 NOINLINE void drain(PerClass *C, uptr ClassId) {
183 const u32 Count = Min(C->MaxCount / 2, C->Count);
Kostya Kortchinskyc9369542021-02-10 10:17:18 -0800184 TransferBatch *B =
185 createBatch(ClassId, Allocator->decompactPtr(ClassId, C->Chunks[0]));
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000186 if (UNLIKELY(!B))
Kostya Kortchinsky1b132352021-04-07 12:39:24 -0700187 reportOutOfMemory(SizeClassAllocator::getSizeByClassId(BatchClassId));
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800188 B->setFromArray(&C->Chunks[0], Count);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000189 C->Count -= Count;
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800190 for (uptr I = 0; I < C->Count; I++)
191 C->Chunks[I] = C->Chunks[I + Count];
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000192 Allocator->pushBatch(ClassId, B);
193 }
194};
195
196} // namespace scudo
197
198#endif // SCUDO_LOCAL_CACHE_H_