aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/stack_trace6
-rw-r--r--configure.ac4
-rw-r--r--src/common/backtrace.c157
-rw-r--r--src/common/backtrace.h12
-rw-r--r--src/common/include.am2
-rw-r--r--src/common/util.c6
-rw-r--r--src/or/config.c14
-rw-r--r--src/or/main.c9
8 files changed, 207 insertions, 3 deletions
diff --git a/changes/stack_trace b/changes/stack_trace
new file mode 100644
index 000000000..dc6363887
--- /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 4aeec929e..5059079f7 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 000000000..d3f59b2c7
--- /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 <execinfo.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#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 000000000..bb2396080
--- /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 032befd20..814786b77 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 814eb1379..0b65437b0 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 <io.h>
@@ -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 ad6689cee..af5bf41d2 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 d1728250a..ea86df073 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();