diff options
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 153 |
1 files changed, 118 insertions, 35 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 469c026..40eeaf0 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -74,16 +74,104 @@ static void dso__set_sorted_by_name(struct dso *dso, enum map_type type) bool symbol_type__is_a(char symbol_type, enum map_type map_type) { + symbol_type = toupper(symbol_type); + switch (map_type) { case MAP__FUNCTION: return symbol_type == 'T' || symbol_type == 'W'; case MAP__VARIABLE: - return symbol_type == 'D' || symbol_type == 'd'; + return symbol_type == 'D'; default: return false; } } +static int prefix_underscores_count(const char *str) +{ + const char *tail = str; + + while (*tail == '_') + tail++; + + return tail - str; +} + +#define SYMBOL_A 0 +#define SYMBOL_B 1 + +static int choose_best_symbol(struct symbol *syma, struct symbol *symb) +{ + s64 a; + s64 b; + + /* Prefer a symbol with non zero length */ + a = syma->end - syma->start; + b = symb->end - symb->start; + if ((b == 0) && (a > 0)) + return SYMBOL_A; + else if ((a == 0) && (b > 0)) + return SYMBOL_B; + + /* Prefer a non weak symbol over a weak one */ + a = syma->binding == STB_WEAK; + b = symb->binding == STB_WEAK; + if (b && !a) + return SYMBOL_A; + if (a && !b) + return SYMBOL_B; + + /* Prefer a global symbol over a non global one */ + a = syma->binding == STB_GLOBAL; + b = symb->binding == STB_GLOBAL; + if (a && !b) + return SYMBOL_A; + if (b && !a) + return SYMBOL_B; + + /* Prefer a symbol with less underscores */ + a = prefix_underscores_count(syma->name); + b = prefix_underscores_count(symb->name); + if (b > a) + return SYMBOL_A; + else if (a > b) + return SYMBOL_B; + + /* If all else fails, choose the symbol with the longest name */ + if (strlen(syma->name) >= strlen(symb->name)) + return SYMBOL_A; + else + return SYMBOL_B; +} + +static void symbols__fixup_duplicate(struct rb_root *symbols) +{ + struct rb_node *nd; + struct symbol *curr, *next; + + nd = rb_first(symbols); + + while (nd) { + curr = rb_entry(nd, struct symbol, rb_node); +again: + nd = rb_next(&curr->rb_node); + next = rb_entry(nd, struct symbol, rb_node); + + if (!nd) + break; + + if (curr->start != next->start) + continue; + + if (choose_best_symbol(curr, next) == SYMBOL_A) { + rb_erase(&next->rb_node, symbols); + goto again; + } else { + nd = rb_next(&curr->rb_node); + rb_erase(&curr->rb_node, symbols); + } + } +} + static void symbols__fixup_end(struct rb_root *symbols) { struct rb_node *nd, *prevnd = rb_first(symbols); @@ -438,18 +526,11 @@ int kallsyms__parse(const char *filename, void *arg, char *line = NULL; size_t n; int err = -1; - u64 prev_start = 0; - char prev_symbol_type = 0; - char *prev_symbol_name; FILE *file = fopen(filename, "r"); if (file == NULL) goto out_failure; - prev_symbol_name = malloc(KSYM_NAME_LEN); - if (prev_symbol_name == NULL) - goto out_close; - err = 0; while (!feof(file)) { @@ -470,7 +551,7 @@ int kallsyms__parse(const char *filename, void *arg, if (len + 2 >= line_len) continue; - symbol_type = toupper(line[len]); + symbol_type = line[len]; len += 2; symbol_name = line + len; len = line_len - len; @@ -480,24 +561,18 @@ int kallsyms__parse(const char *filename, void *arg, break; } - if (prev_symbol_type) { - u64 end = start; - if (end != prev_start) - --end; - err = process_symbol(arg, prev_symbol_name, - prev_symbol_type, prev_start, end); - if (err) - break; - } - - memcpy(prev_symbol_name, symbol_name, len + 1); - prev_symbol_type = symbol_type; - prev_start = start; + /* + * module symbols are not sorted so we add all + * symbols with zero length and rely on + * symbols__fixup_end() to fix it up. + */ + err = process_symbol(arg, symbol_name, + symbol_type, start, start); + if (err) + break; } - free(prev_symbol_name); free(line); -out_close: fclose(file); return err; @@ -703,6 +778,9 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, if (dso__load_all_kallsyms(dso, filename, map) < 0) return -1; + symbols__fixup_duplicate(&dso->symbols[map->type]); + symbols__fixup_end(&dso->symbols[map->type]); + if (dso->kernel == DSO_TYPE_GUEST_KERNEL) dso->symtab_type = SYMTAB__GUEST_KALLSYMS; else @@ -1092,8 +1170,7 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, if (dso->has_build_id) { u8 build_id[BUILD_ID_SIZE]; - if (elf_read_build_id(elf, build_id, - BUILD_ID_SIZE) != BUILD_ID_SIZE) + if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0) goto out_elf_end; if (!dso__build_id_equal(dso, build_id)) @@ -1111,6 +1188,8 @@ static int dso__load_sym(struct dso *dso, struct map *map, const char *name, } opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); + if (opdshdr.sh_type != SHT_PROGBITS) + opdsec = NULL; if (opdsec) opddata = elf_rawdata(opdsec, NULL); @@ -1276,6 +1355,7 @@ new_symbol: * For misannotated, zeroed, ASM function sizes. */ if (nr > 0) { + symbols__fixup_duplicate(&dso->symbols[map->type]); symbols__fixup_end(&dso->symbols[map->type]); if (kmap) { /* @@ -1362,8 +1442,8 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) ptr = data->d_buf; while (ptr < (data->d_buf + data->d_size)) { GElf_Nhdr *nhdr = ptr; - int namesz = NOTE_ALIGN(nhdr->n_namesz), - descsz = NOTE_ALIGN(nhdr->n_descsz); + size_t namesz = NOTE_ALIGN(nhdr->n_namesz), + descsz = NOTE_ALIGN(nhdr->n_descsz); const char *name; ptr += sizeof(*nhdr); @@ -1372,8 +1452,10 @@ static int elf_read_build_id(Elf *elf, void *bf, size_t size) if (nhdr->n_type == NT_GNU_BUILD_ID && nhdr->n_namesz == sizeof("GNU")) { if (memcmp(name, "GNU", sizeof("GNU")) == 0) { - memcpy(bf, ptr, BUILD_ID_SIZE); - err = BUILD_ID_SIZE; + size_t sz = min(size, descsz); + memcpy(bf, ptr, sz); + memset(bf + sz, 0, size - sz); + err = descsz; break; } } @@ -1425,7 +1507,7 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) while (1) { char bf[BUFSIZ]; GElf_Nhdr nhdr; - int namesz, descsz; + size_t namesz, descsz; if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr)) break; @@ -1434,15 +1516,16 @@ int sysfs__read_build_id(const char *filename, void *build_id, size_t size) descsz = NOTE_ALIGN(nhdr.n_descsz); if (nhdr.n_type == NT_GNU_BUILD_ID && nhdr.n_namesz == sizeof("GNU")) { - if (read(fd, bf, namesz) != namesz) + if (read(fd, bf, namesz) != (ssize_t)namesz) break; if (memcmp(bf, "GNU", sizeof("GNU")) == 0) { - if (read(fd, build_id, - BUILD_ID_SIZE) == BUILD_ID_SIZE) { + size_t sz = min(descsz, size); + if (read(fd, build_id, sz) == (ssize_t)sz) { + memset(build_id + sz, 0, size - sz); err = 0; break; } - } else if (read(fd, bf, descsz) != descsz) + } else if (read(fd, bf, descsz) != (ssize_t)descsz) break; } else { int n = namesz + descsz; |