diff options
Diffstat (limited to 'tools/perf/util/symbol.c')
-rw-r--r-- | tools/perf/util/symbol.c | 230 |
1 files changed, 120 insertions, 110 deletions
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index c0c36965fff..a9d758a3b37 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -18,12 +18,9 @@ #include <elf.h> #include <limits.h> +#include <symbol/kallsyms.h> #include <sys/utsname.h> -#ifndef KSYM_NAME_LEN -#define KSYM_NAME_LEN 256 -#endif - static int dso__load_kernel_sym(struct dso *dso, struct map *map, symbol_filter_t filter); static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, @@ -446,62 +443,6 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso, return ret; } -int kallsyms__parse(const char *filename, void *arg, - int (*process_symbol)(void *arg, const char *name, - char type, u64 start)) -{ - char *line = NULL; - size_t n; - int err = -1; - FILE *file = fopen(filename, "r"); - - if (file == NULL) - goto out_failure; - - err = 0; - - while (!feof(file)) { - u64 start; - int line_len, len; - char symbol_type; - char *symbol_name; - - line_len = getline(&line, &n, file); - if (line_len < 0 || !line) - break; - - line[--line_len] = '\0'; /* \n */ - - len = hex2u64(line, &start); - - len++; - if (len + 2 >= line_len) - continue; - - symbol_type = line[len]; - len += 2; - symbol_name = line + len; - len = line_len - len; - - if (len >= KSYM_NAME_LEN) { - err = -1; - break; - } - - err = process_symbol(arg, symbol_name, - symbol_type, start); - if (err) - break; - } - - free(line); - fclose(file); - return err; - -out_failure: - return -1; -} - int modules__parse(const char *filename, void *arg, int (*process_module)(void *arg, const char *name, u64 start)) @@ -565,12 +506,34 @@ struct process_kallsyms_args { struct dso *dso; }; -static u8 kallsyms2elf_type(char type) +bool symbol__is_idle(struct symbol *sym) { - if (type == 'W') - return STB_WEAK; + const char * const idle_symbols[] = { + "cpu_idle", + "intel_idle", + "default_idle", + "native_safe_halt", + "enter_idle", + "exit_idle", + "mwait_idle", + "mwait_idle_with_hints", + "poll_idle", + "ppc64_runlatch_off", + "pseries_dedicated_idle_sleep", + NULL + }; + + int i; + + if (!sym) + return false; + + for (i = 0; idle_symbols[i]; i++) { + if (!strcmp(idle_symbols[i], sym->name)) + return true; + } - return isupper(type) ? STB_GLOBAL : STB_LOCAL; + return false; } static int map__process_kallsym_symbol(void *arg, const char *name, @@ -664,7 +627,7 @@ static int dso__split_kallsyms_for_kcore(struct dso *dso, struct map *map, * kernel range is broken in several maps, named [kernel].N, as we don't have * the original ELF section names vmlinux have. */ -static int dso__split_kallsyms(struct dso *dso, struct map *map, +static int dso__split_kallsyms(struct dso *dso, struct map *map, u64 delta, symbol_filter_t filter) { struct map_groups *kmaps = map__kmap(map)->kmaps; @@ -729,6 +692,12 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, char dso_name[PATH_MAX]; struct dso *ndso; + if (delta) { + /* Kernel was relocated at boot time */ + pos->start -= delta; + pos->end -= delta; + } + if (count == 0) { curr_map = map; goto filter_symbol; @@ -758,6 +727,10 @@ static int dso__split_kallsyms(struct dso *dso, struct map *map, curr_map->map_ip = curr_map->unmap_ip = identity__map_ip; map_groups__insert(kmaps, curr_map); ++kernel_range; + } else if (delta) { + /* Kernel was relocated at boot time */ + pos->start -= delta; + pos->end -= delta; } filter_symbol: if (filter && filter(curr_map, pos)) { @@ -833,7 +806,7 @@ static void delete_modules(struct rb_root *modules) mi = rb_entry(next, struct module_info, rb_node); next = rb_next(&mi->rb_node); rb_erase(&mi->rb_node, modules); - free(mi->name); + zfree(&mi->name); free(mi); } } @@ -1013,6 +986,23 @@ static int validate_kcore_modules(const char *kallsyms_filename, return 0; } +static int validate_kcore_addresses(const char *kallsyms_filename, + struct map *map) +{ + struct kmap *kmap = map__kmap(map); + + if (kmap->ref_reloc_sym && kmap->ref_reloc_sym->name) { + u64 start; + + start = kallsyms__get_function_start(kallsyms_filename, + kmap->ref_reloc_sym->name); + if (start != kmap->ref_reloc_sym->addr) + return -EINVAL; + } + + return validate_kcore_modules(kallsyms_filename, map); +} + struct kcore_mapfn_data { struct dso *dso; enum map_type type; @@ -1056,8 +1046,8 @@ static int dso__load_kcore(struct dso *dso, struct map *map, kallsyms_filename)) return -EINVAL; - /* All modules must be present at their original addresses */ - if (validate_kcore_modules(kallsyms_filename, map)) + /* Modules and kernel must be present at their original addresses */ + if (validate_kcore_addresses(kallsyms_filename, map)) return -EINVAL; md.dso = dso; @@ -1126,10 +1116,10 @@ static int dso__load_kcore(struct dso *dso, struct map *map, * dso__data_read_addr(). */ if (dso->kernel == DSO_TYPE_GUEST_KERNEL) - dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE; + dso->binary_type = DSO_BINARY_TYPE__GUEST_KCORE; else - dso->data_type = DSO_BINARY_TYPE__KCORE; - dso__set_long_name(dso, strdup(kcore_filename)); + dso->binary_type = DSO_BINARY_TYPE__KCORE; + dso__set_long_name(dso, strdup(kcore_filename), true); close(fd); @@ -1150,15 +1140,41 @@ out_err: return -EINVAL; } +/* + * If the kernel is relocated at boot time, kallsyms won't match. Compute the + * delta based on the relocation reference symbol. + */ +static int kallsyms__delta(struct map *map, const char *filename, u64 *delta) +{ + struct kmap *kmap = map__kmap(map); + u64 addr; + + if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->name) + return 0; + + addr = kallsyms__get_function_start(filename, + kmap->ref_reloc_sym->name); + if (!addr) + return -1; + + *delta = addr - kmap->ref_reloc_sym->addr; + return 0; +} + int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, symbol_filter_t filter) { + u64 delta = 0; + if (symbol__restricted_filename(filename, "/proc/kallsyms")) return -1; if (dso__load_all_kallsyms(dso, filename, map) < 0) return -1; + if (kallsyms__delta(map, filename, &delta)) + return -1; + symbols__fixup_duplicate(&dso->symbols[map->type]); symbols__fixup_end(&dso->symbols[map->type]); @@ -1170,7 +1186,7 @@ int dso__load_kallsyms(struct dso *dso, const char *filename, if (!dso__load_kcore(dso, map, filename)) return dso__split_kallsyms_for_kcore(dso, map, filter); else - return dso__split_kallsyms(dso, map, filter); + return dso__split_kallsyms(dso, map, delta, filter); } static int dso__load_perf_map(struct dso *dso, struct map *map, @@ -1295,8 +1311,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) enum dso_binary_type symtab_type = binary_type_symtab[i]; - if (dso__binary_type_file(dso, symtab_type, - root_dir, name, PATH_MAX)) + if (dso__read_binary_type_filename(dso, symtab_type, + root_dir, name, PATH_MAX)) continue; /* Name is now the name of the next image to try */ @@ -1306,6 +1322,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) if (!syms_ss && symsrc__has_symtab(ss)) { syms_ss = ss; next_slot = true; + if (!dso->symsrc_filename) + dso->symsrc_filename = strdup(name); } if (!runtime_ss && symsrc__possibly_runtime(ss)) { @@ -1376,7 +1394,8 @@ struct map *map_groups__find_by_name(struct map_groups *mg, } int dso__load_vmlinux(struct dso *dso, struct map *map, - const char *vmlinux, symbol_filter_t filter) + const char *vmlinux, bool vmlinux_allocated, + symbol_filter_t filter) { int err = -1; struct symsrc ss; @@ -1402,10 +1421,10 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, if (err > 0) { if (dso->kernel == DSO_TYPE_GUEST_KERNEL) - dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX; + dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX; else - dso->data_type = DSO_BINARY_TYPE__VMLINUX; - dso__set_long_name(dso, (char *)vmlinux); + dso->binary_type = DSO_BINARY_TYPE__VMLINUX; + dso__set_long_name(dso, vmlinux, vmlinux_allocated); dso__set_loaded(dso, map->type); pr_debug("Using %s for symbols\n", symfs_vmlinux); } @@ -1424,21 +1443,16 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, filename = dso__build_id_filename(dso, NULL, 0); if (filename != NULL) { - err = dso__load_vmlinux(dso, map, filename, filter); - if (err > 0) { - dso->lname_alloc = 1; + err = dso__load_vmlinux(dso, map, filename, true, filter); + if (err > 0) goto out; - } free(filename); } for (i = 0; i < vmlinux_path__nr_entries; ++i) { - err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); - if (err > 0) { - dso__set_long_name(dso, strdup(vmlinux_path[i])); - dso->lname_alloc = 1; + err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); + if (err > 0) break; - } } out: return err; @@ -1463,7 +1477,7 @@ static int find_matching_kcore(struct map *map, char *dir, size_t dir_sz) continue; scnprintf(kallsyms_filename, sizeof(kallsyms_filename), "%s/%s/kallsyms", dir, dent->d_name); - if (!validate_kcore_modules(kallsyms_filename, map)) { + if (!validate_kcore_addresses(kallsyms_filename, map)) { strlcpy(dir, kallsyms_filename, dir_sz); ret = 0; break; @@ -1496,14 +1510,15 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map) build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir, + sbuild_id); + /* Use /proc/kallsyms if possible */ if (is_host) { DIR *d; int fd; /* If no cached kcore go with /proc/kallsyms */ - scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", - buildid_dir, sbuild_id); d = opendir(path); if (!d) goto proc_kallsyms; @@ -1517,7 +1532,7 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map) if (fd != -1) { close(fd); /* If module maps match go with /proc/kallsyms */ - if (!validate_kcore_modules("/proc/kallsyms", map)) + if (!validate_kcore_addresses("/proc/kallsyms", map)) goto proc_kallsyms; } @@ -1528,6 +1543,10 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map) goto proc_kallsyms; } + /* Find kallsyms in build-id cache with kcore */ + if (!find_matching_kcore(map, path, sizeof(path))) + return strdup(path); + scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", buildid_dir, sbuild_id); @@ -1570,15 +1589,8 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, } if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { - err = dso__load_vmlinux(dso, map, - symbol_conf.vmlinux_name, filter); - if (err > 0) { - dso__set_long_name(dso, - strdup(symbol_conf.vmlinux_name)); - dso->lname_alloc = 1; - return err; - } - return err; + return dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name, + false, filter); } if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) { @@ -1604,7 +1616,7 @@ do_kallsyms: free(kallsyms_allocated_filename); if (err > 0 && !dso__is_kcore(dso)) { - dso__set_long_name(dso, strdup("[kernel.kallsyms]")); + dso__set_long_name(dso, "[kernel.kallsyms]", false); map__fixup_start(map); map__fixup_end(map); } @@ -1634,7 +1646,8 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, */ if (symbol_conf.default_guest_vmlinux_name != NULL) { err = dso__load_vmlinux(dso, map, - symbol_conf.default_guest_vmlinux_name, filter); + symbol_conf.default_guest_vmlinux_name, + false, filter); return err; } @@ -1651,7 +1664,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, pr_debug("Using %s for symbols\n", kallsyms_filename); if (err > 0 && !dso__is_kcore(dso)) { machine__mmap_name(machine, path, sizeof(path)); - dso__set_long_name(dso, strdup(path)); + dso__set_long_name(dso, strdup(path), true); map__fixup_start(map); map__fixup_end(map); } @@ -1661,13 +1674,10 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, static void vmlinux_path__exit(void) { - while (--vmlinux_path__nr_entries >= 0) { - free(vmlinux_path[vmlinux_path__nr_entries]); - vmlinux_path[vmlinux_path__nr_entries] = NULL; - } + while (--vmlinux_path__nr_entries >= 0) + zfree(&vmlinux_path[vmlinux_path__nr_entries]); - free(vmlinux_path); - vmlinux_path = NULL; + zfree(&vmlinux_path); } static int vmlinux_path__init(void) @@ -1719,7 +1729,7 @@ out_fail: return -1; } -static int setup_list(struct strlist **list, const char *list_str, +int setup_list(struct strlist **list, const char *list_str, const char *list_name) { if (list_str == NULL) |