diff options
Diffstat (limited to '')
-rw-r--r-- | tools/perf/util/unwind-libunwind-local.c | 238 |
1 files changed, 167 insertions, 71 deletions
diff --git a/tools/perf/util/unwind-libunwind-local.c b/tools/perf/util/unwind-libunwind-local.c index 9aededc0bc06..16c2b03831f3 100644 --- a/tools/perf/util/unwind-libunwind-local.c +++ b/tools/perf/util/unwind-libunwind-local.c @@ -82,7 +82,7 @@ UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, #define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ #define DW_EH_PE_aligned 0x50 /* aligned pointer */ -/* Flags intentionaly not handled, since they're not needed: +/* Flags intentionally not handled, since they're not needed: * #define DW_EH_PE_indirect 0x80 * #define DW_EH_PE_uleb128 0x01 * #define DW_EH_PE_udata2 0x02 @@ -96,6 +96,7 @@ struct unwind_info { struct perf_sample *sample; struct machine *machine; struct thread *thread; + bool best_effort; }; #define dw_read(ptr, type, end) ({ \ @@ -168,29 +169,63 @@ static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, __v; \ }) -static u64 elf_section_offset(int fd, const char *name) +static int elf_section_address_and_offset(int fd, const char *name, u64 *address, u64 *offset) { Elf *elf; GElf_Ehdr ehdr; GElf_Shdr shdr; - u64 offset = 0; + int ret = -1; elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); if (elf == NULL) + return -1; + + if (gelf_getehdr(elf, &ehdr) == NULL) + goto out_err; + + if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) + goto out_err; + + *address = shdr.sh_addr; + *offset = shdr.sh_offset; + ret = 0; +out_err: + elf_end(elf); + return ret; +} + +#ifndef NO_LIBUNWIND_DEBUG_FRAME +static u64 elf_section_offset(int fd, const char *name) +{ + u64 address, offset = 0; + + if (elf_section_address_and_offset(fd, name, &address, &offset)) return 0; - do { - if (gelf_getehdr(elf, &ehdr) == NULL) - break; + return offset; +} +#endif - if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) - break; +static u64 elf_base_address(int fd) +{ + Elf *elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + GElf_Phdr phdr; + u64 retval = 0; + size_t i, phdrnum = 0; - offset = shdr.sh_offset; - } while (0); + if (elf == NULL) + return 0; + (void)elf_getphdrnum(elf, &phdrnum); + /* PT_LOAD segments are sorted by p_vaddr, so the first has the minimum p_vaddr. */ + for (i = 0; i < phdrnum; i++) { + if (gelf_getphdr(elf, i, &phdr) && phdr.p_type == PT_LOAD) { + retval = phdr.p_vaddr & -getpagesize(); + break; + } + } elf_end(elf); - return offset; + return retval; } #ifndef NO_LIBUNWIND_DEBUG_FRAME @@ -247,8 +282,7 @@ struct eh_frame_hdr { } __packed; static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, - u64 offset, u64 *table_data, u64 *segbase, - u64 *fde_count) + u64 offset, u64 *table_data_offset, u64 *fde_count) { struct eh_frame_hdr hdr; u8 *enc = (u8 *) &hdr.enc; @@ -264,35 +298,64 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc); - *segbase = offset; - *table_data = (enc - (u8 *) &hdr) + offset; + *table_data_offset = enc - (u8 *) &hdr; return 0; } -static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, +struct read_unwind_spec_eh_frame_maps_cb_args { + struct dso *dso; + u64 base_addr; +}; + +static int read_unwind_spec_eh_frame_maps_cb(struct map *map, void *data) +{ + + struct read_unwind_spec_eh_frame_maps_cb_args *args = data; + + if (map__dso(map) == args->dso && map__start(map) - map__pgoff(map) < args->base_addr) + args->base_addr = map__start(map) - map__pgoff(map); + + return 0; +} + + +static int read_unwind_spec_eh_frame(struct dso *dso, struct unwind_info *ui, u64 *table_data, u64 *segbase, u64 *fde_count) { - int ret = -EINVAL, fd; - u64 offset = dso->data.eh_frame_hdr_offset; + struct read_unwind_spec_eh_frame_maps_cb_args args = { + .dso = dso, + .base_addr = UINT64_MAX, + }; + int ret, fd; - if (offset == 0) { - fd = dso__data_get_fd(dso, machine); + if (dso__data(dso)->eh_frame_hdr_offset == 0) { + fd = dso__data_get_fd(dso, ui->machine); if (fd < 0) return -EINVAL; /* Check the .eh_frame section for unwinding info */ - offset = elf_section_offset(fd, ".eh_frame_hdr"); - dso->data.eh_frame_hdr_offset = offset; + ret = elf_section_address_and_offset(fd, ".eh_frame_hdr", + &dso__data(dso)->eh_frame_hdr_addr, + &dso__data(dso)->eh_frame_hdr_offset); + dso__data(dso)->elf_base_addr = elf_base_address(fd); dso__data_put_fd(dso); + if (ret || dso__data(dso)->eh_frame_hdr_offset == 0) + return -EINVAL; } - if (offset) - ret = unwind_spec_ehframe(dso, machine, offset, - table_data, segbase, - fde_count); + maps__for_each_map(thread__maps(ui->thread), read_unwind_spec_eh_frame_maps_cb, &args); - return ret; + args.base_addr -= dso__data(dso)->elf_base_addr; + /* Address of .eh_frame_hdr */ + *segbase = args.base_addr + dso__data(dso)->eh_frame_hdr_addr; + ret = unwind_spec_ehframe(dso, ui->machine, dso__data(dso)->eh_frame_hdr_offset, + table_data, fde_count); + if (ret) + return ret; + /* binary_search_table offset plus .eh_frame_hdr address */ + *table_data += *segbase; + return 0; } #ifndef NO_LIBUNWIND_DEBUG_FRAME @@ -300,7 +363,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, struct machine *machine, u64 *offset) { int fd; - u64 ofs = dso->data.debug_frame_offset; + u64 ofs = dso__data(dso)->debug_frame_offset; /* debug_frame can reside in: * - dso @@ -316,7 +379,7 @@ static int read_unwind_spec_debug_frame(struct dso *dso, } if (ofs <= 0) { - fd = open(dso->symsrc_filename, O_RDONLY); + fd = open(dso__symsrc_filename(dso), O_RDONLY); if (fd >= 0) { ofs = elf_section_offset(fd, ".debug_frame"); close(fd); @@ -327,6 +390,11 @@ static int read_unwind_spec_debug_frame(struct dso *dso, char *debuglink = malloc(PATH_MAX); int ret = 0; + if (debuglink == NULL) { + pr_err("unwind: Can't read unwind spec debug frame.\n"); + return -ENOMEM; + } + ret = dso__read_binary_type_filename( dso, DSO_BINARY_TYPE__DEBUGLINK, machine->root_dir, debuglink, PATH_MAX); @@ -339,21 +407,21 @@ static int read_unwind_spec_debug_frame(struct dso *dso, } } if (ofs > 0) { - if (dso->symsrc_filename != NULL) { + if (dso__symsrc_filename(dso) != NULL) { pr_warning( "%s: overwrite symsrc(%s,%s)\n", __func__, - dso->symsrc_filename, + dso__symsrc_filename(dso), debuglink); - zfree(&dso->symsrc_filename); + dso__free_symsrc_filename(dso); } - dso->symsrc_filename = debuglink; + dso__set_symsrc_filename(dso, debuglink); } else { free(debuglink); } } - dso->data.debug_frame_offset = ofs; + dso__data(dso)->debug_frame_offset = ofs; } *offset = ofs; @@ -367,7 +435,13 @@ static int read_unwind_spec_debug_frame(struct dso *dso, static struct map *find_map(unw_word_t ip, struct unwind_info *ui) { struct addr_location al; - return thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); + struct map *ret; + + addr_location__init(&al); + thread__find_map(ui->thread, PERF_RECORD_MISC_USER, ip, &al); + ret = map__get(al.map); + addr_location__exit(&al); + return ret; } static int @@ -376,25 +450,31 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, { struct unwind_info *ui = arg; struct map *map; + struct dso *dso; unw_dyn_info_t di; u64 table_data, segbase, fde_count; int ret = -EINVAL; map = find_map(ip, ui); - if (!map || !map->dso) + if (!map) return -EINVAL; - pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); + dso = map__dso(map); + if (!dso) { + map__put(map); + return -EINVAL; + } + + pr_debug("unwind: find_proc_info dso %s\n", dso__name(dso)); /* Check the .eh_frame section for unwinding info */ - if (!read_unwind_spec_eh_frame(map->dso, ui->machine, - &table_data, &segbase, &fde_count)) { + if (!read_unwind_spec_eh_frame(dso, ui, &table_data, &segbase, &fde_count)) { memset(&di, 0, sizeof(di)); di.format = UNW_INFO_FORMAT_REMOTE_TABLE; - di.start_ip = map->start; - di.end_ip = map->end; - di.u.rti.segbase = map->start + segbase - map->pgoff; - di.u.rti.table_data = map->start + table_data - map->pgoff; + di.start_ip = map__start(map); + di.end_ip = map__end(map); + di.u.rti.segbase = segbase; + di.u.rti.table_data = table_data; di.u.rti.table_len = fde_count * sizeof(struct table_entry) / sizeof(unw_word_t); ret = dwarf_search_unwind_table(as, ip, &di, pi, @@ -404,25 +484,25 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, #ifndef NO_LIBUNWIND_DEBUG_FRAME /* Check the .debug_frame section for unwinding info */ if (ret < 0 && - !read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { - int fd = dso__data_get_fd(map->dso, ui->machine); - int is_exec = elf_is_exec(fd, map->dso->name); - unw_word_t base = is_exec ? 0 : map->start; + !read_unwind_spec_debug_frame(dso, ui->machine, &segbase)) { + int fd = dso__data_get_fd(dso, ui->machine); + int is_exec = elf_is_exec(fd, dso__name(dso)); + u64 start = map__start(map); + unw_word_t base = is_exec ? 0 : start; const char *symfile; if (fd >= 0) - dso__data_put_fd(map->dso); + dso__data_put_fd(dso); - symfile = map->dso->symsrc_filename ?: map->dso->name; + symfile = dso__symsrc_filename(dso) ?: dso__name(dso); memset(&di, 0, sizeof(di)); - if (dwarf_find_debug_frame(0, &di, ip, base, symfile, - map->start, map->end)) - return dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); + if (dwarf_find_debug_frame(0, &di, ip, base, symfile, start, map__end(map))) + ret = dwarf_search_unwind_table(as, ip, &di, pi, + need_unwind_info, arg); } #endif - + map__put(map); return ret; } @@ -465,6 +545,7 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, unw_word_t *data) { struct map *map; + struct dso *dso; ssize_t size; map = find_map(addr, ui); @@ -473,12 +554,16 @@ static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, return -1; } - if (!map->dso) + dso = map__dso(map); + + if (!dso) { + map__put(map); return -1; + } - size = dso__data_read_addr(map->dso, map, ui->machine, + size = dso__data_read_addr(dso, map, ui->machine, addr, (u8 *) data, sizeof(*data)); - + map__put(map); return !(size == sizeof(*data)); } @@ -487,6 +572,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as, int __write, void *arg) { struct unwind_info *ui = arg; + const char *arch = perf_env__arch(ui->machine->env); struct stack_dump *stack = &ui->sample->user_stack; u64 start, end; int offset; @@ -499,7 +585,7 @@ static int access_mem(unw_addr_space_t __maybe_unused as, } ret = perf_reg_value(&start, &ui->sample->user_regs, - LIBUNWIND__ARCH_REG_SP); + perf_arch_reg_sp(arch)); if (ret) return ret; @@ -553,7 +639,8 @@ static int access_reg(unw_addr_space_t __maybe_unused as, ret = perf_reg_value(&val, &ui->sample->user_regs, id); if (ret) { - pr_err("unwind: can't read reg %d\n", regnum); + if (!ui->best_effort) + pr_err("unwind: can't read reg %d\n", regnum); return ret; } @@ -574,7 +661,9 @@ static int entry(u64 ip, struct thread *thread, { struct unwind_entry e; struct addr_location al; + int ret; + addr_location__init(&al); e.ms.sym = thread__find_symbol(thread, PERF_RECORD_MISC_USER, ip, &al); e.ip = ip; e.ms.map = al.map; @@ -583,9 +672,11 @@ static int entry(u64 ip, struct thread *thread, pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", al.sym ? al.sym->name : "''", ip, - al.map ? al.map->map_ip(al.map, ip) : (u64) 0); + al.map ? map__map_ip(al.map, ip) : (u64) 0); - return cb(&e, arg); + ret = cb(&e, arg); + addr_location__exit(&al); + return ret; } static void display_error(int err) @@ -618,29 +709,32 @@ static unw_accessors_t accessors = { static int _unwind__prepare_access(struct maps *maps) { - maps->addr_space = unw_create_addr_space(&accessors, 0); - if (!maps->addr_space) { + void *addr_space = unw_create_addr_space(&accessors, 0); + + maps__set_addr_space(maps, addr_space); + if (!addr_space) { pr_err("unwind: Can't create unwind address space.\n"); return -ENOMEM; } - unw_set_caching_policy(maps->addr_space, UNW_CACHE_GLOBAL); + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); return 0; } static void _unwind__flush_access(struct maps *maps) { - unw_flush_cache(maps->addr_space, 0, 0); + unw_flush_cache(maps__addr_space(maps), 0, 0); } static void _unwind__finish_access(struct maps *maps) { - unw_destroy_addr_space(maps->addr_space); + unw_destroy_addr_space(maps__addr_space(maps)); } static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { + const char *arch = perf_env__arch(ui->machine->env); u64 val; unw_word_t ips[max_stack]; unw_addr_space_t addr_space; @@ -648,7 +742,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, int ret, i = 0; ret = perf_reg_value(&val, &ui->sample->user_regs, - LIBUNWIND__ARCH_REG_IP); + perf_arch_reg_ip(arch)); if (ret) return ret; @@ -660,13 +754,13 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, */ if (max_stack - 1 > 0) { WARN_ONCE(!ui->thread, "WARNING: ui->thread is NULL"); - addr_space = ui->thread->maps->addr_space; + addr_space = maps__addr_space(thread__maps(ui->thread)); if (addr_space == NULL) return -1; ret = unw_init_remote(&c, addr_space, ui); - if (ret) + if (ret && !ui->best_effort) display_error(ret); while (!ret && (unw_step(&c) > 0) && i < max_stack) { @@ -704,12 +798,14 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, static int _unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, - struct perf_sample *data, int max_stack) + struct perf_sample *data, int max_stack, + bool best_effort) { struct unwind_info ui = { .sample = data, .thread = thread, - .machine = thread->maps->machine, + .machine = maps__machine(thread__maps(thread)), + .best_effort = best_effort }; if (!data->user_regs.regs) |