annotate libsanitizer/sanitizer_common/sanitizer_allocator_local_cache.h @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
111
kono
parents:
diff changeset
1 //===-- sanitizer_allocator_local_cache.h -----------------------*- C++ -*-===//
kono
parents:
diff changeset
2 //
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
111
kono
parents:
diff changeset
6 //
kono
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
kono
parents:
diff changeset
8 //
kono
parents:
diff changeset
9 // Part of the Sanitizer Allocator.
kono
parents:
diff changeset
10 //
kono
parents:
diff changeset
11 //===----------------------------------------------------------------------===//
kono
parents:
diff changeset
12 #ifndef SANITIZER_ALLOCATOR_H
kono
parents:
diff changeset
13 #error This file must be included inside sanitizer_allocator.h
kono
parents:
diff changeset
14 #endif
kono
parents:
diff changeset
15
kono
parents:
diff changeset
16 // Cache used by SizeClassAllocator64.
kono
parents:
diff changeset
17 template <class SizeClassAllocator>
kono
parents:
diff changeset
18 struct SizeClassAllocator64LocalCache {
kono
parents:
diff changeset
19 typedef SizeClassAllocator Allocator;
kono
parents:
diff changeset
20
kono
parents:
diff changeset
21 void Init(AllocatorGlobalStats *s) {
kono
parents:
diff changeset
22 stats_.Init();
kono
parents:
diff changeset
23 if (s)
kono
parents:
diff changeset
24 s->Register(&stats_);
kono
parents:
diff changeset
25 }
kono
parents:
diff changeset
26
kono
parents:
diff changeset
27 void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) {
kono
parents:
diff changeset
28 Drain(allocator);
kono
parents:
diff changeset
29 if (s)
kono
parents:
diff changeset
30 s->Unregister(&stats_);
kono
parents:
diff changeset
31 }
kono
parents:
diff changeset
32
kono
parents:
diff changeset
33 void *Allocate(SizeClassAllocator *allocator, uptr class_id) {
kono
parents:
diff changeset
34 CHECK_NE(class_id, 0UL);
kono
parents:
diff changeset
35 CHECK_LT(class_id, kNumClasses);
kono
parents:
diff changeset
36 PerClass *c = &per_class_[class_id];
kono
parents:
diff changeset
37 if (UNLIKELY(c->count == 0)) {
kono
parents:
diff changeset
38 if (UNLIKELY(!Refill(c, allocator, class_id)))
kono
parents:
diff changeset
39 return nullptr;
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
40 DCHECK_GT(c->count, 0);
111
kono
parents:
diff changeset
41 }
kono
parents:
diff changeset
42 CompactPtrT chunk = c->chunks[--c->count];
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
43 stats_.Add(AllocatorStatAllocated, c->class_size);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
44 return reinterpret_cast<void *>(allocator->CompactPtrToPointer(
111
kono
parents:
diff changeset
45 allocator->GetRegionBeginBySizeClass(class_id), chunk));
kono
parents:
diff changeset
46 }
kono
parents:
diff changeset
47
kono
parents:
diff changeset
48 void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
kono
parents:
diff changeset
49 CHECK_NE(class_id, 0UL);
kono
parents:
diff changeset
50 CHECK_LT(class_id, kNumClasses);
kono
parents:
diff changeset
51 // If the first allocator call on a new thread is a deallocation, then
kono
parents:
diff changeset
52 // max_count will be zero, leading to check failure.
kono
parents:
diff changeset
53 PerClass *c = &per_class_[class_id];
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
54 InitCache(c);
111
kono
parents:
diff changeset
55 if (UNLIKELY(c->count == c->max_count))
kono
parents:
diff changeset
56 Drain(c, allocator, class_id, c->max_count / 2);
kono
parents:
diff changeset
57 CompactPtrT chunk = allocator->PointerToCompactPtr(
kono
parents:
diff changeset
58 allocator->GetRegionBeginBySizeClass(class_id),
kono
parents:
diff changeset
59 reinterpret_cast<uptr>(p));
kono
parents:
diff changeset
60 c->chunks[c->count++] = chunk;
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
61 stats_.Sub(AllocatorStatAllocated, c->class_size);
111
kono
parents:
diff changeset
62 }
kono
parents:
diff changeset
63
kono
parents:
diff changeset
64 void Drain(SizeClassAllocator *allocator) {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
65 for (uptr i = 1; i < kNumClasses; i++) {
111
kono
parents:
diff changeset
66 PerClass *c = &per_class_[i];
kono
parents:
diff changeset
67 while (c->count > 0)
kono
parents:
diff changeset
68 Drain(c, allocator, i, c->count);
kono
parents:
diff changeset
69 }
kono
parents:
diff changeset
70 }
kono
parents:
diff changeset
71
kono
parents:
diff changeset
72 private:
kono
parents:
diff changeset
73 typedef typename Allocator::SizeClassMapT SizeClassMap;
kono
parents:
diff changeset
74 static const uptr kNumClasses = SizeClassMap::kNumClasses;
kono
parents:
diff changeset
75 typedef typename Allocator::CompactPtrT CompactPtrT;
kono
parents:
diff changeset
76
kono
parents:
diff changeset
77 struct PerClass {
kono
parents:
diff changeset
78 u32 count;
kono
parents:
diff changeset
79 u32 max_count;
kono
parents:
diff changeset
80 uptr class_size;
kono
parents:
diff changeset
81 CompactPtrT chunks[2 * SizeClassMap::kMaxNumCachedHint];
kono
parents:
diff changeset
82 };
kono
parents:
diff changeset
83 PerClass per_class_[kNumClasses];
kono
parents:
diff changeset
84 AllocatorStats stats_;
kono
parents:
diff changeset
85
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
86 void InitCache(PerClass *c) {
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
87 if (LIKELY(c->max_count))
111
kono
parents:
diff changeset
88 return;
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
89 for (uptr i = 1; i < kNumClasses; i++) {
111
kono
parents:
diff changeset
90 PerClass *c = &per_class_[i];
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
91 const uptr size = Allocator::ClassIdToSize(i);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
92 c->max_count = 2 * SizeClassMap::MaxCachedHint(size);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
93 c->class_size = size;
111
kono
parents:
diff changeset
94 }
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
95 DCHECK_NE(c->max_count, 0UL);
111
kono
parents:
diff changeset
96 }
kono
parents:
diff changeset
97
kono
parents:
diff changeset
98 NOINLINE bool Refill(PerClass *c, SizeClassAllocator *allocator,
kono
parents:
diff changeset
99 uptr class_id) {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
100 InitCache(c);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
101 const uptr num_requested_chunks = c->max_count / 2;
111
kono
parents:
diff changeset
102 if (UNLIKELY(!allocator->GetFromAllocator(&stats_, class_id, c->chunks,
kono
parents:
diff changeset
103 num_requested_chunks)))
kono
parents:
diff changeset
104 return false;
kono
parents:
diff changeset
105 c->count = num_requested_chunks;
kono
parents:
diff changeset
106 return true;
kono
parents:
diff changeset
107 }
kono
parents:
diff changeset
108
kono
parents:
diff changeset
109 NOINLINE void Drain(PerClass *c, SizeClassAllocator *allocator, uptr class_id,
kono
parents:
diff changeset
110 uptr count) {
kono
parents:
diff changeset
111 CHECK_GE(c->count, count);
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
112 const uptr first_idx_to_drain = c->count - count;
111
kono
parents:
diff changeset
113 c->count -= count;
kono
parents:
diff changeset
114 allocator->ReturnToAllocator(&stats_, class_id,
kono
parents:
diff changeset
115 &c->chunks[first_idx_to_drain], count);
kono
parents:
diff changeset
116 }
kono
parents:
diff changeset
117 };
kono
parents:
diff changeset
118
kono
parents:
diff changeset
119 // Cache used by SizeClassAllocator32.
kono
parents:
diff changeset
120 template <class SizeClassAllocator>
kono
parents:
diff changeset
121 struct SizeClassAllocator32LocalCache {
kono
parents:
diff changeset
122 typedef SizeClassAllocator Allocator;
kono
parents:
diff changeset
123 typedef typename Allocator::TransferBatch TransferBatch;
kono
parents:
diff changeset
124
kono
parents:
diff changeset
125 void Init(AllocatorGlobalStats *s) {
kono
parents:
diff changeset
126 stats_.Init();
kono
parents:
diff changeset
127 if (s)
kono
parents:
diff changeset
128 s->Register(&stats_);
kono
parents:
diff changeset
129 }
kono
parents:
diff changeset
130
kono
parents:
diff changeset
131 // Returns a TransferBatch suitable for class_id.
kono
parents:
diff changeset
132 TransferBatch *CreateBatch(uptr class_id, SizeClassAllocator *allocator,
kono
parents:
diff changeset
133 TransferBatch *b) {
kono
parents:
diff changeset
134 if (uptr batch_class_id = per_class_[class_id].batch_class_id)
kono
parents:
diff changeset
135 return (TransferBatch*)Allocate(allocator, batch_class_id);
kono
parents:
diff changeset
136 return b;
kono
parents:
diff changeset
137 }
kono
parents:
diff changeset
138
kono
parents:
diff changeset
139 // Destroys TransferBatch b.
kono
parents:
diff changeset
140 void DestroyBatch(uptr class_id, SizeClassAllocator *allocator,
kono
parents:
diff changeset
141 TransferBatch *b) {
kono
parents:
diff changeset
142 if (uptr batch_class_id = per_class_[class_id].batch_class_id)
kono
parents:
diff changeset
143 Deallocate(allocator, batch_class_id, b);
kono
parents:
diff changeset
144 }
kono
parents:
diff changeset
145
kono
parents:
diff changeset
146 void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) {
kono
parents:
diff changeset
147 Drain(allocator);
kono
parents:
diff changeset
148 if (s)
kono
parents:
diff changeset
149 s->Unregister(&stats_);
kono
parents:
diff changeset
150 }
kono
parents:
diff changeset
151
kono
parents:
diff changeset
152 void *Allocate(SizeClassAllocator *allocator, uptr class_id) {
kono
parents:
diff changeset
153 CHECK_NE(class_id, 0UL);
kono
parents:
diff changeset
154 CHECK_LT(class_id, kNumClasses);
kono
parents:
diff changeset
155 PerClass *c = &per_class_[class_id];
kono
parents:
diff changeset
156 if (UNLIKELY(c->count == 0)) {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
157 if (UNLIKELY(!Refill(c, allocator, class_id)))
111
kono
parents:
diff changeset
158 return nullptr;
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
159 DCHECK_GT(c->count, 0);
111
kono
parents:
diff changeset
160 }
kono
parents:
diff changeset
161 void *res = c->batch[--c->count];
kono
parents:
diff changeset
162 PREFETCH(c->batch[c->count - 1]);
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
163 stats_.Add(AllocatorStatAllocated, c->class_size);
111
kono
parents:
diff changeset
164 return res;
kono
parents:
diff changeset
165 }
kono
parents:
diff changeset
166
kono
parents:
diff changeset
167 void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) {
kono
parents:
diff changeset
168 CHECK_NE(class_id, 0UL);
kono
parents:
diff changeset
169 CHECK_LT(class_id, kNumClasses);
kono
parents:
diff changeset
170 // If the first allocator call on a new thread is a deallocation, then
kono
parents:
diff changeset
171 // max_count will be zero, leading to check failure.
kono
parents:
diff changeset
172 PerClass *c = &per_class_[class_id];
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
173 InitCache(c);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
174 if (UNLIKELY(c->count == c->max_count))
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
175 Drain(c, allocator, class_id);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
176 c->batch[c->count++] = p;
111
kono
parents:
diff changeset
177 stats_.Sub(AllocatorStatAllocated, c->class_size);
kono
parents:
diff changeset
178 }
kono
parents:
diff changeset
179
kono
parents:
diff changeset
180 void Drain(SizeClassAllocator *allocator) {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
181 for (uptr i = 1; i < kNumClasses; i++) {
111
kono
parents:
diff changeset
182 PerClass *c = &per_class_[i];
kono
parents:
diff changeset
183 while (c->count > 0)
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
184 Drain(c, allocator, i);
111
kono
parents:
diff changeset
185 }
kono
parents:
diff changeset
186 }
kono
parents:
diff changeset
187
kono
parents:
diff changeset
188 private:
kono
parents:
diff changeset
189 typedef typename Allocator::SizeClassMapT SizeClassMap;
kono
parents:
diff changeset
190 static const uptr kBatchClassID = SizeClassMap::kBatchClassID;
kono
parents:
diff changeset
191 static const uptr kNumClasses = SizeClassMap::kNumClasses;
kono
parents:
diff changeset
192 // If kUseSeparateSizeClassForBatch is true, all TransferBatch objects are
kono
parents:
diff changeset
193 // allocated from kBatchClassID size class (except for those that are needed
kono
parents:
diff changeset
194 // for kBatchClassID itself). The goal is to have TransferBatches in a totally
kono
parents:
diff changeset
195 // different region of RAM to improve security.
kono
parents:
diff changeset
196 static const bool kUseSeparateSizeClassForBatch =
kono
parents:
diff changeset
197 Allocator::kUseSeparateSizeClassForBatch;
kono
parents:
diff changeset
198
kono
parents:
diff changeset
199 struct PerClass {
kono
parents:
diff changeset
200 uptr count;
kono
parents:
diff changeset
201 uptr max_count;
kono
parents:
diff changeset
202 uptr class_size;
kono
parents:
diff changeset
203 uptr batch_class_id;
kono
parents:
diff changeset
204 void *batch[2 * TransferBatch::kMaxNumCached];
kono
parents:
diff changeset
205 };
kono
parents:
diff changeset
206 PerClass per_class_[kNumClasses];
kono
parents:
diff changeset
207 AllocatorStats stats_;
kono
parents:
diff changeset
208
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
209 void InitCache(PerClass *c) {
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
210 if (LIKELY(c->max_count))
111
kono
parents:
diff changeset
211 return;
kono
parents:
diff changeset
212 const uptr batch_class_id = SizeClassMap::ClassID(sizeof(TransferBatch));
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
213 for (uptr i = 1; i < kNumClasses; i++) {
111
kono
parents:
diff changeset
214 PerClass *c = &per_class_[i];
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
215 const uptr size = Allocator::ClassIdToSize(i);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
216 const uptr max_cached = TransferBatch::MaxCached(size);
111
kono
parents:
diff changeset
217 c->max_count = 2 * max_cached;
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
218 c->class_size = size;
111
kono
parents:
diff changeset
219 // Precompute the class id to use to store batches for the current class
kono
parents:
diff changeset
220 // id. 0 means the class size is large enough to store a batch within one
kono
parents:
diff changeset
221 // of the chunks. If using a separate size class, it will always be
kono
parents:
diff changeset
222 // kBatchClassID, except for kBatchClassID itself.
kono
parents:
diff changeset
223 if (kUseSeparateSizeClassForBatch) {
kono
parents:
diff changeset
224 c->batch_class_id = (i == kBatchClassID) ? 0 : kBatchClassID;
kono
parents:
diff changeset
225 } else {
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
226 c->batch_class_id = (size <
111
kono
parents:
diff changeset
227 TransferBatch::AllocationSizeRequiredForNElements(max_cached)) ?
kono
parents:
diff changeset
228 batch_class_id : 0;
kono
parents:
diff changeset
229 }
kono
parents:
diff changeset
230 }
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
231 DCHECK_NE(c->max_count, 0UL);
111
kono
parents:
diff changeset
232 }
kono
parents:
diff changeset
233
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
234 NOINLINE bool Refill(PerClass *c, SizeClassAllocator *allocator,
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
235 uptr class_id) {
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
236 InitCache(c);
111
kono
parents:
diff changeset
237 TransferBatch *b = allocator->AllocateBatch(&stats_, this, class_id);
kono
parents:
diff changeset
238 if (UNLIKELY(!b))
kono
parents:
diff changeset
239 return false;
kono
parents:
diff changeset
240 CHECK_GT(b->Count(), 0);
kono
parents:
diff changeset
241 b->CopyToArray(c->batch);
kono
parents:
diff changeset
242 c->count = b->Count();
kono
parents:
diff changeset
243 DestroyBatch(class_id, allocator, b);
kono
parents:
diff changeset
244 return true;
kono
parents:
diff changeset
245 }
kono
parents:
diff changeset
246
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
247 NOINLINE void Drain(PerClass *c, SizeClassAllocator *allocator,
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
248 uptr class_id) {
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
249 const uptr count = Min(c->max_count / 2, c->count);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
250 const uptr first_idx_to_drain = c->count - count;
111
kono
parents:
diff changeset
251 TransferBatch *b = CreateBatch(
kono
parents:
diff changeset
252 class_id, allocator, (TransferBatch *)c->batch[first_idx_to_drain]);
kono
parents:
diff changeset
253 // Failure to allocate a batch while releasing memory is non recoverable.
kono
parents:
diff changeset
254 // TODO(alekseys): Figure out how to do it without allocating a new batch.
145
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
255 if (UNLIKELY(!b)) {
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
256 Report("FATAL: Internal error: %s's allocator failed to allocate a "
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
257 "transfer batch.\n", SanitizerToolName);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
258 Die();
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
259 }
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
260 b->SetFromArray(&c->batch[first_idx_to_drain], count);
1830386684a0 gcc-9.2.0
anatofuz
parents: 111
diff changeset
261 c->count -= count;
111
kono
parents:
diff changeset
262 allocator->DeallocateBatch(&stats_, class_id, b);
kono
parents:
diff changeset
263 }
kono
parents:
diff changeset
264 };