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 <[email protected]>
---
 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

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to