Commit b8af89f1 authored by Peter Hutterer's avatar Peter Hutterer

test: extract file:line from backtrace with addr2line

libunwind gives us a file and an address and usually a function name. Beyond
that, it's mostly guessing.
Fork off addr2line to resolve the addresses that libunwind gives us, if we
succeed we get a backtrace like this:

	Backtrace:
	0: litest_fail_comparison_int() (./test/litest.c:268)
	1: disable_button_scrolling() (./test/pointer.c:115)
	2: middlebutton_doubleclick() (./test/pointer.c:991)
	3: /lib64/libcheck.so.0 (srunner_run+0x7f5) [0x7f6c12d8c025]
	4: litest_run() (./test/litest.c:689)
	5: main() (./test/pointer.c:1280)
	6: /lib64/libc.so.6 (__libc_start_main+0xf0) [0x7f6c11a73790]
	7: ./test/test-pointer (_start+0x29) [0x403d99]
	8: ? (?+0x29) [0x29]

Note: I intentionally swapped function/file name in the output to make it
easier to spot which one is fully resolved and which one is the basic
libunwind output.
Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent dc3dbc4d
......@@ -68,6 +68,11 @@ if test "x$HAVE_LIBUNWIND" = "xyes"; then
AC_DEFINE(HAVE_LIBUNWIND, 1, [Have libunwind support])
fi
AM_CONDITIONAL(HAVE_LIBUNWIND, [test "x$HAVE_LIBUNWIND" = xyes])
AC_PATH_PROG(ADDR2LINE, [addr2line])
if test "x$ADDR2LINE" != "x"; then
AC_DEFINE_UNQUOTED(HAVE_ADDR2LINE, 1, [addr2line found])
AC_DEFINE_UNQUOTED(ADDR2LINE, ["$ADDR2LINE"], [Path to addr2line])
fi
AC_CHECK_LIB([m], [atan2])
AC_CHECK_LIB([rt], [clock_gettime])
......
......@@ -66,6 +66,68 @@ static int verbose = 0;
#define litest_vlog(...) /* __VA_ARGS__ */
#endif
static char cwd[PATH_MAX];
static bool
litest_backtrace_get_lineno(const char *executable,
unw_word_t addr,
char *file_return,
int *line_return)
{
#if HAVE_ADDR2LINE
FILE* f;
char buffer[PATH_MAX];
char *s;
int i;
if (!cwd[0])
getcwd(cwd, sizeof(cwd));
sprintf (buffer,
ADDR2LINE " -C -e %s -i %lx",
executable,
(unsigned long) addr);
f = popen(buffer, "r");
if (f == NULL) {
litest_log("Failed to execute: %s\n", buffer);
return false;
}
buffer[0] = '?';
fgets(buffer, sizeof(buffer), f);
fclose(f);
if (buffer[0] == '?')
return false;
s = strrchr(buffer, ':');
if (!s)
return false;
*s = '\0';
s++;
sscanf(s, "%d", line_return);
/* now strip cwd from buffer */
s = buffer;
i = 0;
while(cwd[i] == *s) {
*s = '\0';
s++;
i++;
}
if (i > 0)
*(--s) = '.';
strcpy(file_return, s);
return true;
#else /* HAVE_ADDR2LINE */
return false;
#endif
}
static void
litest_backtrace(void)
{
......@@ -100,6 +162,10 @@ litest_backtrace(void)
litest_log("\nBacktrace:\n");
ret = unw_step(&cursor);
while (ret > 0) {
char file[PATH_MAX];
int line;
bool have_lineno = false;
ret = unw_get_proc_info(&cursor, &pip);
if (ret) {
litest_log("unw_get_proc_info failed: %s [%d]\n",
......@@ -120,19 +186,33 @@ litest_backtrace(void)
if (dladdr((void *)(pip.start_ip + off), &dlinfo) &&
dlinfo.dli_fname &&
*dlinfo.dli_fname)
*dlinfo.dli_fname) {
filename = dlinfo.dli_fname;
else
have_lineno = litest_backtrace_get_lineno(filename,
(pip.start_ip + off),
file,
&line);
} else {
filename = "?";
}
litest_log("%u: %s (%s%s+%#x) [%p]\n",
i++,
filename,
procname,
ret == -UNW_ENOMEM ? "..." : "",
(int)off,
(void *)(pip.start_ip + off));
if (have_lineno) {
litest_log("%u: %s() (%s:%d)\n",
i,
procname,
file,
line);
} else {
litest_log("%u: %s (%s%s+%#x) [%p]\n",
i,
filename,
procname,
ret == -UNW_ENOMEM ? "..." : "",
(int)off,
(void *)(pip.start_ip + off));
}
i++;
ret = unw_step(&cursor);
if (ret < 0)
litest_log("unw_step failed: %s [%d]\n",
......
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