[PATCH 6/7] test: extract file:line from backtrace with addr2line
Peter Hutterer
peter.hutterer at who-t.net
Thu May 7 22:57:49 PDT 2015
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 <peter.hutterer at who-t.net>
---
configure.ac | 5 +++
test/litest.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 94 insertions(+), 9 deletions(-)
diff --git a/configure.ac b/configure.ac
index 75951fd..f4c2b7d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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])
diff --git a/test/litest.c b/test/litest.c
index 9612e17..13d2afe 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -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",
--
2.3.5
More information about the wayland-devel
mailing list