sig-monitor: Fix backtrace cropping
[src/app-framework-binder.git] / src / sig-monitor.c
index d00f0f9..db83343 100644 (file)
 #define _GNU_SOURCE
 
 #include <stdlib.h>
+#include <stdio.h>
 #include <signal.h>
 #include <string.h>
 #include <setjmp.h>
 #include <time.h>
 #include <unistd.h>
 #include <sys/syscall.h>
+#include <execinfo.h>
 
 #include "sig-monitor.h"
 #include "verbose.h"
 
+#define SIG_FOR_TIMER   SIGVTALRM
+
 /* local handler */
 static _Thread_local sigjmp_buf *error_handler;
+static _Thread_local int in_safe_dumpstack;
 
 /* local timers */
 static _Thread_local int thread_timer_set;
 static _Thread_local timer_t thread_timerid;
 
+/*
+ * Dumps the current stack
+ */
+static void dumpstack(int crop, int signum)
+{
+       int idx, count, rc;
+       void *addresses[100];
+       char **locations;
+       char buffer[8000];
+       size_t pos, length;
+
+       count = backtrace(addresses, sizeof addresses / sizeof *addresses);
+       if (count <= crop)
+               crop = 0;
+       count -= crop;
+       locations = backtrace_symbols(&addresses[crop], count);
+       if (locations == NULL)
+               ERROR("can't get the backtrace (returned %d addresses)", count);
+       else {
+               length = sizeof buffer - 1;
+               pos = 0;
+               idx = 0;
+               while (pos < length && idx < count) {
+                       rc = snprintf(&buffer[pos], length - pos, " [%d/%d] %s\n", idx + 1, count, locations[idx]);
+                       pos += rc >= 0 ? rc : 0;
+                       idx++;
+               }
+               buffer[length] = 0;
+               if (signum)
+                       ERROR("BACKTRACE due to signal %s/%d:\n%s", strsignal(signum), signum, buffer);
+               else
+                       ERROR("BACKTRACE:\n%s", buffer);
+               free(locations);
+       }
+}
+
+static void safe_dumpstack_cb(int signum, void *closure)
+{
+       int *args = closure;
+       if (signum)
+               ERROR("Can't provide backtrace: raised signal %s", strsignal(signum));
+       else
+               dumpstack(args[0], args[1]);
+}
+
+static void safe_dumpstack(int crop, int signum)
+{
+       int args[2] = { crop + 3, signum };
+
+       in_safe_dumpstack = 1;
+       sig_monitor(0, safe_dumpstack_cb, args);
+       in_safe_dumpstack = 0;
+}
+
 /*
  * Creates a timer for the current thread
  *
@@ -49,7 +108,7 @@ static inline int timeout_create()
                rc = 0;
        else {
                sevp.sigev_notify = SIGEV_THREAD_ID;
-               sevp.sigev_signo = SIGALRM;
+               sevp.sigev_signo = SIG_FOR_TIMER;
                sevp.sigev_value.sival_ptr = NULL;
 #if defined(sigev_notify_thread_id)
                sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid);
@@ -106,24 +165,30 @@ static inline void timeout_delete()
 /* Handles signals that terminate the process */
 static void on_signal_terminate (int signum)
 {
-       ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
+       if (!in_safe_dumpstack) {
+               ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
+               if (signum == SIGABRT)
+                       safe_dumpstack(3, signum);
+       }
        exit(1);
 }
 
 /* Handles monitored signals that can be continued */
 static void on_signal_error(int signum)
 {
-       sigset_t sigset;
+       if (in_safe_dumpstack)
+               longjmp(*error_handler, signum);
+
+       ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
+       if (error_handler == NULL && signum == SIG_FOR_TIMER)
+               return;
+
+       safe_dumpstack(3, signum);
 
        // unlock signal to allow a new signal to come
-       if (error_handler != NULL) {
-               sigemptyset(&sigset);
-               sigaddset(&sigset, signum);
-               sigprocmask(SIG_UNBLOCK, &sigset, 0);
+       if (error_handler != NULL)
                longjmp(*error_handler, signum);
-       }
-       if (signum == SIGALRM)
-               return;
+
        ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
        exit(2);
 }
@@ -132,9 +197,14 @@ static void on_signal_error(int signum)
 static int install(void (*handler)(int), int *signals)
 {
        int result = 1;
+       struct sigaction sa;
+
+       sa.sa_handler = handler;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_NODEFER;
        while(*signals > 0) {
-               if (signal(*signals, handler) == SIG_ERR) {
-                       ERROR("failed to install signal handler for signal %s", strsignal(*signals));
+               if (sigaction(*signals, &sa, NULL) < 0) {
+                       ERROR("failed to install signal handler for signal %s: %m", strsignal(*signals));
                        result = 0;
                }
                signals++;
@@ -144,8 +214,8 @@ static int install(void (*handler)(int), int *signals)
 
 int sig_monitor_init()
 {
-       static int sigerr[] = { SIGALRM, SIGSEGV, SIGFPE, 0 };
-       static int sigterm[] = { SIGINT, SIGABRT, 0 };
+       static int sigerr[] = { SIG_FOR_TIMER, SIGSEGV, SIGFPE, SIGILL, SIGBUS, 0 };
+       static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 };
 
        return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
 }
@@ -161,16 +231,6 @@ void sig_monitor_clean_timeouts()
 }
 
 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
-{
-       sig_monitor3(timeout, (void (*)(int,void*,void*,void*))function, arg, NULL, NULL);
-}
-
-void sig_monitor2(int timeout, void (*function)(int sig, void*, void*), void *arg1, void *arg2)
-{
-       sig_monitor3(timeout, (void (*)(int,void*,void*,void*))function, arg1, arg2, NULL);
-}
-
-void sig_monitor3(int timeout, void (*function)(int sig, void*, void*, void*), void *arg1, void *arg2, void *arg3)
 {
        volatile int signum, signum2;
        sigjmp_buf jmpbuf, *older;
@@ -181,11 +241,11 @@ void sig_monitor3(int timeout, void (*function)(int sig, void*, void*, void*), v
                error_handler = &jmpbuf;
                if (timeout)
                        timeout_arm(timeout);
-               function(0, arg1, arg2, arg3);
+               function(0, arg);
        } else {
                signum2 = setjmp(jmpbuf);
                if (signum2 == 0)
-                       function(signum, arg1, arg2, arg3);
+                       function(signum, arg);
        }
        error_handler = older;
        if (timeout)
@@ -193,6 +253,3 @@ void sig_monitor3(int timeout, void (*function)(int sig, void*, void*, void*), v
 }
 
 
-
-
-