compositor: Use libunwind if available for better backtraces
authorMarcin Slusarz <marcin.slusarz@gmail.com>
Mon, 18 Feb 2013 18:27:22 +0000 (13:27 -0500)
committerKristian Høgsberg <krh@bitplanet.net>
Tue, 19 Feb 2013 01:00:42 +0000 (20:00 -0500)
libunwind has a dwarf parser and automatically queries the dlinfo
for location of dlopened modules.  The resulting backtrace is much
better and includes stack frames in dynamically loaded modules.

krh: Originally submitted for Xorg, adapted for weston:

  http://lists.x.org/archives/xorg-devel/2013-February/035493.html

Note this require libunwind at least 1.1 to get the pkg-config files.

Signed-off-by: Marcin Slusarz <marcin.slusarz@gmail.com>
Conflicts:
configure.ac

configure.ac
src/Makefile.am
src/compositor.c

index 8a9c291..7bc5c83 100644 (file)
@@ -240,6 +240,13 @@ fi
 AC_SUBST(GCC_CFLAGS)
 AC_SUBST(GCC_CXXFLAGS)
 
+PKG_CHECK_MODULES(LIBUNWIND, libunwind,
+                  [have_libunwind=yes], [have_libunwind=no])
+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])
+
 WAYLAND_SCANNER_RULES(['$(top_srcdir)/protocol'])
 
 AC_CONFIG_FILES([Makefile
index 4be2e11..447b911 100644 (file)
@@ -7,8 +7,9 @@ AM_CPPFLAGS =                                   \
        -DLIBEXECDIR='"$(libexecdir)"'
 
 weston_LDFLAGS = -export-dynamic
-weston_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
-weston_LDADD = $(COMPOSITOR_LIBS) $(DLOPEN_LIBS) -lm ../shared/libshared.la
+weston_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) $(LIBUNWIND_CFLAGS)
+weston_LDADD = $(COMPOSITOR_LIBS) $(LIBUNWIND_LIBS) \
+       $(DLOPEN_LIBS) -lm ../shared/libshared.la
 
 weston_SOURCES =                               \
        git-version.h                           \
index 27e1868..6d7b2c7 100644 (file)
 #include <sys/time.h>
 #include <time.h>
 
+#ifdef HAVE_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#endif
+
 #include <wayland-server.h>
 #include "compositor.h"
 #include "../shared/os-compatibility.h"
@@ -2913,13 +2918,88 @@ static int on_term_signal(int signal_number, void *data)
        return 1;
 }
 
+#ifdef HAVE_LIBUNWIND
+
 static void
-on_segv_signal(int s, siginfo_t *siginfo, void *context)
+print_backtrace(void)
+{
+       unw_cursor_t cursor;
+       unw_context_t context;
+       unw_word_t off;
+       unw_proc_info_t pip;
+       int ret, i = 0;
+       char procname[256];
+       const char *filename;
+       Dl_info dlinfo;
+
+       pip.unwind_info = NULL;
+       ret = unw_getcontext(&context);
+       if (ret) {
+               weston_log("unw_getcontext: %d\n", ret);
+               return;
+       }
+
+       ret = unw_init_local(&cursor, &context);
+       if (ret) {
+               weston_log("unw_init_local: %d\n", ret);
+               return;
+       }
+
+       ret = unw_step(&cursor);
+       while (ret > 0) {
+               ret = unw_get_proc_info(&cursor, &pip);
+               if (ret) {
+                       weston_log("unw_get_proc_info: %d\n", ret);
+                       break;
+               }
+
+               ret = unw_get_proc_name(&cursor, procname, 256, &off);
+               if (ret && ret != -UNW_ENOMEM) {
+                       if (ret != -UNW_EUNSPEC)
+                               weston_log("unw_get_proc_name: %d\n", ret);
+                       procname[0] = '?';
+                       procname[1] = 0;
+               }
+
+               if (dladdr((void *)(pip.start_ip + off), &dlinfo) && dlinfo.dli_fname &&
+                   *dlinfo.dli_fname)
+                       filename = dlinfo.dli_fname;
+               else
+                       filename = "?";
+
+               weston_log("%u: %s (%s%s+0x%x) [%p]\n", i++, filename, procname,
+                          ret == -UNW_ENOMEM ? "..." : "", (int)off, (void *)(pip.start_ip + off));
+
+               ret = unw_step(&cursor);
+               if (ret < 0)
+                       weston_log("unw_step: %d\n", ret);
+       }
+}
+
+#else
+
+static void
+print_backtrace(void)
 {
        void *buffer[32];
        int i, count;
        Dl_info info;
 
+       count = backtrace(buffer, ARRAY_LENGTH(buffer));
+       for (i = 0; i < count; i++) {
+               dladdr(buffer[i], &info);
+               weston_log("  [%016lx]  %s  (%s)\n",
+                       (long) buffer[i],
+                       info.dli_sname ? info.dli_sname : "--",
+                       info.dli_fname);
+       }
+}
+
+#endif
+
+static void
+on_segv_signal(int s, siginfo_t *siginfo, void *context)
+{
        /* This SIGSEGV handler will do a best-effort backtrace, and
         * then call the backend restore function, which will switch
         * back to the vt we launched from or ungrab X etc and then
@@ -2930,14 +3010,7 @@ on_segv_signal(int s, siginfo_t *siginfo, void *context)
 
        weston_log("caught segv\n");
 
-       count = backtrace(buffer, ARRAY_LENGTH(buffer));
-       for (i = 0; i < count; i++) {
-               dladdr(buffer[i], &info);
-               weston_log("  [%016lx]  %s  (%s)\n",
-                       (long) buffer[i],
-                       info.dli_sname ? info.dli_sname : "--",
-                       info.dli_fname);
-       }
+       print_backtrace();
 
        segv_compositor->restore(segv_compositor);