sig-monitor: handle SIGSEGV in backtrace
[src/app-framework-binder.git] / src / sig-monitor.c
1 /*
2  * Copyright (C) 2017 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <signal.h>
22 #include <string.h>
23 #include <setjmp.h>
24 #include <time.h>
25 #include <unistd.h>
26 #include <sys/syscall.h>
27 #include <execinfo.h>
28
29 #include "sig-monitor.h"
30 #include "verbose.h"
31
32 #define SIG_FOR_TIMER   SIGVTALRM
33
34 /* local handler */
35 static _Thread_local sigjmp_buf *error_handler;
36 static _Thread_local int in_safe_dumpstack;
37
38 /* local timers */
39 static _Thread_local int thread_timer_set;
40 static _Thread_local timer_t thread_timerid;
41
42 /*
43  * Dumps the current stack
44  */
45 static void dumpstack(int crop, int signum)
46 {
47         int idx, count;
48         void *addresses[1000];
49         char **locations;
50
51         count = backtrace(addresses, sizeof addresses / sizeof *addresses);
52         locations = backtrace_symbols(addresses, count);
53         if (locations == NULL)
54                 ERROR("can't get the backtrace (returned %d addresses)", count);
55         else {
56                 for (idx = crop; idx < count; idx++)
57                         ERROR("[BACKTRACE %d/%d] %s", idx - crop + 1, count - crop, locations[idx]);
58                 free(locations);
59         }
60 }
61
62 static void safe_dumpstack_cb(int signum, void *closure)
63 {
64         int *args = closure;
65         if (signum)
66                 ERROR("Can't provide backtrace: raised signal %s", strsignal(signum));
67         else
68                 dumpstack(args[0], args[1]);
69 }
70
71 static void safe_dumpstack(int crop, int signum)
72 {
73         int args[2] = { crop, signum };
74
75         in_safe_dumpstack = 1;
76         sig_monitor(0, safe_dumpstack_cb, args);
77         in_safe_dumpstack = 0;
78 }
79
80 /*
81  * Creates a timer for the current thread
82  *
83  * Returns 0 in case of success
84  */
85 static inline int timeout_create()
86 {
87         int rc;
88         struct sigevent sevp;
89
90         if (thread_timer_set)
91                 rc = 0;
92         else {
93                 sevp.sigev_notify = SIGEV_THREAD_ID;
94                 sevp.sigev_signo = SIG_FOR_TIMER;
95                 sevp.sigev_value.sival_ptr = NULL;
96 #if defined(sigev_notify_thread_id)
97                 sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid);
98 #else
99                 sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid);
100 #endif
101                 rc = timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &thread_timerid);
102                 thread_timer_set = !rc;
103         }
104         return 0;
105 }
106
107 /*
108  * Arms the alarm in timeout seconds for the current thread
109  */
110 static inline int timeout_arm(int timeout)
111 {
112         int rc;
113         struct itimerspec its;
114
115         rc = timeout_create();
116         if (rc == 0) {
117                 its.it_interval.tv_sec = 0;
118                 its.it_interval.tv_nsec = 0;
119                 its.it_value.tv_sec = timeout;
120                 its.it_value.tv_nsec = 0;
121                 rc = timer_settime(thread_timerid, 0, &its, NULL);
122         }
123
124         return rc;
125 }
126
127 /*
128  * Disarms the current alarm
129  */
130 static inline void timeout_disarm()
131 {
132         if (thread_timer_set)
133                 timeout_arm(0);
134 }
135
136 /*
137  * Destroy any alarm resource for the current thread
138  */
139 static inline void timeout_delete()
140 {
141         if (thread_timer_set) {
142                 timer_delete(thread_timerid);
143                 thread_timer_set = 0;
144         }
145 }
146
147
148 /* Handles signals that terminate the process */
149 static void on_signal_terminate (int signum)
150 {
151         if (!in_safe_dumpstack) {
152                 ERROR("Terminating signal %d received: %s", signum, strsignal(signum));
153                 if (signum == SIGABRT)
154                         safe_dumpstack(3, signum);
155         }
156         exit(1);
157 }
158
159 /* Handles monitored signals that can be continued */
160 static void on_signal_error(int signum)
161 {
162         sigset_t sigset;
163
164         if (in_safe_dumpstack)
165                 longjmp(*error_handler, signum);
166
167         ERROR("ALERT! signal %d received: %s", signum, strsignal(signum));
168         if (error_handler == NULL && signum == SIG_FOR_TIMER)
169                 return;
170
171         safe_dumpstack(3, signum);
172
173         // unlock signal to allow a new signal to come
174         if (error_handler != NULL)
175                 longjmp(*error_handler, signum);
176
177         ERROR("Unmonitored signal %d received: %s", signum, strsignal(signum));
178         exit(2);
179 }
180
181 /* install the handlers */
182 static int install(void (*handler)(int), int *signals)
183 {
184         int result = 1;
185         struct sigaction sa;
186
187         sa.sa_handler = handler;
188         sigemptyset(&sa.sa_mask);
189         sa.sa_flags = SA_NODEFER;
190         while(*signals > 0) {
191                 if (sigaction(*signals, &sa, NULL) < 0) {
192                         ERROR("failed to install signal handler for signal %s: %m", strsignal(*signals));
193                         result = 0;
194                 }
195                 signals++;
196         }
197         return result;
198 }
199
200 int sig_monitor_init()
201 {
202         static int sigerr[] = { SIG_FOR_TIMER, SIGSEGV, SIGFPE, SIGILL, SIGBUS, 0 };
203         static int sigterm[] = { SIGINT, SIGABRT, SIGTERM, 0 };
204
205         return (install(on_signal_error, sigerr) & install(on_signal_terminate, sigterm)) - 1;
206 }
207
208 int sig_monitor_init_timeouts()
209 {
210         return timeout_create();
211 }
212
213 void sig_monitor_clean_timeouts()
214 {
215         timeout_delete();
216 }
217
218 void sig_monitor(int timeout, void (*function)(int sig, void*), void *arg)
219 {
220         volatile int signum, signum2;
221         sigjmp_buf jmpbuf, *older;
222
223         older = error_handler;
224         signum = setjmp(jmpbuf);
225         if (signum == 0) {
226                 error_handler = &jmpbuf;
227                 if (timeout)
228                         timeout_arm(timeout);
229                 function(0, arg);
230         } else {
231                 signum2 = setjmp(jmpbuf);
232                 if (signum2 == 0)
233                         function(signum, arg);
234         }
235         error_handler = older;
236         if (timeout)
237                 timeout_disarm();
238 }
239
240