summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml1
-rw-r--r--configure.ac1
-rw-r--r--lib/backtrace.c39
-rw-r--r--lib/backtrace.h19
-rw-r--r--lib/daemon-private.h1
-rw-r--r--lib/daemon-unix.c11
-rw-r--r--lib/fatal-signal.c51
-rw-r--r--m4/openvswitch.m410
8 files changed, 126 insertions, 7 deletions
diff --git a/.travis.yml b/.travis.yml
index 68026312b..b547eb041 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,6 +25,7 @@ addons:
- selinux-policy-dev
- libunbound-dev
- libunbound-dev:i386
+ - libunwind-dev
before_install: ./.travis/${TRAVIS_OS_NAME}-prepare.sh
diff --git a/configure.ac b/configure.ac
index afd1e8345..0c593f469 100644
--- a/configure.ac
+++ b/configure.ac
@@ -138,6 +138,7 @@ OVS_LIBTOOL_VERSIONS
OVS_CHECK_CXX
AX_FUNC_POSIX_MEMALIGN
OVS_CHECK_UNBOUND
+OVS_CHECK_UNWIND
OVS_CHECK_INCLUDE_NEXT([stdio.h string.h])
AC_CONFIG_FILES([
diff --git a/lib/backtrace.c b/lib/backtrace.c
index 5cb2954f8..934763448 100644
--- a/lib/backtrace.c
+++ b/lib/backtrace.c
@@ -15,10 +15,15 @@
*/
#include <config.h>
+#include <errno.h>
+#include <fcntl.h>
#include <inttypes.h>
+#include <string.h>
+#include <unistd.h>
#include "backtrace.h"
#include "openvswitch/vlog.h"
+#include "util.h"
VLOG_DEFINE_THIS_MODULE(backtrace);
@@ -76,3 +81,37 @@ log_backtrace_at(const char *msg, const char *where)
ds_destroy(&ds);
}
+
+#ifdef HAVE_UNWIND
+void
+log_received_backtrace(int fd) {
+ int byte_read;
+ struct unw_backtrace backtrace[UNW_MAX_DEPTH];
+
+ VLOG_WARN("%s fd %d", __func__, fd);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+ memset(backtrace, 0, UNW_MAX_BUF);
+
+ byte_read = read(fd, backtrace, UNW_MAX_BUF);
+ if (byte_read < 0) {
+ VLOG_ERR("Read fd %d failed: %s", fd,
+ ovs_strerror(errno));
+ } else if (byte_read > 0) {
+ VLOG_WARN("SIGSEGV detected, backtrace:");
+ for (int i = 0; i < UNW_MAX_DEPTH; i++) {
+ if (backtrace[i].func[0] == 0) {
+ break;
+ }
+ VLOG_WARN("0x%016lx <%s+0x%lx>\n",
+ backtrace[i].ip,
+ backtrace[i].func,
+ backtrace[i].offset);
+ }
+ }
+}
+#else /* !HAVE_UNWIND */
+void
+log_received_backtrace(int daemonize_fd OVS_UNUSED) {
+ VLOG_WARN("Backtrace using libunwind not supported.");
+}
+#endif /* HAVE_UNWIND */
diff --git a/lib/backtrace.h b/lib/backtrace.h
index 384f2700d..5708bf9c6 100644
--- a/lib/backtrace.h
+++ b/lib/backtrace.h
@@ -20,6 +20,11 @@
#include <stdint.h>
#include "openvswitch/dynamic-string.h"
+#ifdef HAVE_UNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#endif
+
/* log_backtrace() will save the backtrace of a running program
* into the log at the DEBUG level.
*
@@ -68,7 +73,21 @@ struct backtrace {
uintptr_t frames[BACKTRACE_MAX_FRAMES];
};
+#ifdef HAVE_UNWIND
+#define UNW_MAX_DEPTH 32
+#define UNW_MAX_FUNCN 32
+#define UNW_MAX_BUF \
+ (UNW_MAX_DEPTH * sizeof(struct unw_backtrace))
+
+struct unw_backtrace {
+ char func[UNW_MAX_FUNCN];
+ unw_word_t ip;
+ unw_word_t offset;
+};
+#endif
+
void backtrace_capture(struct backtrace *);
void log_backtrace_at(const char *msg, const char *where);
+void log_received_backtrace(int fd);
#endif /* backtrace.h */
diff --git a/lib/daemon-private.h b/lib/daemon-private.h
index 8bc71e2a6..4e0667601 100644
--- a/lib/daemon-private.h
+++ b/lib/daemon-private.h
@@ -19,6 +19,7 @@
extern bool detach;
extern char *pidfile;
+extern int daemonize_fd;
char *make_pidfile_name(const char *name);
diff --git a/lib/daemon-unix.c b/lib/daemon-unix.c
index 6169763c2..7e48630f0 100644
--- a/lib/daemon-unix.c
+++ b/lib/daemon-unix.c
@@ -15,6 +15,7 @@
*/
#include <config.h>
+#include "backtrace.h"
#include "daemon.h"
#include "daemon-private.h"
#include <errno.h>
@@ -75,7 +76,7 @@ static bool overwrite_pidfile;
static bool chdir_ = true;
/* File descriptor used by daemonize_start() and daemonize_complete(). */
-static int daemonize_fd = -1;
+int daemonize_fd = -1;
/* --monitor: Should a supervisory process monitor the daemon and restart it if
* it dies due to an error signal? */
@@ -291,8 +292,7 @@ fork_and_wait_for_startup(int *fdp, pid_t *child_pid)
OVS_NOT_REACHED();
}
}
- close(fds[0]);
- *fdp = -1;
+ *fdp = fds[0];
} else if (!pid) {
/* Running in child process. */
close(fds[0]);
@@ -313,8 +313,6 @@ fork_notify_startup(int fd)
if (error) {
VLOG_FATAL("pipe write failed (%s)", ovs_strerror(error));
}
-
- close(fd);
}
}
@@ -393,6 +391,8 @@ monitor_daemon(pid_t daemon_pid)
}
}
+ log_received_backtrace(daemonize_fd);
+
/* Throttle restarts to no more than once every 10 seconds. */
if (time(NULL) < last_restart + 10) {
VLOG_WARN("%s, waiting until 10 seconds since last "
@@ -508,7 +508,6 @@ daemonize_complete(void)
detached = true;
fork_notify_startup(daemonize_fd);
- daemonize_fd = -1;
daemonize_post_detach();
}
}
diff --git a/lib/fatal-signal.c b/lib/fatal-signal.c
index 3b905b6de..7733850d5 100644
--- a/lib/fatal-signal.c
+++ b/lib/fatal-signal.c
@@ -14,6 +14,7 @@
* limitations under the License.
*/
#include <config.h>
+#include "backtrace.h"
#include "fatal-signal.h"
#include <errno.h>
#include <signal.h>
@@ -34,6 +35,10 @@
#include "openvswitch/type-props.h"
+#ifdef HAVE_UNWIND
+#include "daemon-private.h"
+#endif
+
#ifndef SIG_ATOMIC_MAX
#define SIG_ATOMIC_MAX TYPE_MAXIMUM(sig_atomic_t)
#endif
@@ -42,7 +47,8 @@ VLOG_DEFINE_THIS_MODULE(fatal_signal);
/* Signals to catch. */
#ifndef _WIN32
-static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM };
+static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM,
+ SIGSEGV };
#else
static const int fatal_signals[] = { SIGTERM };
#endif
@@ -151,6 +157,44 @@ fatal_signal_add_hook(void (*hook_cb)(void *aux), void (*cancel_cb)(void *aux),
ovs_mutex_unlock(&mutex);
}
+#ifdef HAVE_UNWIND
+/* Send the backtrace buffer to monitor thread.
+ *
+ * Note that this runs in the signal handling context, any system
+ * library functions used here must be async-signal-safe.
+ */
+static inline void
+send_backtrace_to_monitor(void) {
+ int dep;
+ struct unw_backtrace unw_bt[UNW_MAX_DEPTH];
+ unw_cursor_t cursor;
+ unw_context_t uc;
+
+ if (daemonize_fd == -1) {
+ return;
+ }
+
+ dep = 0;
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+
+ while (dep < UNW_MAX_DEPTH && unw_step(&cursor)) {
+ memset(unw_bt[dep].func, 0, UNW_MAX_FUNCN);
+ unw_get_reg(&cursor, UNW_REG_IP, &unw_bt[dep].ip);
+ unw_get_proc_name(&cursor, unw_bt[dep].func, UNW_MAX_FUNCN,
+ &unw_bt[dep].offset);
+ dep++;
+ }
+
+ ignore(write(daemonize_fd, unw_bt, dep * sizeof(struct unw_backtrace)));
+}
+#else
+static inline void
+send_backtrace_to_monitor(void) {
+ /* Nothing. */
+}
+#endif
+
/* Handles fatal signal number 'sig_nr'.
*
* Ordinarily this is the actual signal handler. When other code needs to
@@ -164,6 +208,11 @@ void
fatal_signal_handler(int sig_nr)
{
#ifndef _WIN32
+ if (sig_nr == SIGSEGV) {
+ signal(sig_nr, SIG_DFL); /* Set it back immediately. */
+ send_backtrace_to_monitor();
+ raise(sig_nr);
+ }
ignore(write(signal_fds[1], "", 1));
#else
SetEvent(wevent);
diff --git a/m4/openvswitch.m4 b/m4/openvswitch.m4
index 78d70fb4e..79e0be5a3 100644
--- a/m4/openvswitch.m4
+++ b/m4/openvswitch.m4
@@ -637,3 +637,13 @@ AC_DEFUN([OVS_CHECK_UNBOUND],
fi
AM_CONDITIONAL([HAVE_UNBOUND], [test "$HAVE_UNBOUND" = yes])
AC_SUBST([HAVE_UNBOUND])])
+
+dnl Checks for libunwind.
+AC_DEFUN([OVS_CHECK_UNWIND],
+ [AC_CHECK_LIB(unwind, unw_backtrace, [HAVE_UNWIND=yes], [HAVE_UNWIND=no])
+ if test "$HAVE_UNWIND" = yes; then
+ AC_DEFINE([HAVE_UNWIND], [1], [Define to 1 if unwind is detected.])
+ LIBS="$LIBS -lunwind"
+ fi
+ AM_CONDITIONAL([HAVE_UNWIND], [test "$HAVE_UNWIND" = yes])
+ AC_SUBST([HAVE_UNWIND])])