X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fapp-framework-binder.git;a=blobdiff_plain;f=src%2Fsig-monitor.c;h=b209dbf6ab2ef7d43857f4856e8de2871c0f8ecd;hp=fdaf8c09d642dc0ec9244e42c52e456523ec6498;hb=65353dce81a629e042800bb7b86fcd869a76727e;hpb=9944b380d1324a4c688f03bd10705b746b176101 diff --git a/src/sig-monitor.c b/src/sig-monitor.c index fdaf8c09..b209dbf6 100644 --- a/src/sig-monitor.c +++ b/src/sig-monitor.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 "IoT.bzh" + * Copyright (C) 2015-2020 "IoT.bzh" * Author José Bollo * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,47 +17,119 @@ #define _GNU_SOURCE +/******************************************************************************* +* sig-monitor is under the control of several compilation flags +*******************************************************************************/ + +/* controls whether to dump stack or not */ +#if !defined(WITH_SIG_MONITOR_DUMPSTACK) +# define WITH_SIG_MONITOR_DUMPSTACK 1 +#endif + +/* control whether to monitor signals */ +#if !defined(WITH_SIG_MONITOR_SIGNALS) +# define WITH_SIG_MONITOR_SIGNALS 1 +#endif + +/* controls whether to monitor calls */ +#if !defined(WITH_SIG_MONITOR_FOR_CALL) +# define WITH_SIG_MONITOR_FOR_CALL 1 +#endif + +/* control whether to monitor timers */ +#if !defined(WITH_SIG_MONITOR_TIMERS) +# define WITH_SIG_MONITOR_TIMERS 1 +#endif + +#if !WITH_SIG_MONITOR_SIGNALS +# undef WITH_SIG_MONITOR_FOR_CALL +# define WITH_SIG_MONITOR_FOR_CALL 0 +#endif + +#if !WITH_SIG_MONITOR_FOR_CALL +# undef WITH_SIG_MONITOR_TIMERS +# define WITH_SIG_MONITOR_TIMERS 0 +#endif + +/******************************************************************************/ + #include -#include +#include #include -#include -#include #include -#include -#include #include "sig-monitor.h" + #include "verbose.h" -#define SIG_FOR_TIMER SIGVTALRM +/******************************************************************************/ +#if !WITH_SIG_MONITOR_DUMPSTACK -/* local handler */ -static _Thread_local sigjmp_buf *error_handler; +static inline void dumpstack(int crop, int signum) {} -/* local timers */ -static _Thread_local int thread_timer_set; -static _Thread_local timer_t thread_timerid; +#else + +#include /* * Dumps the current stack */ -static void dumpstack(int crop) +static void dumpstack(int crop, int signum) { - int idx, count; - void *addresses[1000]; + int idx, count, rc; + void *addresses[100]; char **locations; + char buffer[8000]; + size_t pos, length; count = backtrace(addresses, sizeof addresses / sizeof *addresses); - locations = backtrace_symbols(addresses, count); + 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 { - for (idx = crop; idx < count; idx++) - ERROR("[BACKTRACE %d/%d] %s", idx - crop + 1, count - crop, locations[idx]); + 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); } } +#endif +/******************************************************************************/ +#if !WITH_SIG_MONITOR_TIMERS + +static inline int timeout_create() { return 0; } +static inline int timeout_arm(int timeout) { return 0; } +static inline void timeout_disarm() {} +static inline void timeout_delete() {} + +#define SIG_FOR_TIMER 0 + +#else + +#include +#include +#include + +#define SIG_FOR_TIMER SIGVTALRM + +/* local per thread timers */ +static _Thread_local int thread_timer_set; +static _Thread_local timer_t thread_timerid; + /* * Creates a timer for the current thread * @@ -82,7 +154,7 @@ static inline int timeout_create() rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid); thread_timer_set = !rc; } - return 0; + return rc; } /* @@ -124,44 +196,77 @@ static inline void timeout_delete() thread_timer_set = 0; } } +#endif +/******************************************************************************/ +#if !WITH_SIG_MONITOR_FOR_CALL +static inline void monitor_raise(int signum) {} -/* Handles signals that terminate the process */ -static void on_signal_terminate (int signum) +#else + +#include + +/* local handler */ +static _Thread_local sigjmp_buf *error_handler; + +static void monitor(int timeout, void (*function)(int sig, void*), void *arg) { - ERROR("Terminating signal %d received: %s", signum, strsignal(signum)); - exit(1); + volatile int signum, signum2; + sigjmp_buf jmpbuf, *older; + + older = error_handler; + signum = setjmp(jmpbuf); + if (signum == 0) { + error_handler = &jmpbuf; + if (timeout) { + timeout_create(); + timeout_arm(timeout); + } + function(0, arg); + } else { + signum2 = setjmp(jmpbuf); + if (signum2 == 0) + function(signum, arg); + } + if (timeout) + timeout_disarm(); + error_handler = older; } -/* Handles monitored signals that can be continued */ -static void on_signal_error(int signum) +static inline void monitor_raise(int signum) { - sigset_t sigset; + if (error_handler != NULL) + longjmp(*error_handler, signum); +} +#endif +/******************************************************************************/ +#if !WITH_SIG_MONITOR_SIGNALS - ERROR("ALERT! signal %d received: %s", signum, strsignal(signum)); - if (error_handler == NULL && signum == SIG_FOR_TIMER) - return; +static inline int enable_signal_handling() { return 0; } - dumpstack(3); +#else - // unlock signal to allow a new signal to come - if (error_handler != NULL) { - sigemptyset(&sigset); - sigaddset(&sigset, signum); - sigprocmask(SIG_UNBLOCK, &sigset, 0); - longjmp(*error_handler, signum); - } - ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum)); - exit(2); -} +#include + +/* internal signal lists */ +static int sigerr[] = { SIGSEGV, SIGFPE, SIGILL, SIGBUS, SIG_FOR_TIMER, 0 }; +static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 }; + +static int exiting = 0; +static int enabled = 0; /* install the handlers */ -static int install(void (*handler)(int), int *signals) +static int set_signals_handler(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++; @@ -169,12 +274,127 @@ static int install(void (*handler)(int), int *signals) return result; } -int sig_monitor_init() +/* + * rescue exit + */ +static void on_rescue_exit(int signum) +{ + ERROR("Rescue exit for signal %d: %s", signum, strsignal(signum)); + _exit(exiting); +} + +/* + * Do a direct safe exit + */ +static void direct_safe_exit(int code) +{ + set_signals_handler(on_rescue_exit, sigerr); + set_signals_handler(on_rescue_exit, sigterm); + exiting = code; + exit(code); +} + +/* + * Do a safe exit + */ +#if WITH_SIG_MONITOR_NO_DEFERRED_EXIT +# define safe_exit(x) direct_safe_exit(x) +#else +#include "jobs.h" +static void exit_job(int signum, void* arg) +{ + exiting = (int)(intptr_t)arg; + if (signum) + on_rescue_exit(signum); + exit(exiting); +} + +static void safe_exit(int code) +{ + if (jobs_queue(safe_exit, 0, exit_job, (void*)(intptr_t)code)) + direct_safe_exit(code); +} +#endif + +#if !WITH_SIG_MONITOR_DUMPSTACK + +static inline void safe_dumpstack(int crop, int signum) {} +#define in_safe_dumpstack (0) + +#else + +static _Thread_local int in_safe_dumpstack; + +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; +} +#endif + +/* Handles signals that terminate the process */ +static void on_signal_terminate (int signum) +{ + 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) { - static int sigerr[] = { SIG_FOR_TIMER, SIGSEGV, SIGFPE, 0 }; - static int sigterm[] = { SIGINT, SIGABRT, 0 }; + if (!in_safe_dumpstack) { + ERROR("ALERT! signal %d received: %s", signum, strsignal(signum)); - return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1; + safe_dumpstack(3, signum); + } + monitor_raise(signum); + + if (signum != SIG_FOR_TIMER) { + ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum)); + safe_exit(2); + } +} + +/* +static void disable_signal_handling() +{ + set_signals_handler(SIG_DFL, sigerr); + set_signals_handler(SIG_DFL, sigterm); + enabled = 0; +} +*/ + +static int enable_signal_handling() +{ + if (!set_signals_handler(on_signal_error, sigerr) + || !set_signals_handler(on_signal_terminate, sigterm)) { + return -1; + } + enabled = 1; + return 0; +} +#endif +/******************************************************************************/ + +int sig_monitor_init(int enable) +{ + return enable ? enable_signal_handling() : 0; } int sig_monitor_init_timeouts() @@ -189,24 +409,15 @@ void sig_monitor_clean_timeouts() void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg) { - volatile int signum, signum2; - sigjmp_buf jmpbuf, *older; - - older = error_handler; - signum = setjmp(jmpbuf); - if (signum == 0) { - error_handler = &jmpbuf; - if (timeout) - timeout_arm(timeout); +#if WITH_SIG_MONITOR_SIGNALS && WITH_SIG_MONITOR_FOR_CALL + if (enabled) + monitor(timeout, function, arg); + else +#endif function(0, arg); - } else { - signum2 = setjmp(jmpbuf); - if (signum2 == 0) - function(signum, arg); - } - error_handler = older; - if (timeout) - timeout_disarm(); } - +void sig_monitor_dumpstack() +{ + return dumpstack(1, 0); +}