[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