diff options
Diffstat (limited to 'tools/perf/util/unwind.c')
-rw-r--r-- | tools/perf/util/unwind.c | 84 |
1 files changed, 64 insertions, 20 deletions
diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c index 2f891f7e70bf..0efd5393de85 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind.c | |||
@@ -39,6 +39,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, | |||
39 | 39 | ||
40 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) | 40 | #define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) |
41 | 41 | ||
42 | extern int | ||
43 | UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug, | ||
44 | unw_word_t ip, | ||
45 | unw_word_t segbase, | ||
46 | const char *obj_name, unw_word_t start, | ||
47 | unw_word_t end); | ||
48 | |||
49 | #define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame) | ||
50 | |||
42 | #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ | 51 | #define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ |
43 | #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ | 52 | #define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ |
44 | 53 | ||
@@ -245,8 +254,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine, | |||
245 | return 0; | 254 | return 0; |
246 | } | 255 | } |
247 | 256 | ||
248 | static int read_unwind_spec(struct dso *dso, struct machine *machine, | 257 | static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine, |
249 | u64 *table_data, u64 *segbase, u64 *fde_count) | 258 | u64 *table_data, u64 *segbase, |
259 | u64 *fde_count) | ||
250 | { | 260 | { |
251 | int ret = -EINVAL, fd; | 261 | int ret = -EINVAL, fd; |
252 | u64 offset; | 262 | u64 offset; |
@@ -255,6 +265,7 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine, | |||
255 | if (fd < 0) | 265 | if (fd < 0) |
256 | return -EINVAL; | 266 | return -EINVAL; |
257 | 267 | ||
268 | /* Check the .eh_frame section for unwinding info */ | ||
258 | offset = elf_section_offset(fd, ".eh_frame_hdr"); | 269 | offset = elf_section_offset(fd, ".eh_frame_hdr"); |
259 | close(fd); | 270 | close(fd); |
260 | 271 | ||
@@ -263,10 +274,29 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine, | |||
263 | table_data, segbase, | 274 | table_data, segbase, |
264 | fde_count); | 275 | fde_count); |
265 | 276 | ||
266 | /* TODO .debug_frame check if eh_frame_hdr fails */ | ||
267 | return ret; | 277 | return ret; |
268 | } | 278 | } |
269 | 279 | ||
280 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
281 | static int read_unwind_spec_debug_frame(struct dso *dso, | ||
282 | struct machine *machine, u64 *offset) | ||
283 | { | ||
284 | int fd = dso__data_fd(dso, machine); | ||
285 | |||
286 | if (fd < 0) | ||
287 | return -EINVAL; | ||
288 | |||
289 | /* Check the .debug_frame section for unwinding info */ | ||
290 | *offset = elf_section_offset(fd, ".debug_frame"); | ||
291 | close(fd); | ||
292 | |||
293 | if (*offset) | ||
294 | return 0; | ||
295 | |||
296 | return -EINVAL; | ||
297 | } | ||
298 | #endif | ||
299 | |||
270 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) | 300 | static struct map *find_map(unw_word_t ip, struct unwind_info *ui) |
271 | { | 301 | { |
272 | struct addr_location al; | 302 | struct addr_location al; |
@@ -291,20 +321,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, | |||
291 | 321 | ||
292 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); | 322 | pr_debug("unwind: find_proc_info dso %s\n", map->dso->name); |
293 | 323 | ||
294 | if (read_unwind_spec(map->dso, ui->machine, | 324 | /* Check the .eh_frame section for unwinding info */ |
295 | &table_data, &segbase, &fde_count)) | 325 | if (!read_unwind_spec_eh_frame(map->dso, ui->machine, |
296 | return -EINVAL; | 326 | &table_data, &segbase, &fde_count)) { |
327 | memset(&di, 0, sizeof(di)); | ||
328 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | ||
329 | di.start_ip = map->start; | ||
330 | di.end_ip = map->end; | ||
331 | di.u.rti.segbase = map->start + segbase; | ||
332 | di.u.rti.table_data = map->start + table_data; | ||
333 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | ||
334 | / sizeof(unw_word_t); | ||
335 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
336 | need_unwind_info, arg); | ||
337 | } | ||
338 | |||
339 | #ifndef NO_LIBUNWIND_DEBUG_FRAME | ||
340 | /* Check the .debug_frame section for unwinding info */ | ||
341 | if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { | ||
342 | memset(&di, 0, sizeof(di)); | ||
343 | dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name, | ||
344 | map->start, map->end); | ||
345 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
346 | need_unwind_info, arg); | ||
347 | } | ||
348 | #endif | ||
297 | 349 | ||
298 | memset(&di, 0, sizeof(di)); | 350 | return -EINVAL; |
299 | di.format = UNW_INFO_FORMAT_REMOTE_TABLE; | ||
300 | di.start_ip = map->start; | ||
301 | di.end_ip = map->end; | ||
302 | di.u.rti.segbase = map->start + segbase; | ||
303 | di.u.rti.table_data = map->start + table_data; | ||
304 | di.u.rti.table_len = fde_count * sizeof(struct table_entry) | ||
305 | / sizeof(unw_word_t); | ||
306 | return dwarf_search_unwind_table(as, ip, &di, pi, | ||
307 | need_unwind_info, arg); | ||
308 | } | 351 | } |
309 | 352 | ||
310 | static int access_fpreg(unw_addr_space_t __maybe_unused as, | 353 | static int access_fpreg(unw_addr_space_t __maybe_unused as, |
@@ -516,7 +559,7 @@ static unw_accessors_t accessors = { | |||
516 | }; | 559 | }; |
517 | 560 | ||
518 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | 561 | static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, |
519 | void *arg) | 562 | void *arg, int max_stack) |
520 | { | 563 | { |
521 | unw_addr_space_t addr_space; | 564 | unw_addr_space_t addr_space; |
522 | unw_cursor_t c; | 565 | unw_cursor_t c; |
@@ -532,7 +575,7 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
532 | if (ret) | 575 | if (ret) |
533 | display_error(ret); | 576 | display_error(ret); |
534 | 577 | ||
535 | while (!ret && (unw_step(&c) > 0)) { | 578 | while (!ret && (unw_step(&c) > 0) && max_stack--) { |
536 | unw_word_t ip; | 579 | unw_word_t ip; |
537 | 580 | ||
538 | unw_get_reg(&c, UNW_REG_IP, &ip); | 581 | unw_get_reg(&c, UNW_REG_IP, &ip); |
@@ -545,7 +588,8 @@ static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, | |||
545 | 588 | ||
546 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | 589 | int unwind__get_entries(unwind_entry_cb_t cb, void *arg, |
547 | struct machine *machine, struct thread *thread, | 590 | struct machine *machine, struct thread *thread, |
548 | u64 sample_uregs, struct perf_sample *data) | 591 | u64 sample_uregs, struct perf_sample *data, |
592 | int max_stack) | ||
549 | { | 593 | { |
550 | unw_word_t ip; | 594 | unw_word_t ip; |
551 | struct unwind_info ui = { | 595 | struct unwind_info ui = { |
@@ -567,5 +611,5 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg, | |||
567 | if (ret) | 611 | if (ret) |
568 | return -ENOMEM; | 612 | return -ENOMEM; |
569 | 613 | ||
570 | return get_entries(&ui, cb, arg); | 614 | return get_entries(&ui, cb, arg, max_stack); |
571 | } | 615 | } |