compositor: Use libunwind if available for better backtraces

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>
diff --git a/src/compositor.c b/src/compositor.c
index 63fe793..5c19d92 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -49,6 +49,11 @@
 #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"
@@ -3051,13 +3056,88 @@
 	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
@@ -3068,14 +3148,7 @@
 
 	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);