ConnMan backtrace support
authorSamuel Ortiz <sameo@linux.intel.com>
Tue, 17 Aug 2010 15:10:46 +0000 (17:10 +0200)
committerSamuel Ortiz <sameo@linux.intel.com>
Tue, 17 Aug 2010 15:11:49 +0000 (17:11 +0200)
Add a generic signal handler in order to dump ConnMan backtrace when
crashing. The implementation is based on glibc backtrace() routines and
thus can not resolve static function names. A little python wrapper over
addr2line fixes that by taking a full backtrace from a complete connman
log file.

Makefile.am
src/log.c
test/backtrace [new file with mode: 0644]

index ed4689c..1bf7207 100644 (file)
@@ -179,7 +179,7 @@ test_scripts = test/get-state test/list-profiles test/list-services \
                test/monitor-manager test/test-counter test/set-ip-method \
                test/set-nameservers test/set-domains test/find-service \
                test/get-services test/get-proxy-autoconfig \
-               test/enable-tethering test/disable-tethering
+               test/enable-tethering test/disable-tethering test/backtrace
 
 if TEST
 testdir = $(pkglibdir)/test
index 327731f..4dee003 100644 (file)
--- a/src/log.c
+++ b/src/log.c
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <stdarg.h>
 #include <syslog.h>
+#include <execinfo.h>
+#include <dlfcn.h>
+#include <stdlib.h>
 
 #include "connman.h"
 
@@ -148,6 +152,49 @@ static connman_bool_t is_enabled(struct connman_debug_desc *desc)
        return FALSE;
 }
 
+static void signal_handler(int signo)
+{
+       void *frames[64];
+       char **symbols;
+       size_t n_ptrs;
+       unsigned int i;
+
+       n_ptrs = backtrace(frames, G_N_ELEMENTS(frames));
+       symbols = backtrace_symbols(frames, n_ptrs);
+       if (symbols == NULL) {
+               connman_error("No backtrace symbols");
+               exit(1);
+       }
+
+       connman_error("Aborting (signal %d)", signo);
+       connman_error("++++++++ ConnMan backtrace ++++++++");
+
+       for (i = 1; i < n_ptrs; i++)
+               connman_error("[%d]: %s", i - 1, symbols[i]);
+
+       connman_error("++++++++++++++++++++++++++++++++++++");
+
+       g_free(symbols);
+       exit(1);
+}
+
+static void signal_setup(sighandler_t handler)
+{
+       struct sigaction sa;
+       sigset_t mask;
+
+       sigemptyset(&mask);
+       sa.sa_handler = handler;
+       sa.sa_mask = mask;
+       sa.sa_flags = 0;
+       sigaction(SIGBUS, &sa, NULL);
+       sigaction(SIGILL, &sa, NULL);
+       sigaction(SIGFPE, &sa, NULL);
+       sigaction(SIGSEGV, &sa, NULL);
+       sigaction(SIGABRT, &sa, NULL);
+       sigaction(SIGPIPE, &sa, NULL);
+}
+
 int __connman_log_init(const char *debug, connman_bool_t detach)
 {
        int option = LOG_NDELAY | LOG_PID;
@@ -179,6 +226,8 @@ int __connman_log_init(const char *debug, connman_bool_t detach)
        if (detach == FALSE)
                option |= LOG_PERROR;
 
+       signal_setup(signal_handler);
+
        openlog("connmand", option, LOG_DAEMON);
 
        syslog(LOG_INFO, "Connection Manager version %s", VERSION);
@@ -193,4 +242,6 @@ void __connman_log_cleanup(void)
        closelog();
 
        g_strfreev(enabled);
+
+       signal_setup(SIG_DFL);
 }
diff --git a/test/backtrace b/test/backtrace
new file mode 100644 (file)
index 0000000..d33bcb1
--- /dev/null
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+
+import os
+import re
+import sys
+import subprocess
+
+if (len(sys.argv) < 3):
+       print "Usage: %s [connman binary] [connman log]" % (sys.argv[0])
+       sys.exit(1)
+
+binary = sys.argv[1]
+count = 0
+frames = []
+addrs = []
+
+log_file = open(sys.argv[2], 'r')
+
+# Extract addresses
+for line in log_file:
+    matchobj = re.compile(r'\[(0x[0-9a-f]+)\]$').search(line)
+    if matchobj:
+        addrs.append(matchobj.group(1))
+
+log_file.close()
+
+# Feed into addr2line
+command = ['addr2line', '--demangle', '--functions', '--basename', '-e', binary]
+command.extend(addrs)
+
+p = subprocess.Popen(command, shell=False, bufsize=0,
+          stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True)
+(child_stdin, child_stdout) = (p.stdin, p.stdout)
+
+child_stdin.close()
+
+# Backtrace display
+for line in child_stdout:
+
+    if line.startswith("??"): continue
+
+    line = line.strip()
+
+    frames.append(line)
+
+child_stdout.close()
+
+frame_count = len(frames);
+
+count = 0
+print "-------- ConnMan backtrace --------"
+while count < frame_count:
+    print "[%d]: %s() [%s]" % (count/2, frames[count], frames[count + 1])
+    count = count + 2
+print "-----------------------------------"