blob: 28b16d976e5eac9d8909b8b9844a6b1a13f291b7 [file] [log] [blame]
Dynamic Tools Team517193e2019-09-11 14:48:41 +00001//===-- size_class_map.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_SIZE_CLASS_MAP_H_
10#define SCUDO_SIZE_CLASS_MAP_H_
11
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080012#include "chunk.h"
Dynamic Tools Team517193e2019-09-11 14:48:41 +000013#include "common.h"
14#include "string_utils.h"
15
16namespace scudo {
17
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080018inline uptr scaledLog2(uptr Size, uptr ZeroLog, uptr LogBits) {
19 const uptr L = getMostSignificantSetBitIndex(Size);
20 const uptr LBits = (Size >> (L - LogBits)) - (1 << LogBits);
21 const uptr HBits = (L - ZeroLog) << LogBits;
22 return LBits + HBits;
23}
24
25template <typename Config> struct SizeClassMapBase {
26 static u32 getMaxCachedHint(uptr Size) {
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080027 DCHECK_NE(Size, 0);
28 u32 N;
29 // Force a 32-bit division if the template parameters allow for it.
30 if (Config::MaxBytesCachedLog > 31 || Config::MaxSizeLog > 31)
31 N = static_cast<u32>((1UL << Config::MaxBytesCachedLog) / Size);
32 else
33 N = (1U << Config::MaxBytesCachedLog) / static_cast<u32>(Size);
34 return Max(1U, Min(Config::MaxNumCachedHint, N));
35 }
36};
37
Dynamic Tools Team517193e2019-09-11 14:48:41 +000038// SizeClassMap maps allocation sizes into size classes and back, in an
39// efficient table-free manner.
40//
41// Class 0 is a special class that doesn't abide by the same rules as other
42// classes. The allocator uses it to hold batches.
43//
44// The other sizes are controlled by the template parameters:
45// - MinSizeLog: defines the first class as 2^MinSizeLog bytes.
46// - MaxSizeLog: defines the last class as 2^MaxSizeLog bytes.
47// - MidSizeLog: classes increase with step 2^MinSizeLog from 2^MinSizeLog to
48// 2^MidSizeLog bytes.
49// - NumBits: the number of non-zero bits in sizes after 2^MidSizeLog.
50// eg. with NumBits==3 all size classes after 2^MidSizeLog look like
51// 0b1xx0..0 (where x is either 0 or 1).
52//
53// This class also gives a hint to a thread-caching allocator about the amount
54// of chunks that can be cached per-thread:
55// - MaxNumCachedHint is a hint for the max number of chunks cached per class.
56// - 2^MaxBytesCachedLog is the max number of bytes cached per class.
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080057template <typename Config>
58class FixedSizeClassMap : public SizeClassMapBase<Config> {
59 typedef SizeClassMapBase<Config> Base;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000060
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080061 static const uptr MinSize = 1UL << Config::MinSizeLog;
62 static const uptr MidSize = 1UL << Config::MidSizeLog;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000063 static const uptr MidClass = MidSize / MinSize;
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080064 static const u8 S = Config::NumBits - 1;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000065 static const uptr M = (1UL << S) - 1;
66
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080067public:
68 static const u32 MaxNumCachedHint = Config::MaxNumCachedHint;
69
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -070070 static const uptr MaxSize = (1UL << Config::MaxSizeLog) + Config::SizeDelta;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000071 static const uptr NumClasses =
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080072 MidClass + ((Config::MaxSizeLog - Config::MidSizeLog) << S) + 1;
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080073 static_assert(NumClasses <= 256, "");
Dynamic Tools Team517193e2019-09-11 14:48:41 +000074 static const uptr LargestClassId = NumClasses - 1;
75 static const uptr BatchClassId = 0;
76
77 static uptr getSizeByClassId(uptr ClassId) {
78 DCHECK_NE(ClassId, BatchClassId);
79 if (ClassId <= MidClass)
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -070080 return (ClassId << Config::MinSizeLog) + Config::SizeDelta;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000081 ClassId -= MidClass;
82 const uptr T = MidSize << (ClassId >> S);
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -070083 return T + (T >> S) * (ClassId & M) + Config::SizeDelta;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000084 }
85
Peter Collingbourne39f99a92021-04-21 21:03:45 -070086 static u8 getSizeLSBByClassId(uptr ClassId) {
87 return u8(getLeastSignificantSetBitIndex(getSizeByClassId(ClassId)));
88 }
89
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -070090 static constexpr bool usesCompressedLSBFormat() { return false; }
Peter Collingbournee910c692021-04-22 12:59:57 -070091
Dynamic Tools Team517193e2019-09-11 14:48:41 +000092 static uptr getClassIdBySize(uptr Size) {
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -070093 if (Size <= Config::SizeDelta + (1 << Config::MinSizeLog))
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080094 return 1;
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -070095 Size -= Config::SizeDelta;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000096 DCHECK_LE(Size, MaxSize);
97 if (Size <= MidSize)
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -080098 return (Size + MinSize - 1) >> Config::MinSizeLog;
99 return MidClass + 1 + scaledLog2(Size - 1, Config::MidSizeLog, S);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000100 }
Dynamic Tools Teama98a9962020-02-13 09:27:18 -0800101
102 static u32 getMaxCachedHint(uptr Size) {
103 DCHECK_LE(Size, MaxSize);
104 return Base::getMaxCachedHint(Size);
105 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000106};
107
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800108template <typename Config>
109class TableSizeClassMap : public SizeClassMapBase<Config> {
Dynamic Tools Teama98a9962020-02-13 09:27:18 -0800110 typedef SizeClassMapBase<Config> Base;
111
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800112 static const u8 S = Config::NumBits - 1;
113 static const uptr M = (1UL << S) - 1;
114 static const uptr ClassesSize =
115 sizeof(Config::Classes) / sizeof(Config::Classes[0]);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000116
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800117 struct SizeTable {
118 constexpr SizeTable() {
119 uptr Pos = 1 << Config::MidSizeLog;
120 uptr Inc = 1 << (Config::MidSizeLog - S);
121 for (uptr i = 0; i != getTableSize(); ++i) {
122 Pos += Inc;
123 if ((Pos & (Pos - 1)) == 0)
124 Inc *= 2;
125 Tab[i] = computeClassId(Pos + Config::SizeDelta);
126 }
127 }
128
129 constexpr static u8 computeClassId(uptr Size) {
130 for (uptr i = 0; i != ClassesSize; ++i) {
131 if (Size <= Config::Classes[i])
Dynamic Tools Teama98a9962020-02-13 09:27:18 -0800132 return static_cast<u8>(i + 1);
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800133 }
Dynamic Tools Teama98a9962020-02-13 09:27:18 -0800134 return static_cast<u8>(-1);
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800135 }
136
137 constexpr static uptr getTableSize() {
138 return (Config::MaxSizeLog - Config::MidSizeLog) << S;
139 }
140
141 u8 Tab[getTableSize()] = {};
142 };
143
Peter Collingbourne39f99a92021-04-21 21:03:45 -0700144 static constexpr SizeTable SzTable = {};
145
146 struct LSBTable {
147 constexpr LSBTable() {
Peter Collingbournee910c692021-04-22 12:59:57 -0700148 u8 Min = 255, Max = 0;
Peter Collingbourne39f99a92021-04-21 21:03:45 -0700149 for (uptr I = 0; I != ClassesSize; ++I) {
150 for (u8 Bit = 0; Bit != 64; ++Bit) {
151 if (Config::Classes[I] & (1 << Bit)) {
152 Tab[I] = Bit;
Peter Collingbournee910c692021-04-22 12:59:57 -0700153 if (Bit < Min)
154 Min = Bit;
155 if (Bit > Max)
156 Max = Bit;
Peter Collingbourne39f99a92021-04-21 21:03:45 -0700157 break;
158 }
159 }
160 }
Peter Collingbournee910c692021-04-22 12:59:57 -0700161
162 if (Max - Min > 3 || ClassesSize > 32)
163 return;
164
165 UseCompressedFormat = true;
166 CompressedMin = Min;
167 for (uptr I = 0; I != ClassesSize; ++I)
168 CompressedValue |= u64(Tab[I] - Min) << (I * 2);
Peter Collingbourne39f99a92021-04-21 21:03:45 -0700169 }
170
171 u8 Tab[ClassesSize] = {};
Peter Collingbournee910c692021-04-22 12:59:57 -0700172
173 bool UseCompressedFormat = false;
174 u8 CompressedMin = 0;
175 u64 CompressedValue = 0;
Peter Collingbourne39f99a92021-04-21 21:03:45 -0700176 };
177
178 static constexpr LSBTable LTable = {};
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800179
180public:
181 static const u32 MaxNumCachedHint = Config::MaxNumCachedHint;
182
183 static const uptr NumClasses = ClassesSize + 1;
184 static_assert(NumClasses < 256, "");
185 static const uptr LargestClassId = NumClasses - 1;
186 static const uptr BatchClassId = 0;
187 static const uptr MaxSize = Config::Classes[LargestClassId - 1];
188
189 static uptr getSizeByClassId(uptr ClassId) {
190 return Config::Classes[ClassId - 1];
191 }
192
Peter Collingbourne39f99a92021-04-21 21:03:45 -0700193 static u8 getSizeLSBByClassId(uptr ClassId) {
Peter Collingbournee910c692021-04-22 12:59:57 -0700194 if (LTable.UseCompressedFormat)
195 return ((LTable.CompressedValue >> ((ClassId - 1) * 2)) & 3) +
196 LTable.CompressedMin;
197 else
198 return LTable.Tab[ClassId - 1];
199 }
200
201 static constexpr bool usesCompressedLSBFormat() {
202 return LTable.UseCompressedFormat;
Peter Collingbourne39f99a92021-04-21 21:03:45 -0700203 }
204
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800205 static uptr getClassIdBySize(uptr Size) {
206 if (Size <= Config::Classes[0])
207 return 1;
208 Size -= Config::SizeDelta;
209 DCHECK_LE(Size, MaxSize);
210 if (Size <= (1 << Config::MidSizeLog))
211 return ((Size - 1) >> Config::MinSizeLog) + 1;
Peter Collingbourne39f99a92021-04-21 21:03:45 -0700212 return SzTable.Tab[scaledLog2(Size - 1, Config::MidSizeLog, S)];
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800213 }
214
Dynamic Tools Teama98a9962020-02-13 09:27:18 -0800215 static u32 getMaxCachedHint(uptr Size) {
216 DCHECK_LE(Size, MaxSize);
217 return Base::getMaxCachedHint(Size);
218 }
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800219};
220
Kostya Kortchinskyc9369542021-02-10 10:17:18 -0800221struct DefaultSizeClassConfig {
222 static const uptr NumBits = 3;
223 static const uptr MinSizeLog = 5;
224 static const uptr MidSizeLog = 8;
225 static const uptr MaxSizeLog = 17;
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -0700226 static const u32 MaxNumCachedHint = 14;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -0800227 static const uptr MaxBytesCachedLog = 10;
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -0700228 static const uptr SizeDelta = 0;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -0800229};
230
231typedef FixedSizeClassMap<DefaultSizeClassConfig> DefaultSizeClassMap;
232
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -0700233struct FuchsiaSizeClassConfig {
234 static const uptr NumBits = 3;
235 static const uptr MinSizeLog = 5;
236 static const uptr MidSizeLog = 8;
237 static const uptr MaxSizeLog = 17;
238 static const u32 MaxNumCachedHint = 10;
239 static const uptr MaxBytesCachedLog = 10;
240 static const uptr SizeDelta = Chunk::getHeaderSize();
241};
242
243typedef FixedSizeClassMap<FuchsiaSizeClassConfig> FuchsiaSizeClassMap;
244
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800245struct AndroidSizeClassConfig {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000246#if SCUDO_WORDSIZE == 64U
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800247 static const uptr NumBits = 7;
248 static const uptr MinSizeLog = 4;
249 static const uptr MidSizeLog = 6;
250 static const uptr MaxSizeLog = 16;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -0800251 static const u32 MaxNumCachedHint = 13;
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800252 static const uptr MaxBytesCachedLog = 13;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000253
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800254 static constexpr u32 Classes[] = {
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800255 0x00020, 0x00030, 0x00040, 0x00050, 0x00060, 0x00070, 0x00090, 0x000b0,
256 0x000c0, 0x000e0, 0x00120, 0x00160, 0x001c0, 0x00250, 0x00320, 0x00450,
257 0x00670, 0x00830, 0x00a10, 0x00c30, 0x01010, 0x01210, 0x01bd0, 0x02210,
258 0x02d90, 0x03790, 0x04010, 0x04810, 0x05a10, 0x07310, 0x08210, 0x10010,
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800259 };
260 static const uptr SizeDelta = 16;
261#else
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800262 static const uptr NumBits = 8;
263 static const uptr MinSizeLog = 4;
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800264 static const uptr MidSizeLog = 7;
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800265 static const uptr MaxSizeLog = 16;
266 static const u32 MaxNumCachedHint = 14;
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800267 static const uptr MaxBytesCachedLog = 13;
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800268
269 static constexpr u32 Classes[] = {
270 0x00020, 0x00030, 0x00040, 0x00050, 0x00060, 0x00070, 0x00080, 0x00090,
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800271 0x000a0, 0x000b0, 0x000c0, 0x000e0, 0x000f0, 0x00110, 0x00120, 0x00130,
272 0x00150, 0x00160, 0x00170, 0x00190, 0x001d0, 0x00210, 0x00240, 0x002a0,
273 0x00330, 0x00370, 0x003a0, 0x00400, 0x00430, 0x004a0, 0x00530, 0x00610,
274 0x00730, 0x00840, 0x00910, 0x009c0, 0x00a60, 0x00b10, 0x00ca0, 0x00e00,
275 0x00fb0, 0x01030, 0x01130, 0x011f0, 0x01490, 0x01650, 0x01930, 0x02010,
276 0x02190, 0x02490, 0x02850, 0x02d50, 0x03010, 0x03210, 0x03c90, 0x04090,
277 0x04510, 0x04810, 0x05c10, 0x06f10, 0x07310, 0x08010, 0x0c010, 0x10010,
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800278 };
279 static const uptr SizeDelta = 16;
280#endif
281};
282
283typedef TableSizeClassMap<AndroidSizeClassConfig> AndroidSizeClassMap;
284
Peter Collingbourne1b552942021-04-22 16:09:23 -0700285#if SCUDO_WORDSIZE == 64U && defined(__clang__)
Peter Collingbournee910c692021-04-22 12:59:57 -0700286static_assert(AndroidSizeClassMap::usesCompressedLSBFormat(), "");
287#endif
288
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800289struct SvelteSizeClassConfig {
290#if SCUDO_WORDSIZE == 64U
291 static const uptr NumBits = 4;
292 static const uptr MinSizeLog = 4;
293 static const uptr MidSizeLog = 8;
294 static const uptr MaxSizeLog = 14;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -0800295 static const u32 MaxNumCachedHint = 13;
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800296 static const uptr MaxBytesCachedLog = 10;
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -0700297 static const uptr SizeDelta = Chunk::getHeaderSize();
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800298#else
299 static const uptr NumBits = 4;
300 static const uptr MinSizeLog = 3;
301 static const uptr MidSizeLog = 7;
302 static const uptr MaxSizeLog = 14;
Kostya Kortchinskyc9369542021-02-10 10:17:18 -0800303 static const u32 MaxNumCachedHint = 14;
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800304 static const uptr MaxBytesCachedLog = 10;
Kostya Kortchinsky27c58d42021-05-19 09:10:30 -0700305 static const uptr SizeDelta = Chunk::getHeaderSize();
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800306#endif
307};
308
309typedef FixedSizeClassMap<SvelteSizeClassConfig> SvelteSizeClassMap;
310
Daniel Michael63933f12021-06-07 09:10:46 -0700311// Trusty is configured to only have one region containing blocks of size
312// 2^7 bytes.
313struct TrustySizeClassConfig {
314 static const uptr NumBits = 1;
315 static const uptr MinSizeLog = 7;
316 static const uptr MidSizeLog = 7;
317 static const uptr MaxSizeLog = 7;
318 static const u32 MaxNumCachedHint = 8;
319 static const uptr MaxBytesCachedLog = 10;
320 static const uptr SizeDelta = 0;
321};
322
323typedef FixedSizeClassMap<TrustySizeClassConfig> TrustySizeClassMap;
324
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800325template <typename SCMap> inline void printMap() {
Kostya Kortchinsky53aea5c2021-06-03 12:11:05 -0700326 ScopedString Buffer;
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800327 uptr PrevS = 0;
328 uptr TotalCached = 0;
329 for (uptr I = 0; I < SCMap::NumClasses; I++) {
330 if (I == SCMap::BatchClassId)
331 continue;
332 const uptr S = SCMap::getSizeByClassId(I);
333 const uptr D = S - PrevS;
334 const uptr P = PrevS ? (D * 100 / PrevS) : 0;
335 const uptr L = S ? getMostSignificantSetBitIndex(S) : 0;
336 const uptr Cached = SCMap::getMaxCachedHint(S) * S;
337 Buffer.append(
Kostya Kortchinskyc2e59962021-08-16 15:23:48 -0700338 "C%02zu => S: %zu diff: +%zu %02zu%% L %zu Cached: %u %zu; id %zu\n", I,
339 S, D, P, L, SCMap::getMaxCachedHint(S), Cached,
Dynamic Tools Team130bfdb2020-02-10 15:53:07 -0800340 SCMap::getClassIdBySize(S));
341 TotalCached += Cached;
342 PrevS = S;
343 }
344 Buffer.append("Total Cached: %zu\n", TotalCached);
345 Buffer.output();
346}
347
348template <typename SCMap> static void validateMap() {
349 for (uptr C = 0; C < SCMap::NumClasses; C++) {
350 if (C == SCMap::BatchClassId)
351 continue;
352 const uptr S = SCMap::getSizeByClassId(C);
353 CHECK_NE(S, 0U);
354 CHECK_EQ(SCMap::getClassIdBySize(S), C);
355 if (C < SCMap::LargestClassId)
356 CHECK_EQ(SCMap::getClassIdBySize(S + 1), C + 1);
357 CHECK_EQ(SCMap::getClassIdBySize(S - 1), C);
358 if (C - 1 != SCMap::BatchClassId)
359 CHECK_GT(SCMap::getSizeByClassId(C), SCMap::getSizeByClassId(C - 1));
360 }
361 // Do not perform the loop if the maximum size is too large.
362 if (SCMap::MaxSize > (1 << 19))
363 return;
364 for (uptr S = 1; S <= SCMap::MaxSize; S++) {
365 const uptr C = SCMap::getClassIdBySize(S);
366 CHECK_LT(C, SCMap::NumClasses);
367 CHECK_GE(SCMap::getSizeByClassId(C), S);
368 if (C - 1 != SCMap::BatchClassId)
369 CHECK_LT(SCMap::getSizeByClassId(C - 1), S);
370 }
371}
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000372} // namespace scudo
373
374#endif // SCUDO_SIZE_CLASS_MAP_H_