Commit b93b0967826a4a00297dde1cbbda8df673c1689e

Authored by Wang Nan
Committed by Arnaldo Carvalho de Melo
1 parent ea1fe3a887

perf test: Fix dwarf unwind using libunwind.

Perf tool fails to unwind user stack if the event raises in a shared
object. This patch improves tests/dwarf-unwind.c to demonstrate the
problem by utilizing commonly used glibc function "bsearch". If perf is
not statically linked, the testcase will try to unwind a mixed call
trace.

By debugging libunwind I found that there is a bug in unwind-libunwind:
it always passes 0 as segbase to libunwind, cause libunwind unable to
locate debug_frame entry fir first level ip address (I add some more
debugging output into libunwind to make things clear):

               >_Uarm_dwarf_find_debug_frame: start_ip = 10be98, end_ip = 10c2a4
               >_Uarm_dwarf_find_debug_frame: found debug_frame table `/lib/libc-2.18.so': segbase=0x0, len=7, gp=0x0, table_data=0x449388
               >_Uarm_dwarf_search_unwind_table: call lookup:ip = b6cd3bcc, segbase = 0, rel_ip = b6cd3bcc
               >lookup: e->start_ip_offset = bcf18 (rel_ip = b6cd3bcc)
               >lookup: e->start_ip_offset = 6d314 (rel_ip = b6cd3bcc)
               >lookup: e->start_ip_offset = 33d0c (rel_ip = b6cd3bcc)
                ...
               >lookup: e->start_ip_offset = 15d0c (rel_ip = b6cd3bcc)
               >lookup: e->start_ip_offset = 15c40 (rel_ip = b6cd3bcc)
 >_Uarm_dwarf_search_unwind_table: IP b6cd3bcc inside range b6c12000-b6d4c000, but no explicit unwind info found
                >put_rs_cache: unmasking signals/interrupts and releasing lock
               >_Uarm_dwarf_step: returning -10
 >_Uarm_step: dwarf_step()=-10

This patch passes map->start as segbase to dwarf_find_debug_frame(), so
di will be initialized correctly.

In addition, dso and executable are different when setting segbase. This
patch first check whether the elf is executable, and pass segbase only
for shared object.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/1421203007-75799-1-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Showing 2 changed files with 61 additions and 3 deletions Side-by-side Diff

tools/perf/tests/dwarf-unwind.c
... ... @@ -11,6 +11,9 @@
11 11 #include "thread.h"
12 12 #include "callchain.h"
13 13  
  14 +/* For bsearch. We try to unwind functions in shared object. */
  15 +#include <stdlib.h>
  16 +
14 17 static int mmap_handler(struct perf_tool *tool __maybe_unused,
15 18 union perf_event *event,
16 19 struct perf_sample *sample __maybe_unused,
... ... @@ -28,7 +31,7 @@
28 31 mmap_handler, machine, true);
29 32 }
30 33  
31   -#define MAX_STACK 6
  34 +#define MAX_STACK 8
32 35  
33 36 static int unwind_entry(struct unwind_entry *entry, void *arg)
34 37 {
... ... @@ -37,6 +40,8 @@
37 40 static const char *funcs[MAX_STACK] = {
38 41 "test__arch_unwind_sample",
39 42 "unwind_thread",
  43 + "compare",
  44 + "bsearch",
40 45 "krava_3",
41 46 "krava_2",
42 47 "krava_1",
43 48  
44 49  
... ... @@ -88,10 +93,37 @@
88 93 return err;
89 94 }
90 95  
  96 +static int global_unwind_retval = -INT_MAX;
  97 +
91 98 __attribute__ ((noinline))
  99 +static int compare(void *p1, void *p2)
  100 +{
  101 + /* Any possible value should be 'thread' */
  102 + struct thread *thread = *(struct thread **)p1;
  103 +
  104 + if (global_unwind_retval == -INT_MAX)
  105 + global_unwind_retval = unwind_thread(thread);
  106 +
  107 + return p1 - p2;
  108 +}
  109 +
  110 +__attribute__ ((noinline))
92 111 static int krava_3(struct thread *thread)
93 112 {
94   - return unwind_thread(thread);
  113 + struct thread *array[2] = {thread, thread};
  114 + void *fp = &bsearch;
  115 + /*
  116 + * make _bsearch a volatile function pointer to
  117 + * prevent potential optimization, which may expand
  118 + * bsearch and call compare directly from this function,
  119 + * instead of libc shared object.
  120 + */
  121 + void *(*volatile _bsearch)(void *, void *, size_t,
  122 + size_t, int (*)(void *, void *));
  123 +
  124 + _bsearch = fp;
  125 + _bsearch(array, &thread, 2, sizeof(struct thread **), compare);
  126 + return global_unwind_retval;
95 127 }
96 128  
97 129 __attribute__ ((noinline))
tools/perf/util/unwind-libunwind.c
... ... @@ -185,6 +185,28 @@
185 185 return offset;
186 186 }
187 187  
  188 +#ifndef NO_LIBUNWIND_DEBUG_FRAME
  189 +static int elf_is_exec(int fd, const char *name)
  190 +{
  191 + Elf *elf;
  192 + GElf_Ehdr ehdr;
  193 + int retval = 0;
  194 +
  195 + elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
  196 + if (elf == NULL)
  197 + return 0;
  198 + if (gelf_getehdr(elf, &ehdr) == NULL)
  199 + goto out;
  200 +
  201 + retval = (ehdr.e_type == ET_EXEC);
  202 +
  203 +out:
  204 + elf_end(elf);
  205 + pr_debug("unwind: elf_is_exec(%s): %d\n", name, retval);
  206 + return retval;
  207 +}
  208 +#endif
  209 +
188 210 struct table_entry {
189 211 u32 start_ip_offset;
190 212 u32 fde_offset;
191 213  
... ... @@ -322,8 +344,12 @@
322 344 #ifndef NO_LIBUNWIND_DEBUG_FRAME
323 345 /* Check the .debug_frame section for unwinding info */
324 346 if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
  347 + int fd = dso__data_fd(map->dso, ui->machine);
  348 + int is_exec = elf_is_exec(fd, map->dso->name);
  349 + unw_word_t base = is_exec ? 0 : map->start;
  350 +
325 351 memset(&di, 0, sizeof(di));
326   - if (dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name,
  352 + if (dwarf_find_debug_frame(0, &di, ip, base, map->dso->name,
327 353 map->start, map->end))
328 354 return dwarf_search_unwind_table(as, ip, &di, pi,
329 355 need_unwind_info, arg);