| 1 | //===-- compute_size_class_config.cpp -------------------------------------===// |
| 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 | #include <errno.h> |
| 10 | #include <stdio.h> |
| 11 | #include <stdlib.h> |
| 12 | #include <string.h> |
| 13 | |
| 14 | #include <algorithm> |
| 15 | #include <vector> |
| 16 | |
| 17 | struct Alloc { |
| 18 | size_t size, count; |
| 19 | }; |
| 20 | |
| 21 | size_t measureWastage(const std::vector<Alloc> &allocs, |
| 22 | const std::vector<size_t> &classes, size_t pageSize, |
| 23 | size_t ) { |
| 24 | size_t totalWastage = 0; |
| 25 | for (auto &a : allocs) { |
| 26 | size_t sizePlusHeader = a.size + headerSize; |
| 27 | size_t wastage = -1ull; |
| 28 | for (auto c : classes) |
| 29 | if (c >= sizePlusHeader && c - sizePlusHeader < wastage) |
| 30 | wastage = c - sizePlusHeader; |
| 31 | if (wastage == -1ull) |
| 32 | continue; |
| 33 | if (wastage > 2 * pageSize) |
| 34 | wastage = 2 * pageSize; |
| 35 | totalWastage += wastage * a.count; |
| 36 | } |
| 37 | return totalWastage; |
| 38 | } |
| 39 | |
| 40 | void readAllocs(std::vector<Alloc> &allocs, const char *path) { |
| 41 | FILE *f = fopen(path, "r" ); |
| 42 | if (!f) { |
| 43 | fprintf(stderr, "compute_size_class_config: could not open %s: %s\n" , path, |
| 44 | strerror(errno)); |
| 45 | exit(1); |
| 46 | } |
| 47 | |
| 48 | const char header[] = "<malloc version=\"scudo-1\">\n" ; |
| 49 | char buf[sizeof(header) - 1]; |
| 50 | if (fread(buf, 1, sizeof(header) - 1, f) != sizeof(header) - 1 || |
| 51 | memcmp(buf, header, sizeof(header) - 1) != 0) { |
| 52 | fprintf(stderr, "compute_size_class_config: invalid input format\n" ); |
| 53 | exit(1); |
| 54 | } |
| 55 | |
| 56 | Alloc a; |
| 57 | while (fscanf(f, "<alloc size=\"%zu\" count=\"%zu\"/>\n" , &a.size, |
| 58 | &a.count) == 2) |
| 59 | allocs.push_back(a); |
| 60 | fclose(f); |
| 61 | } |
| 62 | |
| 63 | size_t log2Floor(size_t x) { return sizeof(long) * 8 - 1 - __builtin_clzl(x); } |
| 64 | |
| 65 | void usage() { |
| 66 | fprintf(stderr, |
| 67 | format: "usage: compute_size_class_config [-p pageSize] [-c largestClass] " |
| 68 | "[-h headerSize] [-n numClasses] [-b numBits] profile...\n" ); |
| 69 | exit(status: 1); |
| 70 | } |
| 71 | |
| 72 | int main(int argc, char **argv) { |
| 73 | size_t pageSize = 4096; |
| 74 | size_t largestClass = 65552; |
| 75 | size_t = 16; |
| 76 | size_t numClasses = 32; |
| 77 | size_t numBits = 5; |
| 78 | |
| 79 | std::vector<Alloc> allocs; |
| 80 | for (size_t i = 1; i != argc;) { |
| 81 | auto matchArg = [&](size_t &arg, const char *name) { |
| 82 | if (strcmp(s1: argv[i], s2: name) == 0) { |
| 83 | if (i + 1 != argc) { |
| 84 | arg = atoi(nptr: argv[i + 1]); |
| 85 | i += 2; |
| 86 | } else { |
| 87 | usage(); |
| 88 | } |
| 89 | return true; |
| 90 | } |
| 91 | return false; |
| 92 | }; |
| 93 | if (matchArg(pageSize, "-p" ) || matchArg(largestClass, "-c" ) || |
| 94 | matchArg(headerSize, "-h" ) || matchArg(numClasses, "-n" ) || |
| 95 | matchArg(numBits, "-b" )) |
| 96 | continue; |
| 97 | readAllocs(allocs, argv[i]); |
| 98 | ++i; |
| 99 | } |
| 100 | |
| 101 | if (allocs.empty()) |
| 102 | usage(); |
| 103 | |
| 104 | std::vector<size_t> classes; |
| 105 | classes.push_back(largestClass); |
| 106 | for (size_t i = 1; i != numClasses; ++i) { |
| 107 | size_t minWastage = -1ull; |
| 108 | size_t minWastageClass; |
| 109 | for (size_t newClass = 16; newClass != largestClass; newClass += 16) { |
| 110 | // Skip classes with more than numBits bits, ignoring leading or trailing |
| 111 | // zero bits. |
| 112 | if (__builtin_ctzl(newClass - headerSize) + |
| 113 | __builtin_clzl(newClass - headerSize) < |
| 114 | sizeof(long) * 8 - numBits) |
| 115 | continue; |
| 116 | |
| 117 | classes.push_back(newClass); |
| 118 | size_t newWastage = measureWastage(allocs, classes, pageSize, headerSize); |
| 119 | classes.pop_back(); |
| 120 | if (newWastage < minWastage) { |
| 121 | minWastage = newWastage; |
| 122 | minWastageClass = newClass; |
| 123 | } |
| 124 | } |
| 125 | classes.push_back(minWastageClass); |
| 126 | } |
| 127 | |
| 128 | std::sort(classes.begin(), classes.end()); |
| 129 | size_t minSizeLog = log2Floor(x: headerSize); |
| 130 | size_t midSizeIndex = 0; |
| 131 | while (classes[midSizeIndex + 1] - classes[midSizeIndex] == (1 << minSizeLog)) |
| 132 | midSizeIndex++; |
| 133 | size_t midSizeLog = log2Floor(classes[midSizeIndex] - headerSize); |
| 134 | size_t maxSizeLog = log2Floor(classes.back() - headerSize - 1) + 1; |
| 135 | |
| 136 | printf(R"(// wastage = %zu |
| 137 | |
| 138 | struct MySizeClassConfig { |
| 139 | static const uptr NumBits = %zu; |
| 140 | static const uptr MinSizeLog = %zu; |
| 141 | static const uptr MidSizeLog = %zu; |
| 142 | static const uptr MaxSizeLog = %zu; |
| 143 | static const u16 MaxNumCachedHint = 14; |
| 144 | static const uptr MaxBytesCachedLog = 14; |
| 145 | |
| 146 | static constexpr u32 Classes[] = {)" , |
| 147 | measureWastage(allocs, classes, pageSize, headerSize), numBits, |
| 148 | minSizeLog, midSizeLog, maxSizeLog); |
| 149 | for (size_t i = 0; i != classes.size(); ++i) { |
| 150 | if ((i % 8) == 0) |
| 151 | printf(format: "\n " ); |
| 152 | else |
| 153 | printf(format: " " ); |
| 154 | printf("0x%05zx," , classes[i]); |
| 155 | } |
| 156 | printf(format: R"( |
| 157 | }; |
| 158 | static const uptr SizeDelta = %zu; |
| 159 | }; |
| 160 | )" , |
| 161 | headerSize); |
| 162 | } |
| 163 | |