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 16 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->mapped = false; 26 cd->fqn = NULL; 27 cd->tag = -1; 28 cd->addr = 0; 29 INIT_LIST_HEAD(&cd->fragments); 30 } 31 32 static struct die *create_die(Dwarf_Die *die, enum die_state state) 33 { 34 struct die *cd; 35 36 cd = xmalloc(sizeof(struct die)); 37 init_die(cd); 38 cd->addr = (uintptr_t)die->addr; 39 40 hash_add(die_map, &cd->hash, die_hash(cd->addr, state)); 41 return cd; 42 } 43 44 int __die_map_get(uintptr_t addr, enum die_state state, struct die **res) 45 { 46 struct die *cd; 47 48 hash_for_each_possible(die_map, cd, hash, die_hash(addr, state)) { 49 if (cd->addr == addr && cd->state == state) { 50 *res = cd; 51 return 0; 52 } 53 } 54 55 return -1; 56 } 57 58 struct die *die_map_get(Dwarf_Die *die, enum die_state state) 59 { 60 struct die *cd; 61 62 if (__die_map_get((uintptr_t)die->addr, state, &cd) == 0) { 63 map_hits++; 64 return cd; 65 } 66 67 map_misses++; 68 return create_die(die, state); 69 } 70 71 static void reset_die(struct die *cd) 72 { 73 struct die_fragment *tmp; 74 struct die_fragment *df; 75 76 list_for_each_entry_safe(df, tmp, &cd->fragments, list) { 77 if (df->type == FRAGMENT_STRING) 78 free(df->data.str); 79 free(df); 80 } 81 82 if (cd->fqn && *cd->fqn) 83 free(cd->fqn); 84 init_die(cd); 85 } 86 87 void die_map_for_each(die_map_callback_t func, void *arg) 88 { 89 struct hlist_node *tmp; 90 struct die *cd; 91 92 hash_for_each_safe(die_map, cd, tmp, hash) { 93 func(cd, arg); 94 } 95 } 96 97 void die_map_free(void) 98 { 99 struct hlist_node *tmp; 100 unsigned int stats[DIE_LAST + 1]; 101 struct die *cd; 102 int i; 103 104 memset(stats, 0, sizeof(stats)); 105 106 hash_for_each_safe(die_map, cd, tmp, hash) { 107 stats[cd->state]++; 108 reset_die(cd); 109 free(cd); 110 } 111 hash_init(die_map); 112 113 if (map_hits + map_misses > 0) 114 debug("hits %u, misses %u (hit rate %.02f%%)", map_hits, 115 map_misses, 116 (100.0f * map_hits) / (map_hits + map_misses)); 117 118 for (i = 0; i <= DIE_LAST; i++) 119 debug("%s: %u entries", die_state_name(i), stats[i]); 120 } 121 122 static struct die_fragment *append_item(struct die *cd) 123 { 124 struct die_fragment *df; 125 126 df = xmalloc(sizeof(struct die_fragment)); 127 df->type = FRAGMENT_EMPTY; 128 list_add_tail(&df->list, &cd->fragments); 129 return df; 130 } 131 132 void die_map_add_string(struct die *cd, const char *str) 133 { 134 struct die_fragment *df; 135 136 if (!cd) 137 return; 138 139 df = append_item(cd); 140 df->data.str = xstrdup(str); 141 df->type = FRAGMENT_STRING; 142 } 143 144 void die_map_add_linebreak(struct die *cd, int linebreak) 145 { 146 struct die_fragment *df; 147 148 if (!cd) 149 return; 150 151 df = append_item(cd); 152 df->data.linebreak = linebreak; 153 df->type = FRAGMENT_LINEBREAK; 154 } 155 156 void die_map_add_die(struct die *cd, struct die *child) 157 { 158 struct die_fragment *df; 159 160 if (!cd) 161 return; 162 163 df = append_item(cd); 164 df->data.addr = child->addr; 165 df->type = FRAGMENT_DIE; 166 } 167