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