Nilorea Library
C utilities for networking, threading, graphics
n_signals.c
Go to the documentation of this file.
1
9#include <signal.h>
10#include <stdio.h>
11#include <assert.h>
12#include <stdlib.h>
13#include <stdbool.h>
14#include <stdint.h>
15#include <stdbool.h>
16#include <errno.h>
17#include <string.h>
18
19#include "nilorea/n_log.h"
20#include "nilorea/n_signals.h"
21#include "nilorea/n_str.h"
22
24#define LOGFPRT( ... ) fprintf( stderr , "Error: " __VA_ARGS__ )
25
27#define LOGNLOG( ... ) n_log( LOG_ERR , __VA_ARGS__ )
28
30#define LOGSIG LOGNLOG
31
33static const char *__n_stack_traced_progam_name ;
34
35#ifdef __windows__
36#include <windows.h>
37#include <imagehlp.h>
38#else
39#include <err.h>
40#include <execinfo.h>
41#endif
42
43// void almost_c99_signal_handler(int sig)
44// {
45// switch(sig)
46// {
47// case SIGABRT:
48// LOGSIG("Caught SIGABRT: usually caused by an abort() or assert()" );
49// break;
50// case SIGFPE:
51// LOGSIG("Caught SIGFPE: arithmetic exception, such as divide by zero" );
52// break;
53// case SIGILL:
54// LOGSIG("Caught SIGILL: illegal instruction" );
55// break;
56// case SIGINT:
57// LOGSIG("Caught SIGINT: interactive attention signal, probably a ctrl+c" );
58// break;
59// case SIGSEGV:
60// LOGSIG("Caught SIGSEGV: segfault" );
61// break;
62// case SIGTERM:
63// default:
64// LOGSIG("Caught SIGTERM: a termination request was sent to the program" );
65// break;
66// }
67// _Exit(1);
68// }
69
70// void set_signal_handler()
71// {
72// signal(SIGABRT, almost_c99_signal_handler);
73// signal(SIGFPE, almost_c99_signal_handler);
74// signal(SIGILL, almost_c99_signal_handler);
75// signal(SIGINT, almost_c99_signal_handler);
76// signal(SIGSEGV, almost_c99_signal_handler);
77// signal(SIGTERM, almost_c99_signal_handler);
78// }
79
80
81
88int addr2line(char const * const program_name, void const * const addr)
89{
90 char addr2line_cmd[4093] = "" ;
91
92 /* have addr2line map the address to the relent line in the code */
93#ifdef __APPLE__
94 /* apple does things differently... */
95 sprintf(addr2line_cmd,"atos -o %.256s ./%p", program_name, addr);
96#else
97#ifdef __windows__
98 sprintf(addr2line_cmd,"addr2line -f -p -e ./%s %p", program_name, addr);
99 LOGSIG( "%s", addr2line_cmd );
100 return 0 ;
101#else
102 sprintf(addr2line_cmd,"addr2line -f -p -e %.256s %p", program_name, addr);
103#endif
104#endif
105 N_STR *output = NULL ;
106 int ret = -1 ;
107 n_popen( addr2line_cmd, 1024, (void **)&output, &ret );
108 LOGSIG( "%s", output -> data );
109 free_nstr( &output );
110 return ret ;
111} /* addr2line(...) */
112
113
114#ifdef __windows__
119void windows_print_stacktrace(CONTEXT* context)
120{
121 SymInitialize(GetCurrentProcess(), 0, true);
122
123 STACKFRAME frame = { 0 };
124
125 /* setup initial stack frame */
126#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
127 /* Wrong values for W10 ? */
128 frame.AddrPC.Offset = context->Eip;
129 frame.AddrPC.Mode = AddrModeFlat;
130 frame.AddrStack.Offset = context->Esp;
131 frame.AddrStack.Mode = AddrModeFlat;
132 frame.AddrFrame.Offset = context->Ebp;
133 frame.AddrFrame.Mode = AddrModeFlat;
134#else
135 frame.AddrPC.Offset = context->Rip;
136 frame.AddrPC.Mode = AddrModeFlat;
137 frame.AddrStack.Offset = context->Rsp;
138 frame.AddrStack.Mode = AddrModeFlat;
139 frame.AddrFrame.Offset = context->Rbp;
140 frame.AddrFrame.Mode = AddrModeFlat;
141#endif
142
143 while (StackWalk(IMAGE_FILE_MACHINE_I386,
144 GetCurrentProcess(),
145 GetCurrentThread(),
146 &frame,
147 context,
148 0,
149 SymFunctionTableAccess,
150 SymGetModuleBase,
151 0 ) )
152 {
153 addr2line( __n_stack_traced_progam_name, (void*)frame.AddrPC.Offset);
154 }
155
156 SymCleanup( GetCurrentProcess() );
157} /* windows_print_stacktrace(...) */
158
159
160
166LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS * ExceptionInfo)
167{
168 switch(ExceptionInfo->ExceptionRecord->ExceptionCode)
169 {
170 case EXCEPTION_ACCESS_VIOLATION:
171 LOGSIG("Error: EXCEPTION_ACCESS_VIOLATION" );
172 break;
173 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
174 LOGSIG("Error: EXCEPTION_ARRAY_BOUNDS_EXCEEDED" );
175 break;
176 case EXCEPTION_BREAKPOINT:
177 LOGSIG("Error: EXCEPTION_BREAKPOINT" );
178 break;
179 case EXCEPTION_DATATYPE_MISALIGNMENT:
180 LOGSIG("Error: EXCEPTION_DATATYPE_MISALIGNMENT" );
181 break;
182 case EXCEPTION_FLT_DENORMAL_OPERAND:
183 LOGSIG("Error: EXCEPTION_FLT_DENORMAL_OPERAND" );
184 break;
185 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
186 LOGSIG("Error: EXCEPTION_FLT_DIVIDE_BY_ZERO" );
187 break;
188 case EXCEPTION_FLT_INEXACT_RESULT:
189 LOGSIG("Error: EXCEPTION_FLT_INEXACT_RESULT" );
190 break;
191 case EXCEPTION_FLT_INVALID_OPERATION:
192 LOGSIG("Error: EXCEPTION_FLT_INVALID_OPERATION" );
193 break;
194 case EXCEPTION_FLT_OVERFLOW:
195 LOGSIG("Error: EXCEPTION_FLT_OVERFLOW" );
196 break;
197 case EXCEPTION_FLT_STACK_CHECK:
198 LOGSIG("Error: EXCEPTION_FLT_STACK_CHECK" );
199 break;
200 case EXCEPTION_FLT_UNDERFLOW:
201 LOGSIG("Error: EXCEPTION_FLT_UNDERFLOW" );
202 break;
203 case EXCEPTION_ILLEGAL_INSTRUCTION:
204 LOGSIG("Error: EXCEPTION_ILLEGAL_INSTRUCTION" );
205 break;
206 case EXCEPTION_IN_PAGE_ERROR:
207 LOGSIG("Error: EXCEPTION_IN_PAGE_ERROR" );
208 break;
209 case EXCEPTION_INT_DIVIDE_BY_ZERO:
210 LOGSIG( "Error: EXCEPTION_INT_DIVIDE_BY_ZERO" );
211 break;
212 case EXCEPTION_INT_OVERFLOW:
213 LOGSIG( "Error: EXCEPTION_INT_OVERFLOW" );
214 break;
215 case EXCEPTION_INVALID_DISPOSITION:
216 LOGSIG( "Error: EXCEPTION_INVALID_DISPOSITION" );
217 break;
218 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
219 LOGSIG( "Error: EXCEPTION_NONCONTINUABLE_EXCEPTION" );
220 break;
221 case EXCEPTION_PRIV_INSTRUCTION:
222 LOGSIG( "Error: EXCEPTION_PRIV_INSTRUCTION" );
223 break;
224 case EXCEPTION_SINGLE_STEP:
225 LOGSIG( "Error: EXCEPTION_SINGLE_STEP" );
226 break;
227 case EXCEPTION_STACK_OVERFLOW:
228 LOGSIG( "Error: EXCEPTION_STACK_OVERFLOW" );
229 break;
230 default:
231 LOGSIG( "Error: Unrecognized Exception" );
232 break;
233 }
234 /* If this is a stack overflow then we can't walk the stack, so just show
235 where the error happened */
236 if (EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode)
237 {
238 windows_print_stacktrace(ExceptionInfo->ContextRecord);
239 }
240 else
241 {
242 addr2line(__n_stack_traced_progam_name, (void*)ExceptionInfo->ContextRecord->Rip);
243 }
244 fflush(stdout);
245 fflush(stderr);
246 return EXCEPTION_EXECUTE_HANDLER;
247} /* windows_exception_handler( ... ) */
248
249
254void set_signal_handler( const char *progname )
255{
257 SetUnhandledExceptionFilter(windows_exception_handler);
258}
259#else
262
267{
268 int i, trace_size = 0;
269 char **messages = (char **)NULL;
270
271 memset( stack_traces , 0 , MAX_STACK_FRAMES * sizeof( void * ) );
272
273 trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
274 messages = backtrace_symbols(stack_traces, trace_size);
275
276 /* skip the first couple stack frames (as they are this function and
277 our handler) and also skip the last frame as it's (always?) junk. */
278 // for (i = 3; i < (trace_size - 1); ++i)
279 for (i = 0; i < trace_size; ++i) // we'll use this for now so you can see what's going on
280 {
282 {
283 LOGSIG(" error determining line # for: %s", messages[i] );
284 }
285 }
286 if (messages)
287 {
288 free(messages);
289 }
290}
291
298void posix_signal_handler(int sig, siginfo_t *siginfo, void *context)
299{
300 (void)context;
301 switch(sig)
302 {
303 case SIGSEGV:
304 LOGSIG("Caught SIGSEGV: Segmentation Fault" );
305 break;
306 case SIGINT:
307 LOGSIG("Caught SIGINT: Interactive attention signal, (usually ctrl+c)" );
308 break;
309 case SIGFPE:
310 switch(siginfo->si_code)
311 {
312 case FPE_INTDIV:
313 LOGSIG("Caught SIGFPE: (integer divide by zero)" );
314 break;
315 case FPE_INTOVF:
316 LOGSIG("Caught SIGFPE: (integer overflow)" );
317 break;
318 case FPE_FLTDIV:
319 LOGSIG("Caught SIGFPE: (floating-point divide by zero)" );
320 break;
321 case FPE_FLTOVF:
322 LOGSIG("Caught SIGFPE: (floating-point overflow)" );
323 break;
324 case FPE_FLTUND:
325 LOGSIG("Caught SIGFPE: (floating-point underflow)" );
326 break;
327 case FPE_FLTRES:
328 LOGSIG("Caught SIGFPE: (floating-point inexact result)" );
329 break;
330 case FPE_FLTINV:
331 LOGSIG("Caught SIGFPE: (floating-point invalid operation)" );
332 break;
333 case FPE_FLTSUB:
334 LOGSIG("Caught SIGFPE: (subscript out of range)" );
335 break;
336 default:
337 LOGSIG("Caught SIGFPE: Arithmetic Exception" );
338 break;
339 }
340 break;
341 case SIGILL:
342 switch(siginfo->si_code)
343 {
344 case ILL_ILLOPC:
345 LOGSIG("Caught SIGILL: (illegal opcode)" );
346 break;
347 case ILL_ILLOPN:
348 LOGSIG("Caught SIGILL: (illegal operand)" );
349 break;
350 case ILL_ILLADR:
351 LOGSIG("Caught SIGILL: (illegal addressing mode)" );
352 break;
353 case ILL_ILLTRP:
354 LOGSIG("Caught SIGILL: (illegal trap)" );
355 break;
356 case ILL_PRVOPC:
357 LOGSIG("Caught SIGILL: (privileged opcode)" );
358 break;
359 case ILL_PRVREG:
360 LOGSIG("Caught SIGILL: (privileged register)" );
361 break;
362 case ILL_COPROC:
363 LOGSIG("Caught SIGILL: (coprocessor error)" );
364 break;
365 case ILL_BADSTK:
366 LOGSIG("Caught SIGILL: (internal stack error)" );
367 break;
368 default:
369 LOGSIG("Caught SIGILL: Illegal Instruction" );
370 break;
371 }
372 break;
373 case SIGTERM:
374 LOGSIG("Caught SIGTERM: a termination request was sent to the program" );
375 break;
376 case SIGABRT:
377 LOGSIG("Caught SIGABRT: usually caused by an abort() or assert()" );
378 break;
379 default:
380 break;
381 }
383 _Exit(1);
384}
385
388
393void set_signal_handler( const char *progname )
394{
396
397#ifdef RLIMIT_STACK
398 /* Before starting the endless recursion, try to be friendly to the user's
399 machine. On some Linux 2.2.x systems, there is no stack limit for user
400 processes at all. We don't want to kill such systems. */
401 struct rlimit rl;
402 rl.rlim_cur = rl.rlim_max = 0x100000; /* 1 MB */
403 setrlimit (RLIMIT_STACK, &rl);
404#endif
405
406 /* setup alternate stack */
407 {
408 stack_t ss ;
409
410 /* malloc is usually used here, I'm not 100% sure my static allocation
411 is valid but it seems to work just fine. */
412 ss.ss_sp = alternate_stack;
413 ss.ss_size = sizeof( alternate_stack );
414 ss.ss_flags = SS_ONSTACK ;
415
416 if (sigaltstack(&ss, NULL) != 0)
417 {
418 err(1, "sigaltstack");
419 }
420 }
421
422 /* register our signal handlers */
423 {
424 struct sigaction sig_action ;
425 sigemptyset( &sig_action.sa_mask );
426 sig_action.sa_sigaction = posix_signal_handler;
427
428#ifdef __APPLE__
429 /* for some reason we backtrace() doesn't work on osx
430 when we use an alternate stack */
431 sig_action.sa_flags = SA_SIGINFO;
432#else
433 sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
434#endif
435
436 if (sigaction(SIGSEGV, &sig_action, NULL) != 0)
437 {
438 err(1, "sigaction");
439 }
440 if (sigaction(SIGFPE, &sig_action, NULL) != 0)
441 {
442 err(1, "sigaction");
443 }
444 if (sigaction(SIGINT, &sig_action, NULL) != 0)
445 {
446 err(1, "sigaction");
447 }
448 if (sigaction(SIGILL, &sig_action, NULL) != 0)
449 {
450 err(1, "sigaction");
451 }
452 /* No TERM catch
453 if (sigaction(SIGTERM, &sig_action, NULL) != 0) { err(1, "sigaction"); } */
454 if (sigaction(SIGABRT, &sig_action, NULL) != 0)
455 {
456 err(1, "sigaction");
457 }
458 }
459}
460#endif
int n_popen(char *cmd, int read_buf_size, void **nstr_output, int *ret)
launch a command abd return output and status
Definition: n_common.c:185
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition: n_str.h:222
A box including a string and his lenght.
Definition: n_str.h:173
#define SIGALTSTACK_SIZE
Size of the signal handler alternate stack.
Definition: n_signals.h:23
#define MAX_STACK_FRAMES
Number of backtrace log lines.
Definition: n_signals.h:25
void set_signal_handler(const char *progname)
Install a signal handler for progname.
Definition: n_signals.c:393
Generic log system.
static void * stack_traces[MAX_STACK_FRAMES]
static frame list
Definition: n_signals.c:261
static const char * __n_stack_traced_progam_name
name of program to debug (addr2line & co)
Definition: n_signals.c:33
int addr2line(char const *const program_name, void const *const addr)
Resolve symbol name and source location given the path to the executable and an address.
Definition: n_signals.c:88
static uint8_t alternate_stack[SIGALTSTACK_SIZE]
static block for alternate stack
Definition: n_signals.c:387
#define LOGSIG
internal: output to syslog
Definition: n_signals.c:30
void posix_print_stack_trace()
print current stack
Definition: n_signals.c:266
void posix_signal_handler(int sig, siginfo_t *siginfo, void *context)
decode a signal and call stack print
Definition: n_signals.c:298
Signals general handling with stack printing, from https://gist.github.com/jvranish/4441299.
N_STR and string function declaration.