Commit 9343e45b authored by Matija Glavinic Pecotic's avatar Matija Glavinic Pecotic Committed by Arnaldo Carvalho de Melo

perf unwind: Fix looking up dwarf unwind stack info

Using perf with call graph method dwarf fails to provide backtrace
support for stripped binary even though .gnu_debuglink points to *.dbg
flavor with properly populated debug symbols.

Problem is reproduced on ARM (v7, v8), kernels 3.14.y, 4.4.y and
4.10.rc3.  Perf is configured with libunwind, and unwind dwarf support
[1]. Test code (stress_bt.c) can be found on [2].

Running (explicitly disable other unwinding methods):

  $ gcc -g -o stress_bt -fomit-frame-pointer -fno-unwind-tables \
	-fno-asynchronous-unwind-tables stress_bt.c
  $ perf record -N --call-graph dwarf ./stress_bt
  $ perf report

results in properly generated call graph. Stripping the binary and running
it results with missing call graph. Expected result is to have call graph:

  $ gcc -g -o stress_bt -fomit-frame-pointer -fno-unwind-tables \
	  -fno-asynchronous-unwind-tables stress_bt.c
  $ objcopy --only-keep-debug stress_bt stress_bt.dbg
  $ objcopy --strip-debug stress_bt
  $ objcopy --add-gnu-debuglink=stress_bt.dbg stress_bt
  $ perf record -N --call-graph dwarf ./stress_bt
  $ perf report

Problem is that perf doesn't try to read symbols pointed by gnu
debuglink.  Patch adds checking, and reading of the symbols from
debuglink and symsrc.  Order of the check is to first check within dso,
then check whether symsrc is defined and try to read from it. Finally,
debuglink is checked. Default locations of debug files are discussed in
[3] and [4]. Comments on RFC are on [5].

[1] https://wiki.linaro.org/LEG/Engineering/TOOLS/perf-callstack-unwinding
[2] [1]#Backtrace_stress_application
[3] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
[4] https://sourceware.org/binutils/docs/binutils/objcopy.html
[5] https://lkml.org/lkml/2016/8/22/473Signed-off-by: default avatarMatija Glavinic Pecotic <matija.glavinic-pecotic.ext@nokia.com>
Acked-by: default avatarJiri Olsa <jolsa@kernel.org>
Cc: Alexander Sverdlin <alexander.sverdlin@nokia.com>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/d309d40a-463f-482b-68e1-1465326efdc1@nokia.comSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 9f6f941e
...@@ -9,6 +9,13 @@ ...@@ -9,6 +9,13 @@
#include "debug.h" #include "debug.h"
#include "vdso.h" #include "vdso.h"
static const char * const debuglink_paths[] = {
"%.0s%s",
"%s/%s",
"%s/.debug/%s",
"/usr/lib/debug%s/%s"
};
char dso__symtab_origin(const struct dso *dso) char dso__symtab_origin(const struct dso *dso)
{ {
static const char origin[] = { static const char origin[] = {
...@@ -44,24 +51,43 @@ int dso__read_binary_type_filename(const struct dso *dso, ...@@ -44,24 +51,43 @@ int dso__read_binary_type_filename(const struct dso *dso,
size_t len; size_t len;
switch (type) { switch (type) {
case DSO_BINARY_TYPE__DEBUGLINK: { case DSO_BINARY_TYPE__DEBUGLINK:
char *debuglink; {
const char *last_slash;
char dso_dir[PATH_MAX];
char symfile[PATH_MAX];
unsigned int i;
len = __symbol__join_symfs(filename, size, dso->long_name); len = __symbol__join_symfs(filename, size, dso->long_name);
debuglink = filename + len; last_slash = filename + len;
while (debuglink != filename && *debuglink != '/') while (last_slash != filename && *last_slash != '/')
debuglink--; last_slash--;
if (*debuglink == '/')
debuglink++;
ret = -1; strncpy(dso_dir, filename, last_slash - filename);
if (!is_regular_file(filename)) dso_dir[last_slash-filename] = '\0';
if (!is_regular_file(filename)) {
ret = -1;
break;
}
ret = filename__read_debuglink(filename, symfile, PATH_MAX);
if (ret)
break; break;
ret = filename__read_debuglink(filename, debuglink, /* Check predefined locations where debug file might reside */
size - (debuglink - filename)); ret = -1;
for (i = 0; i < ARRAY_SIZE(debuglink_paths); i++) {
snprintf(filename, size,
debuglink_paths[i], dso_dir, symfile);
if (is_regular_file(filename)) {
ret = 0;
break;
}
} }
break; break;
}
case DSO_BINARY_TYPE__BUILD_ID_CACHE: case DSO_BINARY_TYPE__BUILD_ID_CACHE:
if (dso__build_id_filename(dso, filename, size) == NULL) if (dso__build_id_filename(dso, filename, size) == NULL)
ret = -1; ret = -1;
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "util.h" #include "util.h"
#include "debug.h" #include "debug.h"
#include "asm/bug.h" #include "asm/bug.h"
#include "dso.h"
extern int extern int
UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
...@@ -297,15 +298,58 @@ static int read_unwind_spec_debug_frame(struct dso *dso, ...@@ -297,15 +298,58 @@ static int read_unwind_spec_debug_frame(struct dso *dso,
int fd; int fd;
u64 ofs = dso->data.debug_frame_offset; u64 ofs = dso->data.debug_frame_offset;
/* debug_frame can reside in:
* - dso
* - debug pointed by symsrc_filename
* - gnu_debuglink, which doesn't necessary
* has to be pointed by symsrc_filename
*/
if (ofs == 0) { if (ofs == 0) {
fd = dso__data_get_fd(dso, machine); fd = dso__data_get_fd(dso, machine);
if (fd < 0) if (fd >= 0) {
return -EINVAL; ofs = elf_section_offset(fd, ".debug_frame");
dso__data_put_fd(dso);
}
if (ofs <= 0) {
fd = open(dso->symsrc_filename, O_RDONLY);
if (fd >= 0) {
ofs = elf_section_offset(fd, ".debug_frame");
close(fd);
}
}
if (ofs <= 0) {
char *debuglink = malloc(PATH_MAX);
int ret = 0;
ret = dso__read_binary_type_filename(
dso, DSO_BINARY_TYPE__DEBUGLINK,
machine->root_dir, debuglink, PATH_MAX);
if (!ret) {
fd = open(debuglink, O_RDONLY);
if (fd >= 0) {
ofs = elf_section_offset(fd,
".debug_frame");
close(fd);
}
}
if (ofs > 0) {
if (dso->symsrc_filename != NULL) {
pr_warning(
"%s: overwrite symsrc(%s,%s)\n",
__func__,
dso->symsrc_filename,
debuglink);
free(dso->symsrc_filename);
}
dso->symsrc_filename = debuglink;
} else {
free(debuglink);
}
}
/* Check the .debug_frame section for unwinding info */
ofs = elf_section_offset(fd, ".debug_frame");
dso->data.debug_frame_offset = ofs; dso->data.debug_frame_offset = ofs;
dso__data_put_fd(dso);
} }
*offset = ofs; *offset = ofs;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment