Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

gendwarfksyms: Add a cache for processed DIEs

Basic types in DWARF repeat frequently and traversing the DIEs using
libdw is relatively slow. Add a simple hashtable based cache for the
processed DIEs.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

authored by

Sami Tolvanen and committed by
Masahiro Yamada
0c1c7627 5b7780e8

+308 -41
+1
scripts/gendwarfksyms/Makefile
··· 2 2 hostprogs-always-y += gendwarfksyms 3 3 4 4 gendwarfksyms-objs += gendwarfksyms.o 5 + gendwarfksyms-objs += die.o 5 6 gendwarfksyms-objs += dwarf.o 6 7 gendwarfksyms-objs += symbols.o 7 8
+143
scripts/gendwarfksyms/die.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + */ 5 + 6 + #include <string.h> 7 + #include "gendwarfksyms.h" 8 + 9 + #define DIE_HASH_BITS 15 10 + 11 + /* {die->addr, state} -> struct die * */ 12 + static HASHTABLE_DEFINE(die_map, 1 << DIE_HASH_BITS); 13 + 14 + static unsigned int map_hits; 15 + static unsigned int map_misses; 16 + 17 + static inline unsigned int die_hash(uintptr_t addr, enum die_state state) 18 + { 19 + return hash_32(addr_hash(addr) ^ (unsigned int)state); 20 + } 21 + 22 + static void init_die(struct die *cd) 23 + { 24 + cd->state = DIE_INCOMPLETE; 25 + cd->fqn = NULL; 26 + cd->tag = -1; 27 + cd->addr = 0; 28 + INIT_LIST_HEAD(&cd->fragments); 29 + } 30 + 31 + static struct die *create_die(Dwarf_Die *die, enum die_state state) 32 + { 33 + struct die *cd; 34 + 35 + cd = xmalloc(sizeof(struct die)); 36 + init_die(cd); 37 + cd->addr = (uintptr_t)die->addr; 38 + 39 + hash_add(die_map, &cd->hash, die_hash(cd->addr, state)); 40 + return cd; 41 + } 42 + 43 + int __die_map_get(uintptr_t addr, enum die_state state, struct die **res) 44 + { 45 + struct die *cd; 46 + 47 + hash_for_each_possible(die_map, cd, hash, die_hash(addr, state)) { 48 + if (cd->addr == addr && cd->state == state) { 49 + *res = cd; 50 + return 0; 51 + } 52 + } 53 + 54 + return -1; 55 + } 56 + 57 + struct die *die_map_get(Dwarf_Die *die, enum die_state state) 58 + { 59 + struct die *cd; 60 + 61 + if (__die_map_get((uintptr_t)die->addr, state, &cd) == 0) { 62 + map_hits++; 63 + return cd; 64 + } 65 + 66 + map_misses++; 67 + return create_die(die, state); 68 + } 69 + 70 + static void reset_die(struct die *cd) 71 + { 72 + struct die_fragment *tmp; 73 + struct die_fragment *df; 74 + 75 + list_for_each_entry_safe(df, tmp, &cd->fragments, list) { 76 + if (df->type == FRAGMENT_STRING) 77 + free(df->data.str); 78 + free(df); 79 + } 80 + 81 + if (cd->fqn && *cd->fqn) 82 + free(cd->fqn); 83 + init_die(cd); 84 + } 85 + 86 + void die_map_free(void) 87 + { 88 + struct hlist_node *tmp; 89 + unsigned int stats[DIE_LAST + 1]; 90 + struct die *cd; 91 + int i; 92 + 93 + memset(stats, 0, sizeof(stats)); 94 + 95 + hash_for_each_safe(die_map, cd, tmp, hash) { 96 + stats[cd->state]++; 97 + reset_die(cd); 98 + free(cd); 99 + } 100 + hash_init(die_map); 101 + 102 + if (map_hits + map_misses > 0) 103 + debug("hits %u, misses %u (hit rate %.02f%%)", map_hits, 104 + map_misses, 105 + (100.0f * map_hits) / (map_hits + map_misses)); 106 + 107 + for (i = 0; i <= DIE_LAST; i++) 108 + debug("%s: %u entries", die_state_name(i), stats[i]); 109 + } 110 + 111 + static struct die_fragment *append_item(struct die *cd) 112 + { 113 + struct die_fragment *df; 114 + 115 + df = xmalloc(sizeof(struct die_fragment)); 116 + df->type = FRAGMENT_EMPTY; 117 + list_add_tail(&df->list, &cd->fragments); 118 + return df; 119 + } 120 + 121 + void die_map_add_string(struct die *cd, const char *str) 122 + { 123 + struct die_fragment *df; 124 + 125 + if (!cd) 126 + return; 127 + 128 + df = append_item(cd); 129 + df->data.str = xstrdup(str); 130 + df->type = FRAGMENT_STRING; 131 + } 132 + 133 + void die_map_add_die(struct die *cd, struct die *child) 134 + { 135 + struct die_fragment *df; 136 + 137 + if (!cd) 138 + return; 139 + 140 + df = append_item(cd); 141 + df->data.addr = child->addr; 142 + df->type = FRAGMENT_DIE; 143 + }
+98 -38
scripts/gendwarfksyms/dwarf.c
··· 72 72 /* 73 73 * Type string processing 74 74 */ 75 - static void process(const char *s) 75 + static void process(struct die *cache, const char *s) 76 76 { 77 77 s = s ?: "<null>"; 78 78 79 79 if (dump_dies) 80 80 fputs(s, stderr); 81 + 82 + die_map_add_string(cache, s); 81 83 } 82 84 83 85 #define MAX_FMT_BUFFER_SIZE 128 84 86 85 - static void process_fmt(const char *fmt, ...) 87 + static void process_fmt(struct die *cache, const char *fmt, ...) 86 88 { 87 89 char buf[MAX_FMT_BUFFER_SIZE]; 88 90 va_list args; ··· 94 92 if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf)) 95 93 error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE"); 96 94 97 - process(buf); 95 + process(cache, buf); 98 96 va_end(args); 99 97 } 100 98 ··· 167 165 return fqn; 168 166 } 169 167 170 - static void process_fqn(Dwarf_Die *die) 168 + static void update_fqn(struct die *cache, Dwarf_Die *die) 171 169 { 172 - process(" "); 173 - process(get_fqn(die) ?: ""); 170 + if (!cache->fqn) 171 + cache->fqn = get_fqn(die) ?: ""; 174 172 } 175 173 176 - #define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute) \ 177 - static void process_##attribute##_attr(Dwarf_Die *die) \ 178 - { \ 179 - Dwarf_Word value; \ 180 - if (get_udata_attr(die, DW_AT_##attribute, &value)) \ 181 - process_fmt(" " #attribute "(%" PRIu64 ")", value); \ 174 + static void process_fqn(struct die *cache, Dwarf_Die *die) 175 + { 176 + update_fqn(cache, die); 177 + if (*cache->fqn) 178 + process(cache, " "); 179 + process(cache, cache->fqn); 180 + } 181 + 182 + #define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute) \ 183 + static void process_##attribute##_attr(struct die *cache, \ 184 + Dwarf_Die *die) \ 185 + { \ 186 + Dwarf_Word value; \ 187 + if (get_udata_attr(die, DW_AT_##attribute, &value)) \ 188 + process_fmt(cache, " " #attribute "(%" PRIu64 ")", \ 189 + value); \ 182 190 } 183 191 184 192 DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment) ··· 200 188 return true; 201 189 } 202 190 203 - int process_die_container(struct state *state, Dwarf_Die *die, 204 - die_callback_t func, die_match_callback_t match) 191 + int process_die_container(struct state *state, struct die *cache, 192 + Dwarf_Die *die, die_callback_t func, 193 + die_match_callback_t match) 205 194 { 206 195 Dwarf_Die current; 207 196 int res; ··· 211 198 while (!res) { 212 199 if (match(&current)) { 213 200 /* <0 = error, 0 = continue, >0 = stop */ 214 - res = checkp(func(state, &current)); 201 + res = checkp(func(state, cache, &current)); 215 202 if (res) 216 203 return res; 217 204 } ··· 222 209 return 0; 223 210 } 224 211 225 - static int process_type(struct state *state, Dwarf_Die *die); 212 + static int process_type(struct state *state, struct die *parent, 213 + Dwarf_Die *die); 226 214 227 - static void process_type_attr(struct state *state, Dwarf_Die *die) 215 + static void process_type_attr(struct state *state, struct die *cache, 216 + Dwarf_Die *die) 228 217 { 229 218 Dwarf_Die type; 230 219 231 220 if (get_ref_die_attr(die, DW_AT_type, &type)) { 232 - check(process_type(state, &type)); 221 + check(process_type(state, cache, &type)); 233 222 return; 234 223 } 235 224 236 225 /* Compilers can omit DW_AT_type -- print out 'void' to clarify */ 237 - process("base_type void"); 226 + process(cache, "base_type void"); 238 227 } 239 228 240 - static void process_base_type(struct state *state, Dwarf_Die *die) 229 + static void process_base_type(struct state *state, struct die *cache, 230 + Dwarf_Die *die) 241 231 { 242 - process("base_type"); 243 - process_fqn(die); 244 - process_byte_size_attr(die); 245 - process_encoding_attr(die); 246 - process_alignment_attr(die); 232 + process(cache, "base_type"); 233 + process_fqn(cache, die); 234 + process_byte_size_attr(cache, die); 235 + process_encoding_attr(cache, die); 236 + process_alignment_attr(cache, die); 247 237 } 248 238 249 - #define PROCESS_TYPE(type) \ 250 - case DW_TAG_##type##_type: \ 251 - process_##type##_type(state, die); \ 239 + static void process_cached(struct state *state, struct die *cache, 240 + Dwarf_Die *die) 241 + { 242 + struct die_fragment *df; 243 + Dwarf_Die child; 244 + 245 + list_for_each_entry(df, &cache->fragments, list) { 246 + switch (df->type) { 247 + case FRAGMENT_STRING: 248 + process(NULL, df->data.str); 249 + break; 250 + case FRAGMENT_DIE: 251 + if (!dwarf_die_addr_die(dwarf_cu_getdwarf(die->cu), 252 + (void *)df->data.addr, &child)) 253 + error("dwarf_die_addr_die failed"); 254 + check(process_type(state, NULL, &child)); 255 + break; 256 + default: 257 + error("empty die_fragment"); 258 + } 259 + } 260 + } 261 + 262 + #define PROCESS_TYPE(type) \ 263 + case DW_TAG_##type##_type: \ 264 + process_##type##_type(state, cache, die); \ 252 265 break; 253 266 254 - static int process_type(struct state *state, Dwarf_Die *die) 267 + static int process_type(struct state *state, struct die *parent, Dwarf_Die *die) 255 268 { 269 + struct die *cache; 256 270 int tag = dwarf_tag(die); 271 + 272 + /* 273 + * If we have the DIE already cached, use it instead of walking 274 + * through DWARF. 275 + */ 276 + cache = die_map_get(die, DIE_COMPLETE); 277 + 278 + if (cache->state == DIE_COMPLETE) { 279 + process_cached(state, cache, die); 280 + die_map_add_die(parent, cache); 281 + return 0; 282 + } 257 283 258 284 switch (tag) { 259 285 PROCESS_TYPE(base) ··· 300 248 debug("unimplemented type: %x", tag); 301 249 break; 302 250 } 251 + 252 + /* Update cache state and append to the parent (if any) */ 253 + cache->tag = tag; 254 + cache->state = DIE_COMPLETE; 255 + die_map_add_die(parent, cache); 303 256 304 257 return 0; 305 258 } ··· 316 259 die_callback_t process_func) 317 260 { 318 261 debug("%s", state->sym->name); 319 - check(process_func(state, die)); 262 + check(process_func(state, NULL, die)); 320 263 if (dump_dies) 321 264 fputs("\n", stderr); 322 265 } 323 266 324 - static int __process_subprogram(struct state *state, Dwarf_Die *die) 267 + static int __process_subprogram(struct state *state, struct die *cache, 268 + Dwarf_Die *die) 325 269 { 326 - process("subprogram"); 270 + process(cache, "subprogram"); 327 271 return 0; 328 272 } 329 273 ··· 333 275 process_symbol(state, die, __process_subprogram); 334 276 } 335 277 336 - static int __process_variable(struct state *state, Dwarf_Die *die) 278 + static int __process_variable(struct state *state, struct die *cache, 279 + Dwarf_Die *die) 337 280 { 338 - process("variable "); 339 - process_type_attr(state, die); 281 + process(cache, "variable "); 282 + process_type_attr(state, cache, die); 340 283 return 0; 341 284 } 342 285 ··· 346 287 process_symbol(state, die, __process_variable); 347 288 } 348 289 349 - static int process_exported_symbols(struct state *unused, Dwarf_Die *die) 290 + static int process_exported_symbols(struct state *unused, struct die *cache, 291 + Dwarf_Die *die) 350 292 { 351 293 int tag = dwarf_tag(die); 352 294 ··· 357 297 case DW_TAG_class_type: 358 298 case DW_TAG_structure_type: 359 299 return check(process_die_container( 360 - NULL, die, process_exported_symbols, match_all)); 300 + NULL, cache, die, process_exported_symbols, match_all)); 361 301 362 302 /* Possible exported symbols */ 363 303 case DW_TAG_subprogram: ··· 381 321 382 322 void process_cu(Dwarf_Die *cudie) 383 323 { 384 - check(process_die_container(NULL, cudie, process_exported_symbols, 324 + check(process_die_container(NULL, NULL, cudie, process_exported_symbols, 385 325 match_all)); 386 326 }
+6
scripts/gendwarfksyms/gendwarfksyms.c
··· 43 43 debug("%s", name); 44 44 dbg = dwfl_module_getdwarf(mod, &dwbias); 45 45 46 + /* 47 + * Look for exported symbols in each CU, follow the DIE tree, and add 48 + * the entries to die_map. 49 + */ 46 50 do { 47 51 res = dwarf_get_units(dbg, cu, &cu, NULL, NULL, &cudie, NULL); 48 52 if (res < 0) ··· 56 52 57 53 process_cu(&cudie); 58 54 } while (cu); 55 + 56 + die_map_free(); 59 57 60 58 return DWARF_CB_OK; 61 59 }
+60 -3
scripts/gendwarfksyms/gendwarfksyms.h
··· 88 88 void symbol_free(void); 89 89 90 90 /* 91 + * die.c 92 + */ 93 + 94 + enum die_state { 95 + DIE_INCOMPLETE, 96 + DIE_COMPLETE, 97 + DIE_LAST = DIE_COMPLETE 98 + }; 99 + 100 + enum die_fragment_type { 101 + FRAGMENT_EMPTY, 102 + FRAGMENT_STRING, 103 + FRAGMENT_DIE 104 + }; 105 + 106 + struct die_fragment { 107 + enum die_fragment_type type; 108 + union { 109 + char *str; 110 + uintptr_t addr; 111 + } data; 112 + struct list_head list; 113 + }; 114 + 115 + #define CASE_CONST_TO_STR(name) \ 116 + case name: \ 117 + return #name; 118 + 119 + static inline const char *die_state_name(enum die_state state) 120 + { 121 + switch (state) { 122 + CASE_CONST_TO_STR(DIE_INCOMPLETE) 123 + CASE_CONST_TO_STR(DIE_COMPLETE) 124 + } 125 + 126 + error("unexpected die_state: %d", state); 127 + } 128 + 129 + struct die { 130 + enum die_state state; 131 + char *fqn; 132 + int tag; 133 + uintptr_t addr; 134 + struct list_head fragments; 135 + struct hlist_node hash; 136 + }; 137 + 138 + int __die_map_get(uintptr_t addr, enum die_state state, struct die **res); 139 + struct die *die_map_get(Dwarf_Die *die, enum die_state state); 140 + void die_map_add_string(struct die *pd, const char *str); 141 + void die_map_add_linebreak(struct die *pd, int linebreak); 142 + void die_map_add_die(struct die *pd, struct die *child); 143 + void die_map_free(void); 144 + 145 + /* 91 146 * dwarf.c 92 147 */ 93 148 ··· 151 96 Dwarf_Die die; 152 97 }; 153 98 154 - typedef int (*die_callback_t)(struct state *state, Dwarf_Die *die); 99 + typedef int (*die_callback_t)(struct state *state, struct die *cache, 100 + Dwarf_Die *die); 155 101 typedef bool (*die_match_callback_t)(Dwarf_Die *die); 156 102 bool match_all(Dwarf_Die *die); 157 103 158 - int process_die_container(struct state *state, Dwarf_Die *die, 159 - die_callback_t func, die_match_callback_t match); 104 + int process_die_container(struct state *state, struct die *cache, 105 + Dwarf_Die *die, die_callback_t func, 106 + die_match_callback_t match); 160 107 161 108 void process_cu(Dwarf_Die *cudie); 162 109