diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/backtrace.c | 39 | ||||
-rw-r--r-- | lib/backtrace.h | 19 | ||||
-rw-r--r-- | lib/daemon-private.h | 1 | ||||
-rw-r--r-- | lib/daemon-unix.c | 11 | ||||
-rw-r--r-- | lib/fatal-signal.c | 51 |
5 files changed, 114 insertions, 7 deletions
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); |