X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fsig-monitor.c;h=9e13fa13fc8956b5d1ec16ac46bbe2c9634ef103;hb=e9c6530ee21bb3345741044e8bf87fc6a73252e9;hp=3e16f7206d98ade112e0a97b1bf64fb35bde18e9;hpb=49e6e83b203f1be3fe9488f3330c085f691a8ad5;p=src%2Fapp-framework-binder.git diff --git a/src/sig-monitor.c b/src/sig-monitor.c index 3e16f720..9e13fa13 100644 --- a/src/sig-monitor.c +++ b/src/sig-monitor.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 "IoT.bzh" + * Copyright (C) 2017, 2018 "IoT.bzh" * Author José Bollo * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,23 +18,88 @@ #define _GNU_SOURCE #include +#include #include #include #include #include #include #include +#include #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; +/* internal signal lists */ +static int sigerr[] = { SIG_FOR_TIMER, SIGSEGV, SIGFPE, SIGILL, SIGBUS, 0 }; +static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 }; +static int exiting = 0; +static int enabled = 0; + +/* + * 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 +114,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); @@ -102,54 +167,95 @@ static inline void timeout_delete() } } +/* install the handlers */ +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 (sigaction(*signals, &sa, NULL) < 0) { + ERROR("failed to install signal handler for signal %s: %m", strsignal(*signals)); + result = 0; + } + signals++; + } + return result; +} + +/* + * rescue exit + */ +static void on_rescue_exit(int signum) +{ + ERROR("Rescue exit for signal %d: %s", signum, strsignal(signum)); + _exit(exiting); +} + +/* + * Do a safe exit + */ +static void safe_exit(int code) +{ + install(on_rescue_exit, sigerr); + install(on_rescue_exit, sigterm); + exiting = code; + exit(code); +} /* Handles signals that terminate the process */ static void on_signal_terminate (int signum) { - ERROR("Terminating signal %d received: %s", signum, strsignal(signum)); - exit(1); + if (!in_safe_dumpstack) { + ERROR("Terminating signal %d received: %s", signum, strsignal(signum)); + if (signum == SIGABRT) + safe_dumpstack(3, signum); + } + safe_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); + safe_exit(2); } -/* install the handlers */ -static int install(void (*handler)(int), int *signals) +void sig_monitor_disable() { - int result = 1; - while(*signals > 0) { - if (signal(*signals, handler) == SIG_ERR) { - ERROR("failed to install signal handler for signal %s", strsignal(*signals)); - result = 0; - } - signals++; - } - return result; + enabled = 0; + install(SIG_DFL, sigerr); + install(SIG_DFL, sigterm); } -int sig_monitor_init() +int sig_monitor_enable() { - static int sigerr[] = { SIGALRM, SIGSEGV, SIGFPE, 0 }; - static int sigterm[] = { SIGINT, SIGABRT, 0 }; + enabled = install(on_signal_error, sigerr) && install(on_signal_terminate, sigterm); + if (enabled) + return 0; + sig_monitor_disable(); + return -1; +} - return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1; +int sig_monitor_init(int enable) +{ + return enable ? sig_monitor_enable() : (sig_monitor_disable(), 0); } int sig_monitor_init_timeouts() @@ -162,7 +268,7 @@ void sig_monitor_clean_timeouts() timeout_delete(); } -void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg) +static void monitor(int timeout, void (*function)(int sig, void*), void *arg) { volatile int signum, signum2; sigjmp_buf jmpbuf, *older; @@ -171,20 +277,30 @@ void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg) signum = setjmp(jmpbuf); if (signum == 0) { error_handler = &jmpbuf; - if (timeout) + if (timeout) { + timeout_create(); timeout_arm(timeout); + } function(0, arg); } else { signum2 = setjmp(jmpbuf); if (signum2 == 0) function(signum, arg); } - error_handler = older; if (timeout) timeout_disarm(); + error_handler = older; } +void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg) +{ + if (enabled) + monitor(timeout, function, arg); + else + function(0, arg); +} - - - +void sig_monitor_dumpstack() +{ + return dumpstack(1, 0); +}