1 //===-- libsize_map_verify.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_LIBSIZE_MAP_VERIFY_H_
10 #define SCUDO_LIBSIZE_MAP_VERIFY_H_
11
12 #include "common.h"
13 #include "size_class_map.h"
14 #include <string>
15 #include <vector>
16
17 namespace scudo {
18 typedef u8 szTableT;
19
20 // Returns the index of each size class.
21 // Attempting to find the smallest size that fits within each size class.
22 // Example:
23 // If a size class is 8 then 4,5,6,7,8 return the index of size 8
24 // but 9 would return the index of the next size class, 16.
computeClassId(uptr Size,u32 ClassesSize,u32 Classes[])25 u8 computeClassId(uptr Size, u32 ClassesSize, u32 Classes[]) {
26 for (uptr i = 0; i != ClassesSize; ++i)
27 if (Size <= Classes[i])
28 return static_cast<u8>(i + 1);
29 return static_cast<u8>(-1);
30 }
31 // Function returns a vector that contains the classIds for all the sizes.
32 // Needed to check if the NumBits generated assigns the indexes to
33 // classIds correctly.
szTableCreate(u32 NumBits,u32 MidSizeLog,u32 MaxSizeLog,u32 SizeDelta,u32 ClassesSize,u32 Classes[])34 std::vector<u8> szTableCreate(u32 NumBits, u32 MidSizeLog, u32 MaxSizeLog,
35 u32 SizeDelta, u32 ClassesSize, u32 Classes[]) {
36 std::vector<u8> Tab((MaxSizeLog - MidSizeLog) << NumBits);
37 // Pos starts at the MidSize, which ignores the sizes not used in szTable.
38 // Inc uses NumBits - 1 to determine the starting incrementing value.
39 // Tab gets the classId of each size based on computeClassId.
40 uptr Pos = 1 << MidSizeLog;
41 uptr Inc = 1 << (MidSizeLog - NumBits);
42 for (uptr i = 0; i != Tab.size(); ++i) {
43 Pos += Inc;
44 if ((Pos & (Pos - 1)) == 0)
45 Inc *= 2;
46 Tab[i] = computeClassId(Pos + SizeDelta, ClassesSize, Classes);
47 }
48 return Tab;
49 };
50
51 // Function returns the index of the first value greater than MidSizeLog.
findMidSizeIndex()52 template <typename Config> uptr findMidSizeIndex() {
53 const u32 len = sizeof(Config::Classes) / sizeof(Config::Classes[0]);
54 u32 largerMid = 0;
55 for (uptr i = 0; i < len; ++i) {
56 if (Config::Classes[i] > (1 << Config::MidSizeLog) + Config::SizeDelta) {
57 largerMid = i;
58 break;
59 }
60 }
61 return largerMid;
62 }
63
64 // Calculates the minimum NumBits that can be used for the given sizes and
65 // Min/Mid/Max. Smaller NumBits creates a szTable nearly half the size and
66 // quickens navigation of the table. The sizes smaller than MidSizeLog do not
67 // use NumBits or szTable, instead using a formula. This method is faster but
68 // requires sizes to have the exact same spacing of 2^MinSizeLog; therefore,
69 // having an efficient NumBits allows for the table to be more flexible than the
70 // formula while still moving at a reasonable speed.
generateNumBits(std::string & manipMessage)71 template <typename Config> bool generateNumBits(std::string &manipMessage) {
72 // In size_class_map S is used, so it is used for consistency.
73 u32 S = Config::NumBits - 1;
74 const u32 len = sizeof(Config::Classes) / sizeof(Config::Classes[0]);
75 // This is used to display the NumBits calculated
76 u32 minNumBits = S;
77 // largerMid equals the index of the first value greater than MidSizeLog.
78 // These sizes are the only ones used with NumBits, smaller sizes are
79 // ignored.
80 const u32 largerMid = findMidSizeIndex<Config>();
81 if (largerMid == 0) {
82 manipMessage +=
83 "MidSizeLog = MaxSizeLog, NumBits not used for these sizes. "
84 "Only uses the formula without szTable.\n";
85 return true;
86 }
87
88 // Create Classes array that can be inputed into functions and referenced.
89 u32 ClassesFunc[len];
90 for (uptr i = 0; i < len; ++i)
91 ClassesFunc[i] = Config::Classes[i];
92 // Create smaller Classes array that can be manipulated to calculate NumBits.
93 u32 ClassesManip[len - largerMid];
94 for (uptr i = 0; i < len - largerMid; ++i)
95 ClassesManip[i] = ClassesFunc[i + largerMid] - Config::SizeDelta;
96
97 u32 holdIndex[len - largerMid];
98 bool failed = false;
99 // Starting at intial S, it decreases to find the smallest working S
100 // for the current config.
101 for (; S > 0; --S) {
102 // For each size it calls the on the algorithm which retuns an index.
103 for (uptr i = 0; i < len - largerMid; ++i)
104 holdIndex[i] = scaledLog2(ClassesManip[i] - 1, Config::MidSizeLog, S);
105
106 // Vector that holds classIds for sizes that is navigated using indexes
107 // stored in holdIndex.
108 std::vector szTableT =
109 szTableCreate(S, Config::MidSizeLog, Config::MaxSizeLog,
110 Config::SizeDelta, len, ClassesFunc);
111
112 // Checks that each index in holdIndex should refer to a unique classId,
113 // therefore a unique size a duplicate means that the calculated index
114 // for two different sizes refers to the same classId.
115 for (uptr i = 1; i < len - largerMid; ++i) {
116 if (szTableT[holdIndex[i]] == szTableT[holdIndex[i - 1]]) {
117 failed = true;
118 break;
119 }
120 }
121 if (failed == true)
122 break;
123 }
124 // Setting minNumBits to the last working NumBits and Numbits = S + 1.
125 minNumBits = S + 2;
126 // Adds a check to ensure NumBits calculated is not too large.
127 if (minNumBits - 1 > Config::MidSizeLog) {
128 manipMessage +=
129 "Calculated Numbits too large. The max size for NumBits is: "
130 "NumBits - 1 = MidSizeLog.\n"
131 "NumBits = " +
132 std::to_string(minNumBits) + "\n";
133 return false;
134 }
135 manipMessage += "NumBits = " + std::to_string(minNumBits) + "\n";
136 return true;
137 }
138
139 // Verify the sizes and variables entered are functional.
140 // If not, gives a brief explaination of the error.
verifySizeClass(std::string & manipMessage)141 template <typename Config> bool verifySizeClass(std::string &manipMessage) {
142 const u32 len = sizeof(Config::Classes) / sizeof(Config::Classes[0]);
143 u32 ClassesFunc[len];
144 for (uptr i = 0; i < len; ++i)
145 ClassesFunc[i] = Config::Classes[i];
146
147 // Verify smallest size = MinSizeLog and largest size = MaxSizeLog.
148 // Log base 2 of (smallest size - SizeDelta) and
149 // Log base 2 of (largest size - SizeDelta).
150 const u32 MinSize = (1 << Config::MinSizeLog);
151 const u32 MaxSize = (1 << Config::MaxSizeLog);
152 if (ClassesFunc[0] - Config::SizeDelta != MinSize) {
153 manipMessage += "MinSizeLog + SizeDelta not equal to the smallest size. " +
154 std::to_string(MinSize) +
155 " != " + std::to_string(ClassesFunc[0]) + "\n\n";
156 return false;
157 }
158 if (ClassesFunc[len - 1] - Config::SizeDelta != MaxSize) {
159 manipMessage += "Largest size (" + std::to_string(ClassesFunc[len - 1]) +
160 ") - SizeDelta (" + std::to_string(Config::SizeDelta) +
161 ") != MaxSize (" + std::to_string(MaxSize) + ")\n\n";
162 return false;
163 }
164 // Verify MidSizeLog is greater than MinSizeLog.
165 const u32 MidSize = (1 << Config::MidSizeLog);
166 if (MidSize <= MinSize) {
167 manipMessage +=
168 "MidSizeLog needs to be greater than MinSizeLog\n"
169 "If the MidSizeLog is equal to MinSizeLog then the szTable will be "
170 "used for every size.\nMin size = " +
171 std::to_string(MinSize) + "\tMid size = " + std::to_string(MidSize) +
172 "\n\n";
173 return false;
174 }
175
176 // Displays why MidSizeLog is not working.
177 for (uptr i = 1; i < len; ++i) {
178 // If the step ends prior to MidSize, the step needs extending.
179 if (ClassesFunc[i] - ClassesFunc[i - 1] != 1 << Config::MinSizeLog &&
180 ClassesFunc[i - 1] - Config::SizeDelta < MidSize) {
181 manipMessage +=
182 "MidSizeLog non-table formula can be used until: " +
183 std::to_string(ClassesFunc[i - 1]) +
184 "\n\nCurrently stops at: " + std::to_string(MidSize) +
185 "\nFor size_map to work, formula must work for a number >= "
186 "the current MidSize.\nMidSizeLog is either too large or their "
187 "is not an equal step between desired sizes."
188 "\nThe step between sizes should equal 2^MinSizeLog.\n\n";
189 return false;
190 } else if (ClassesFunc[i] - ClassesFunc[i - 1] != 1 << Config::MinSizeLog ||
191 MidSize == MaxSize) {
192 manipMessage += "MidSizeLog non-szTable formula is used until: " +
193 std::to_string(MidSize + Config::SizeDelta) + "\n";
194 break;
195 }
196 }
197 // Verifying if the MidSizeLog and MaxSizeLog.
198 if (MidSize == MaxSize) {
199 manipMessage +=
200 "MidSizeLog = MaxSizeLog, szTable and NumBits are not used at "
201 "all.\n";
202 return true;
203 }
204
205 // Recreates NumBits arrays/vectors to verify the NumBits.
206 // Explained in generateNumBits.
207 u32 S = Config::NumBits - 1;
208 std::vector szTableT =
209 szTableCreate(S, Config::MidSizeLog, Config::MaxSizeLog,
210 Config::SizeDelta, len, ClassesFunc);
211 const u32 largerMid = findMidSizeIndex<Config>();
212 u32 ClassesManip[len - largerMid];
213 for (uptr i = 0; i < len - largerMid; ++i)
214 ClassesManip[i] = Config::Classes[i + largerMid] - Config::SizeDelta;
215 u32 holdIndex[len - largerMid];
216 for (uptr i = 0; i < len - largerMid; ++i)
217 holdIndex[i] = scaledLog2(ClassesManip[i] - 1, Config::MidSizeLog, S);
218
219 for (uptr i = 1; i < len - largerMid; ++i) {
220 if (szTableT[holdIndex[i]] == szTableT[holdIndex[i - 1]]) {
221 manipMessage +=
222 "\nNumBits not large enough to distinguish between values. "
223 "\nHard max NumBits - 1 cannot exceed MidSizeLog.\n"
224 "If NumBits is at max then increase Min/Mid/Max sizelogs and "
225 "increase the sizes accordingly.\n\n\n";
226 return false;
227 }
228 }
229 return true;
230 }
231
232 // Display to what size MidSizeLog will work with and most efficient numbers.
233 // MidSizeLog uses a formula, not a table.
optimizeMidSizeLog(std::string & manipMessage)234 template <typename Config> void optimizeMidSizeLog(std::string &manipMessage) {
235 const u32 len = sizeof(Config::Classes) / sizeof(Config::Classes[0]);
236 u32 ClassesFunc[len];
237 for (uptr i = 0; i < len; ++i)
238 ClassesFunc[i] = Config::Classes[i];
239
240 const u32 MaxSize = (1 << Config::MaxSizeLog);
241 const u32 MidSize = (1 << Config::MidSizeLog);
242 for (uptr i = 1; i < len; ++i) {
243 if (ClassesFunc[i] - ClassesFunc[i - 1] == 1 << Config::MinSizeLog)
244 continue;
245 manipMessage +=
246 "MidSizeLog non-table formula can be used until: " +
247 std::to_string(ClassesFunc[i - 1]) +
248 "\nCurrently stops at: " + std::to_string(MidSize + Config::SizeDelta) +
249 "\n";
250 if (MidSize == ClassesFunc[i - 1] - Config::SizeDelta) {
251 manipMessage +=
252 "MidSizeLog is used efficiently and fully for current config\n";
253 } else {
254 manipMessage +=
255 "For size_map to work, formula must work for a number "
256 ">= the current MidSize.\nMax efficiency is achieved if they "
257 "are equal.\n";
258 if (ClassesFunc[i - 1] - Config::SizeDelta > MidSize) {
259 manipMessage +=
260 "In order to match numbers, increase MidSizeLog.\nEnsure "
261 "each size up to the new MidSize has an equal step between "
262 "each size.\nThe step equals 2^MinSizeLog.\n";
263 } else if (ClassesFunc[i - 1] - Config::SizeDelta < MidSize) {
264 manipMessage +=
265 "MidSizeLog is either too large or their is not an equal "
266 "step between desired sizes.\nThe step between sizes "
267 "should equal 2^MinSizeLog.\n";
268 }
269 }
270 break;
271 }
272 if (ClassesFunc[len - 1] - Config::SizeDelta == MidSize) {
273 manipMessage +=
274 "MidSizeLog non-table formula can be used until: " +
275 std::to_string(ClassesFunc[len - 1]) +
276 "\nCurrently stops at: " + std::to_string(MidSize) +
277 "\n"
278 "MidSizeLog is used efficiently and fully for current config\n";
279 }
280 }
281
282 // Dumps the size of szTable in elements and bits.
dumpszTableInfo(std::string & manipMessage)283 template <typename Config> bool dumpszTableInfo(std::string &manipMessage) {
284 u32 S = Config::NumBits - 1;
285 const u32 len = sizeof(Config::Classes) / sizeof(Config::Classes[0]);
286 u32 minNumBits = S;
287 const u32 largerMid = findMidSizeIndex<Config>();
288 bool failed = false;
289
290 if (largerMid == 0) {
291 manipMessage += "Does not use NumBits. MidSizeLog = MaxsizeLog.";
292 return true;
293 }
294 u32 ClassesFunc[len];
295 for (uptr i = 0; i < len; ++i)
296 ClassesFunc[i] = Config::Classes[i];
297 std::vector szTableT =
298 szTableCreate(S, Config::MidSizeLog, Config::MaxSizeLog,
299 Config::SizeDelta, len, ClassesFunc);
300 manipMessage +=
301 "szTable Number of Elements: " + std::to_string(szTableT.size()) +
302 "\nSize of szTable in Bits: " +
303 std::to_string(szTableT.size() * sizeof(u8)) + "\n";
304 return true;
305 }
306 } // namespace scudo
307
308 #endif // SCUDO_LIBSIZE_MAP_VERIFY_H_
309