diff options
Diffstat (limited to 'tools/perf/util/unwind-libunwind.c')
-rw-r--r-- | tools/perf/util/unwind-libunwind.c | 80 |
1 files changed, 53 insertions, 27 deletions
diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index c83832b555e5..ee7e372297e5 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -319,6 +319,15 @@ static struct map *find_map(unw_word_t ip, struct unwind_info *ui) thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, MAP__FUNCTION, ip, &al); + if (!al.map) { + /* + * We've seen cases (softice) where DWARF unwinder went + * through non executable mmaps, which we need to lookup + * in MAP__VARIABLE tree. + */ + thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, + MAP__VARIABLE, ip, &al); + } return al.map; } @@ -416,20 +425,19 @@ get_proc_name(unw_addr_space_t __maybe_unused as, static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, unw_word_t *data) { - struct addr_location al; + struct map *map; ssize_t size; - thread__find_addr_map(ui->thread, PERF_RECORD_MISC_USER, - MAP__FUNCTION, addr, &al); - if (!al.map) { + map = find_map(addr, ui); + if (!map) { pr_debug("unwind: no map for %lx\n", (unsigned long)addr); return -1; } - if (!al.map->dso) + if (!map->dso) return -1; - size = dso__data_read_addr(al.map->dso, al.map, ui->machine, + size = dso__data_read_addr(map->dso, map, ui->machine, addr, (u8 *) data, sizeof(*data)); return !(size == sizeof(*data)); @@ -614,23 +622,48 @@ void unwind__finish_access(struct thread *thread) static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, void *arg, int max_stack) { + u64 val; + unw_word_t ips[max_stack]; unw_addr_space_t addr_space; unw_cursor_t c; - int ret; - - addr_space = thread__priv(ui->thread); - if (addr_space == NULL) - return -1; + int ret, i = 0; - ret = unw_init_remote(&c, addr_space, ui); + ret = perf_reg_value(&val, &ui->sample->user_regs, PERF_REG_IP); if (ret) - display_error(ret); + return ret; - while (!ret && (unw_step(&c) > 0) && max_stack--) { - unw_word_t ip; + ips[i++] = (unw_word_t) val; - unw_get_reg(&c, UNW_REG_IP, &ip); - ret = ip ? entry(ip, ui->thread, cb, arg) : 0; + /* + * If we need more than one entry, do the DWARF + * unwind itself. + */ + if (max_stack - 1 > 0) { + addr_space = thread__priv(ui->thread); + if (addr_space == NULL) + return -1; + + ret = unw_init_remote(&c, addr_space, ui); + if (ret) + display_error(ret); + + while (!ret && (unw_step(&c) > 0) && i < max_stack) { + unw_get_reg(&c, UNW_REG_IP, &ips[i]); + ++i; + } + + max_stack = i; + } + + /* + * Display what we got based on the order setup. + */ + for (i = 0; i < max_stack && !ret; i++) { + int j = i; + + if (callchain_param.order == ORDER_CALLER) + j = max_stack - i - 1; + ret = ips[j] ? entry(ips[j], ui->thread, cb, arg) : 0; } return ret; @@ -640,24 +673,17 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, struct thread *thread, struct perf_sample *data, int max_stack) { - u64 ip; struct unwind_info ui = { .sample = data, .thread = thread, .machine = thread->mg->machine, }; - int ret; if (!data->user_regs.regs) return -EINVAL; - ret = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); - if (ret) - return ret; - - ret = entry(ip, thread, cb, arg); - if (ret) - return -ENOMEM; + if (max_stack <= 0) + return -EINVAL; - return --max_stack > 0 ? get_entries(&ui, cb, arg, max_stack) : 0; + return get_entries(&ui, cb, arg, max_stack); } |