1 #include "debug.h"
2 #include "logger.h"
3 
4 #ifdef __CYGWIN__
5 #ifndef SA_ONSTACK
6 #define SA_ONSTACK 0x08000000
7 #endif
8 #endif
9 
10 static char *assert_err  = "<no assertion failed>";
11 static char *assert_file = "<no file>";
12 static int assert_line   = 0;
13 
14 void
_debug_assert(char * err,char * file,int line)15 _debug_assert(char *err, char *file, int line)
16 {
17     logger(LOG_WARNING, "=== ASSERTION FAILED ===");
18     logger(LOG_WARNING, "%s:%d '%s' is not true", file, line, err);
19     assert_err  = err;
20     assert_file = file;
21     assert_line = line;
22     // force SIGSEGV to print the bug report
23     *((char *)-1) = 'x';
24 }
25 
26 void
debug_init(void)27 debug_init(void)
28 {
29     struct sigaction act;
30     sigemptyset(&act.sa_mask);
31     act.sa_flags     = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
32     act.sa_sigaction = debug_segv_handler;
33     sigaction(SIGSEGV, &act, NULL);
34     sigaction(SIGBUS, &act, NULL);
35     sigaction(SIGFPE, &act, NULL);
36     sigaction(SIGILL, &act, NULL);
37 }
38 
39 #ifdef HAVE_BACKTRACE
40 #include <execinfo.h>
41 #include <ucontext.h>
42 static void *
getMcontextEip(ucontext_t * uc)43 getMcontextEip(ucontext_t *uc)
44 {
45 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
46     /* OSX < 10.6 */
47     #if defined(__x86_64__)
48     return (void *) uc->uc_mcontext->__ss.__rip;
49     #elif defined(__i386__)
50     return (void *) uc->uc_mcontext->__ss.__eip;
51     #else
52     return (void *) uc->uc_mcontext->__ss.__srr0;
53     #endif
54 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
55     /* OSX >= 10.6 */
56     #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
57     return (void *) uc->uc_mcontext->__ss.__rip;
58     #else
59     return (void *) uc->uc_mcontext->__ss.__eip;
60     #endif
61 #elif defined(__linux__)
62     /* Linux */
63     #if defined(__i386__)
64     return (void *) uc->uc_mcontext.gregs[14]; /* Linux 32 */
65     #elif defined(__X86_64__) || defined(__x86_64__)
66     return (void *) uc->uc_mcontext.gregs[16]; /* Linux 64 */
67     #elif defined(__ia64__)                    /* Linux IA64 */
68     return (void *) uc->uc_mcontext.sc_ip;
69     #else
70     return NULL;
71     #endif
72 #else
73     return NULL;
74 #endif
75 }
76 
77 /**
78  * Logs the stack trace using the backtrace() call. This function is designed to
79  * be called from signal handlers safely.
80  */
81 static void
log_stack_trace(ucontext_t * uc)82 log_stack_trace(ucontext_t *uc)
83 {
84     void *trace[100];
85     int trace_size = 0;
86     int fd         = logger_fd >= 0 ? logger_fd : STDOUT_FILENO;
87     /* Generate the stack trace */
88     trace_size = backtrace(trace, 100);
89     /* overwrite sigaction with caller's address */
90     if (getMcontextEip(uc) != NULL)
91         trace[1] = getMcontextEip(uc);
92 
93     backtrace_symbols_fd(trace, trace_size, fd);
94 }
95 
96 #endif
97 
98 void
debug_segv_handler(int sig,siginfo_t * info,void * secret)99 debug_segv_handler(int sig, siginfo_t *info, void *secret)
100 {
101     logger(LOG_WARNING, "Crashed by signal: %d", sig);
102     logger(LOG_WARNING, "--- STACK TRACE");
103     logger(LOG_WARNING, "Failed assertion: %s (%s:%d)", assert_err, assert_file,
104            assert_line);
105 #ifdef HAVE_BACKTRACE
106     logger(LOG_WARNING, "--- STACK TRACE");
107     ucontext_t *uc = (ucontext_t *) secret;
108     log_stack_trace(uc);
109 #endif
110     /* Make sure we exit with the right signal at the end. So for instance
111      * the core will be dumped if enabled. */
112     struct sigaction act;
113     sigemptyset(&act.sa_mask);
114     act.sa_flags   = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
115     act.sa_handler = SIG_DFL;
116     sigaction(sig, &act, NULL);
117     kill(getpid(), sig);
118 }
119