Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf callchain: Add order support for libdw DWARF unwinder

As reported by Milian, currently for DWARF unwind (both libdw and
libunwind) we display callchain in callee order only.

Adding the support to follow callchain order setup to libdw DWARF
unwinder, so we could get following output for report:

$ perf record --call-graph dwarf ls
...

$ perf report --no-children --stdio

21.12% ls libc-2.21.so [.] __strcoll_l
|
---__strcoll_l
mpsort_with_tmp
mpsort_with_tmp
mpsort_with_tmp
sort_files
main
__libc_start_main
_start

$ perf report --stdio --no-children -g caller

21.12% ls libc-2.21.so [.] __strcoll_l
|
---_start
__libc_start_main
main
sort_files
mpsort_with_tmp
mpsort_with_tmp
mpsort_with_tmp
__strcoll_l

Reported-and-Tested-by: Milian Wolff <milian.wolff@kdab.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Tested-by: Wang Nan <wangnan0@huawei.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jan Kratochvil <jkratoch@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20151119130119.GA26617@krava.brq.redhat.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Jiri Olsa and committed by
Arnaldo Carvalho de Melo
8bd508b0 8dc0564d

+40 -15
+38 -15
tools/perf/util/unwind-libdw.c
··· 11 11 #include <linux/types.h> 12 12 #include "event.h" 13 13 #include "perf_regs.h" 14 + #include "callchain.h" 14 15 15 16 static char *debuginfo_path; 16 17 ··· 53 52 return __report_module(&al, ip, ui); 54 53 } 55 54 55 + /* 56 + * Store all entries within entries array, 57 + * we will process it after we finish unwind. 58 + */ 56 59 static int entry(u64 ip, struct unwind_info *ui) 57 60 58 61 { 59 - struct unwind_entry e; 62 + struct unwind_entry *e = &ui->entries[ui->idx++]; 60 63 struct addr_location al; 61 64 62 65 if (__report_module(&al, ip, ui)) 63 66 return -1; 64 67 65 - e.ip = ip; 66 - e.map = al.map; 67 - e.sym = al.sym; 68 + e->ip = ip; 69 + e->map = al.map; 70 + e->sym = al.sym; 68 71 69 72 pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n", 70 73 al.sym ? al.sym->name : "''", 71 74 ip, 72 75 al.map ? al.map->map_ip(al.map, ip) : (u64) 0); 73 - 74 - return ui->cb(&e, ui->arg); 76 + return 0; 75 77 } 76 78 77 79 static pid_t next_thread(Dwfl *dwfl, void *arg, void **thread_argp) ··· 172 168 struct perf_sample *data, 173 169 int max_stack) 174 170 { 175 - struct unwind_info ui = { 171 + struct unwind_info *ui, ui_buf = { 176 172 .sample = data, 177 173 .thread = thread, 178 174 .machine = thread->mg->machine, ··· 181 177 .max_stack = max_stack, 182 178 }; 183 179 Dwarf_Word ip; 184 - int err = -EINVAL; 180 + int err = -EINVAL, i; 185 181 186 182 if (!data->user_regs.regs) 187 183 return -EINVAL; 188 184 189 - ui.dwfl = dwfl_begin(&offline_callbacks); 190 - if (!ui.dwfl) 185 + ui = zalloc(sizeof(ui_buf) + sizeof(ui_buf.entries[0]) * max_stack); 186 + if (!ui) 187 + return -ENOMEM; 188 + 189 + *ui = ui_buf; 190 + 191 + ui->dwfl = dwfl_begin(&offline_callbacks); 192 + if (!ui->dwfl) 191 193 goto out; 192 194 193 195 err = perf_reg_value(&ip, &data->user_regs, PERF_REG_IP); 194 196 if (err) 195 197 goto out; 196 198 197 - err = report_module(ip, &ui); 199 + err = report_module(ip, ui); 198 200 if (err) 199 201 goto out; 200 202 201 - if (!dwfl_attach_state(ui.dwfl, EM_NONE, thread->tid, &callbacks, &ui)) 203 + if (!dwfl_attach_state(ui->dwfl, EM_NONE, thread->tid, &callbacks, ui)) 202 204 goto out; 203 205 204 - err = dwfl_getthread_frames(ui.dwfl, thread->tid, frame_callback, &ui); 206 + err = dwfl_getthread_frames(ui->dwfl, thread->tid, frame_callback, ui); 205 207 206 - if (err && !ui.max_stack) 208 + if (err && !ui->max_stack) 207 209 err = 0; 210 + 211 + /* 212 + * Display what we got based on the order setup. 213 + */ 214 + for (i = 0; i < ui->idx && !err; i++) { 215 + int j = i; 216 + 217 + if (callchain_param.order == ORDER_CALLER) 218 + j = ui->idx - i - 1; 219 + 220 + err = ui->entries[j].ip ? ui->cb(&ui->entries[j], ui->arg) : 0; 221 + } 208 222 209 223 out: 210 224 if (err) 211 225 pr_debug("unwind: failed with '%s'\n", dwfl_errmsg(-1)); 212 226 213 - dwfl_end(ui.dwfl); 227 + dwfl_end(ui->dwfl); 228 + free(ui); 214 229 return 0; 215 230 }
+2
tools/perf/util/unwind-libdw.h
··· 16 16 unwind_entry_cb_t cb; 17 17 void *arg; 18 18 int max_stack; 19 + int idx; 20 + struct unwind_entry entries[]; 19 21 }; 20 22 21 23 #endif /* __PERF_UNWIND_LIBDW_H */