diff options
author | Nick Mathewson <nickm@torproject.org> | 2013-07-26 13:22:56 +0200 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2013-11-18 10:43:15 -0500 |
commit | d631ddfb59eca910d1faf45a711da630bab7c89a (patch) | |
tree | 285b653c83db9ccb093cc8a2b0cf9d83545bbdab /src/common | |
parent | 2662885aa9cb6c9b7447b729d0321a633e8c46c3 (diff) | |
download | tor-d631ddfb59eca910d1faf45a711da630bab7c89a.tar tor-d631ddfb59eca910d1faf45a711da630bab7c89a.tar.gz |
Make backtrace handler handle signals correctly.
This meant moving a fair bit of code around, and writing a signal
cleanup function. Still pretty nice from what I can tell, though.
Diffstat (limited to 'src/common')
-rw-r--r-- | src/common/backtrace.c | 125 | ||||
-rw-r--r-- | src/common/backtrace.h | 2 | ||||
-rw-r--r-- | src/common/util.c | 4 |
3 files changed, 111 insertions, 20 deletions
diff --git a/src/common/backtrace.c b/src/common/backtrace.c index 2c49146a0..9c69e4f3f 100644 --- a/src/common/backtrace.c +++ b/src/common/backtrace.c @@ -7,6 +7,8 @@ #include "util.h" #include "torlog.h" +#define __USE_GNU + #ifdef HAVE_EXECINFO_H #include <execinfo.h> #endif @@ -16,6 +18,15 @@ #ifdef HAVE_UNISTD_H #include <unistd.h> #endif +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif +#ifdef HAVE_UCONTEXT_H +#include <ucontext.h> +#endif +#ifdef HAVE_SYS_UCONTEXT_H +#include <sys/ucontext.h> +#endif #if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \ defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) @@ -26,53 +37,135 @@ #define NO_BACKTRACE_IMPL #endif +/** Version of Tor to report in backtrace messages. */ static char *bt_version = NULL; #ifdef USE_BACKTRACE +/** Largest stack depth to try to dump. */ #define MAX_DEPTH 256 +/** Static allocation of stack to dump. This is static so we avoid stack + * pressure. */ static void *cb_buf[MAX_DEPTH]; -/**DOCDOC*/ +/** Change a stacktrace in <b>stack</b> of depth <b>depth</b> so that it will + * log the correct function from which a signal was received with context + * <b>ctx</b>. (When we get a signal, the current function will not have + * called any other function, and will therefore have not pushed its address + * onto the stack. Fortunately, we usually have the program counter in the + * ucontext_t structure. + */ +static void +clean_backtrace(void **stack, int depth, const ucontext_t *ctx) +{ +#ifdef PC_FROM_UCONTEXT +#if defined(__linux__) + const int n = 1; +#elif defined(__darwin__) || defined(__APPLE__) || defined(__OpenBSD__) \ + || defined(__FreeBSD__) + const int n = 2; +#else + const int n = 1; +#endif + if (depth <= n) + return; + + stack[n] = (void*) ctx->PC_FROM_UCONTEXT; +#else + (void) depth; + (void) ctx; +#endif +} + +/** Log a message <b>msg</b> at <b>severity</b> in <b>domain</b>, and follow + * that with a backtrace log. */ void -dump_backtrace(const char *msg) +log_backtrace(int severity, int domain, const char *msg) { - int depth; - const int *fds; - int n_fds; + int depth = backtrace(cb_buf, MAX_DEPTH); + char **symbols = backtrace_symbols(cb_buf, depth); int i; + tor_log(severity, domain, "%s. Stack trace:", msg); + if (!symbols) { + tor_log(severity, domain, " Unable to generate backtrace."); + return; + } + for (i=0; i < depth; ++i) { + tor_log(severity, domain, " %s", symbols[i]); + } + free(symbols); +} - if (!msg) msg = "unspecified crash"; +static void crash_handler(int sig, siginfo_t *si, void *ctx_) + __attribute__((noreturn)); + +/** Signal handler: write a crash message with a stack trace, and die. */ +static void +crash_handler(int sig, siginfo_t *si, void *ctx_) +{ + char buf[40]; + int depth; + ucontext_t *ctx = (ucontext_t *) ctx_; + int n_fds, i; + const int *fds = NULL; + + (void) si; depth = backtrace(cb_buf, MAX_DEPTH); + /* Clean up the top stack frame so we get the real function + * name for the most recently failing function. */ + clean_backtrace(cb_buf, depth, ctx); + + format_dec_number_sigsafe((unsigned)sig, buf, sizeof(buf)); - tor_log_err_sigsafe(bt_version, " died: ", msg, "\n", + tor_log_err_sigsafe(bt_version, " died: Caught signal ", buf, "\n", NULL); + n_fds = tor_log_get_sigsafe_err_fds(&fds); for (i=0; i < n_fds; ++i) backtrace_symbols_fd(cb_buf, depth, fds[i]); + + abort(); } -/**DOCDOC*/ +/** Install signal handlers as needed so that when we crash, we produce a + * useful stack trace. Return 0 on success, -1 on failure. */ static int install_bt_handler(void) { - /*XXXX add signal handlers */ /*XXXX make this idempotent */ - return 0; + int trap_signals[] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGSYS, + SIGIO, -1 }; + int i, rv=0; + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = crash_handler; + sa.sa_flags = SA_SIGINFO; + sigfillset(&sa.sa_mask); + + for (i = 0; trap_signals[i] >= 0; ++i) { + if (sigaction(trap_signals[i], &sa, NULL) == -1) { + log_warn(LD_BUG, "Sigaction failed: %s", strerror(errno)); + rv = -1; + } + } + return rv; } -/**DOCDOC*/ + +/** Uninstall crash handlers. */ static void remove_bt_handler(void) { + /*XXXX writeme*/ } #endif #ifdef NO_BACKTRACE_IMPL -/**DOCDOC*/ +/**DOCDOC */ void -dump_backtrace(const char *msg) +log_backtrace(int severity, int domain, const char *msg) { - tor_log_err_sigsafe(bt_version, " died: ", msg, "\n", NULL); + tor_log(severity, domain, "%s. (Stack trace not available)", msg); } /**DOCDOC*/ @@ -95,8 +188,8 @@ configure_backtrace_handler(const char *tor_version) { tor_free(bt_version); if (!tor_version) - tor_version = "Tor"; - bt_version = tor_strdup(tor_version); + tor_version = ""; + tor_asprintf(&bt_version, "Tor %s", tor_version); return install_bt_handler(); } diff --git a/src/common/backtrace.h b/src/common/backtrace.h index 7d7c10a80..765436fee 100644 --- a/src/common/backtrace.h +++ b/src/common/backtrace.h @@ -4,7 +4,7 @@ #ifndef TOR_BACKTRACE_H #define TOR_BACKTRACE_H -void dump_backtrace(const char *msg); +void log_backtrace(int severity, int domain, const char *msg); int configure_backtrace_handler(const char *tor_version); void clean_up_backtrace_handler(void); diff --git a/src/common/util.c b/src/common/util.c index 0b65437b0..bbb604492 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -108,9 +108,7 @@ tor_assertion_failed_(const char *fname, unsigned int line, tor_snprintf(buf, sizeof(buf), "Assertion %s failed in %s at %s:%u", expr, func, fname, line); - dump_backtrace(buf); - fprintf(stderr,"%s:%u: %s: Assertion %s failed; aborting.\n", - fname, line, func, expr); + log_backtrace(LOG_ERR, LD_BUG, buf); } /* ===== |