diff --git a/changes/stack_trace b/changes/stack_trace new file mode 100644 index 0000000000..dc6363887c --- /dev/null +++ b/changes/stack_trace @@ -0,0 +1,6 @@ + o Major features: + - On some platforms (currently, recent OSX versions, and glibc-based + platforms that support the ELF format), Tor can now dump + stack traces when an assertion fails. By default, traces are dumped + to stderr, and to a stack_dump file in the DataDirectory. + diff --git a/configure.ac b/configure.ac index 4aeec929ef..5059079f7c 100644 --- a/configure.ac +++ b/configure.ac @@ -302,6 +302,8 @@ dnl exports strlcpy without defining it in a header. AC_CHECK_FUNCS( _NSGetEnviron \ accept4 \ + backtrace \ + backtrace_symbols_fd \ clock_gettime \ flock \ ftime \ @@ -319,6 +321,7 @@ AC_CHECK_FUNCS( memmem \ prctl \ rint \ + sigaction \ socketpair \ strlcat \ strlcpy \ @@ -802,6 +805,7 @@ dnl These headers are not essential AC_CHECK_HEADERS( arpa/inet.h \ crt_externs.h \ + execinfo.h \ grp.h \ ifaddrs.h \ inttypes.h \ diff --git a/src/common/backtrace.c b/src/common/backtrace.c new file mode 100644 index 0000000000..d3f59b2c70 --- /dev/null +++ b/src/common/backtrace.c @@ -0,0 +1,157 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "backtrace.h" +#include "compat.h" +#include "util.h" + +#ifdef HAVE_EXECINFO_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#if defined(HAVE_EXECINFO_H) && defined(HAVE_BACKTRACE) && \ + defined(HAVE_BACKTRACE_SYMBOLS_FD) && defined(HAVE_SIGACTION) +#define USE_BACKTRACE +#endif + +#if !defined(USE_BACKTRACE) +#define NO_BACKTRACE_IMPL +#endif + +static char *bt_filename = NULL; +static char *bt_version = NULL; + +#ifndef NO_BACKTRACE_IMPL +/**DOCDOC*/ +static int +open_bt_target(void) +{ + int fd = -1; + if (bt_filename) + fd = open(bt_filename, O_WRONLY|O_CREAT|O_APPEND, 0700); + return fd; +} +#endif + +/**DOCDOC*/ +static void +bt_write(int fd, const char *s, ssize_t n) +{ + int r; + if (n < 0) n = strlen(s); + + r = write(STDERR_FILENO, s, n); + if (fd >= 0) + r = write(fd, s, n); + (void)r; +} + +#ifdef USE_BACKTRACE +#define MAX_DEPTH 256 +static void *cb_buf[MAX_DEPTH]; + +/**DOCDOC*/ +void +dump_backtrace(const char *msg) +{ + char timebuf[32]; + time_t t = time(NULL); + int timebuf_len; + int depth; + int fd; + if (!msg) msg = "unspecified crash"; + + depth = backtrace(cb_buf, MAX_DEPTH); + + t /= 900; t *= 900; /* Round to the previous 15 minutes */ + timebuf[0] = '\0'; + timebuf_len = format_dec_number_sigsafe(t, timebuf, sizeof(timebuf)); + + fd = open_bt_target(); + bt_write(fd, "========================================" + "====================================\n", -1); + bt_write(fd, bt_version, -1); + bt_write(fd, " died around T=", -1); + bt_write(fd, timebuf, timebuf_len); + bt_write(fd, ": ", 2); + bt_write(fd, msg, -1); + bt_write(fd, "\n", 1); + backtrace_symbols_fd(cb_buf, depth, STDERR_FILENO); + if (fd >= 0) + backtrace_symbols_fd(cb_buf, depth, fd); + + close(fd); +} + +/**DOCDOC*/ +static int +install_bt_handler(void) +{ + /*XXXX add signal handlers */ + /*XXXX make this idempotent */ + return 0; +} +/**DOCDOC*/ +static void +remove_bt_handler(void) +{ +} +#endif + +#ifdef NO_BACKTRACE_IMPL +/**DOCDOC*/ +void +dump_backtrace(const char *msg) +{ + bt_write(-1, bt_version, -1); + bt_write(-1, " died: ", -1); + bt_write(-1, msg, -1); + bt_write(-1, "\n", -1); +} + +/**DOCDOC*/ +static int +install_bt_handler(void) +{ + return 0; +} + +/**DOCDOC*/ +static void +remove_bt_handler(void) +{ +} +#endif + +/**DOCDOC*/ +int +configure_backtrace_handler(const char *filename, const char *tor_version) +{ + tor_free(bt_filename); + if (filename) + bt_filename = tor_strdup(filename); + tor_free(bt_version); + if (!tor_version) + tor_version = "Tor"; + bt_version = tor_strdup(tor_version); + + return install_bt_handler(); +} + +/**DOCDOC*/ +void +clean_up_backtrace_handler(void) +{ + remove_bt_handler(); + + tor_free(bt_filename); + tor_free(bt_version); +} + diff --git a/src/common/backtrace.h b/src/common/backtrace.h new file mode 100644 index 0000000000..bb2396080a --- /dev/null +++ b/src/common/backtrace.h @@ -0,0 +1,12 @@ +/* Copyright (c) 2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#ifndef TOR_BACKTRACE_H +#define TOR_BACKTRACE_H + +void dump_backtrace(const char *msg); +int configure_backtrace_handler(const char *filename, const char *tor_version); +void clean_up_backtrace_handler(void); + +#endif + diff --git a/src/common/include.am b/src/common/include.am index 032befd209..814786b776 100644 --- a/src/common/include.am +++ b/src/common/include.am @@ -50,6 +50,7 @@ endif LIBOR_A_SOURCES = \ src/common/address.c \ + src/common/backtrace.c \ src/common/compat.c \ src/common/container.c \ src/common/di_ops.c \ @@ -90,6 +91,7 @@ src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS) COMMONHEADERS = \ src/common/address.h \ + src/common/backtrace.h \ src/common/aes.h \ src/common/ciphers.inc \ src/common/compat.h \ diff --git a/src/common/util.c b/src/common/util.c index 814eb1379b..0b65437b0f 100644 --- a/src/common/util.c +++ b/src/common/util.c @@ -24,6 +24,7 @@ #include "torint.h" #include "container.h" #include "address.h" +#include "backtrace.h" #ifdef _WIN32 #include @@ -101,8 +102,13 @@ void tor_assertion_failed_(const char *fname, unsigned int line, const char *func, const char *expr) { + char buf[256]; log_err(LD_BUG, "%s:%u: %s: Assertion %s failed; aborting.", fname, line, func, expr); + 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); } diff --git a/src/or/config.c b/src/or/config.c index ad6689ceef..af5bf41d24 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -12,6 +12,7 @@ #define CONFIG_PRIVATE #include "or.h" #include "addressmap.h" +#include "backtrace.h" #include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" @@ -1113,6 +1114,19 @@ options_act_reversible(const or_options_t *old_options, char **msg) /* No need to roll back, since you can't change the value. */ } + /* Enable crash logging to files */ + { + /* XXXX we might want to set this up earlier, if possible! */ + char *backtrace_fname = NULL; + char *progname = NULL; + tor_asprintf(&backtrace_fname, "%s"PATH_SEPARATOR"stack_dump", + options->DataDirectory); + tor_asprintf(&progname, "Tor %s", get_version()); + configure_backtrace_handler(backtrace_fname, progname); + tor_free(backtrace_fname); + tor_free(progname); + } + /* Write control ports to disk as appropriate */ control_ports_write_to_file(); diff --git a/src/or/main.c b/src/or/main.c index d1728250a4..ea86df0738 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -12,6 +12,7 @@ #include "or.h" #include "addressmap.h" +#include "backtrace.h" #include "buffers.h" #include "channel.h" #include "channeltls.h" @@ -2304,7 +2305,7 @@ handle_signals(int is_parent) int tor_init(int argc, char *argv[]) { - char buf[256]; + char progname[256]; int i, quiet = 0; time_of_process_start = time(NULL); if (!connection_array) @@ -2314,8 +2315,8 @@ tor_init(int argc, char *argv[]) if (!active_linked_connection_lst) active_linked_connection_lst = smartlist_new(); /* Have the log set up with our application name. */ - tor_snprintf(buf, sizeof(buf), "Tor %s", get_version()); - log_set_application_name(buf); + tor_snprintf(progname, sizeof(progname), "Tor %s", get_version()); + log_set_application_name(progname); /* Initialize the history structures. */ rep_hist_init(); /* Initialize the service cache. */ @@ -2684,6 +2685,8 @@ tor_main(int argc, char *argv[]) } #endif + configure_backtrace_handler(NULL, get_version()); + update_approx_time(time(NULL)); tor_threads_init(); init_logging();