1 #include "debug.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdint.h>
5 #include "time_utils.h"
6 
7 #include "breakpad.h"
8 #include "fdb_internal.h"
9 
10 #define N_DBG_SWITCH (256)
11 
12 static uint8_t _global_dbg_switch[N_DBG_SWITCH];
13 static void* _global_dbg_addr[N_DBG_SWITCH];
14 static uint64_t _global_dbg_uint64_t[N_DBG_SWITCH];
15 
16 fdb_fatal_error_callback fatal_error_callback = nullptr;
17 
18 // minidump_dir used by breakpad
19 static const char* minidump_dir = nullptr;
20 
21 // LCOV_EXCL_START
_dbg_sw_set(int n)22 void _dbg_sw_set(int n)
23 {
24     _global_dbg_switch[n] = 1;
25 }
26 
_dbg_sw_clear(int n)27 void _dbg_sw_clear(int n)
28 {
29     _global_dbg_switch[n] = 0;
30 }
31 
_dbg_set_addr(int n, void *addr)32 void _dbg_set_addr(int n, void *addr)
33 {
34     _global_dbg_addr[n] = addr;
35 }
36 
_dbg_get_addr(int n)37 void * _dbg_get_addr(int n)
38 {
39     return _global_dbg_addr[n];
40 }
41 
_dbg_set_uint64_t(int n, uint64_t val)42 void _dbg_set_uint64_t(int n, uint64_t val)
43 {
44     _global_dbg_uint64_t[n] = val;
45 }
46 
_dbg_get_uint64_t(int n)47 uint64_t _dbg_get_uint64_t(int n)
48 {
49     return _global_dbg_uint64_t[n];
50 }
51 
_dbg_is_sw_set(int n)52 int _dbg_is_sw_set(int n)
53 {
54     return _global_dbg_switch[n];
55 }
56 
57 // Only if HANG_ON_CRASH is set in environment variable, hang process
_dbg_hang_process(void)58 static void _dbg_hang_process(void)
59 {
60     char *hang_process = getenv("HANG_ON_CRASH");
61     if (hang_process) {
62         fprintf(stderr, "Hanging process...");
63         fprintf(stderr, "\n");
64         while (1) {
65             usleep(1000);
66         }
67     }
68 }
69 
70 // to profile first install perf
71 // echo 0 > /proc/sys/kernel/kptr_restrict
72 #if defined(__linux__) && !defined(__ANDROID__)
73 #include <string.h>
74 #include <dlfcn.h>
75 #include <ucontext.h>
76 #include <unistd.h>
77 #include <sys/types.h>
78 #include <sys/stat.h>
79 #include <signal.h>
80 
sigalrm_handler(int sig)81 void sigalrm_handler(int sig) {
82     fdb_assert(false, false, false);
83 }
84 
85 static struct sigaction caller_sigact;
86 static stack_t __sigstack;
sigsegv_handler(int sig, siginfo_t *siginfo, void *context)87 static void sigsegv_handler(int sig, siginfo_t *siginfo, void *context)
88 {
89     ucontext_t *u = (ucontext_t *)context;
90 #ifdef REG_RIP // Test if the Program Counter is 64 bits
91     unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];
92 #else // 32 bit machine, PC is stored in %eip register
93     unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_EIP];
94 #endif // REG_RIP for 64-bit machines
95 
96     Dl_info info;
97     if (dladdr(pc, &info)) { // Determine location of the segfault..
98         if (strstr(info.dli_fname, "libforestdb")) {
99             fprintf(stderr,
100                     "Caught SIGSEGV in libforestdb at %s\n", info.dli_sname);
101             // first restore original handler whatever it may be..
102             // so that if BREAKPAD is not available we get a core dump..
103             sigaction(SIGSEGV, &caller_sigact, NULL);
104             _dbg_hang_process();
105             initialize_breakpad(minidump_dir);
106             return; // let breakpad dump backtrace and crash..
107         }
108     }
109     // If not from forestdb, and caller has a signal handler, invoke it..
110     if (caller_sigact.sa_sigaction && caller_sigact.sa_flags & SA_SIGINFO) {
111         caller_sigact.sa_sigaction(sig, siginfo, context);
112     } else if (caller_sigact.sa_sigaction) { // Not in forestdb and no handler from caller
113         caller_sigact.sa_handler(sig);
114     } else {
115         // first restore original handler whatever it may be..
116         // so that if BREAKPAD is not available we get a core dump..
117         sigaction(SIGSEGV, &caller_sigact, NULL);
118         _dbg_hang_process();
119         initialize_breakpad(minidump_dir); // let breakpad handle it..
120     }
121 }
122 
_dbg_init_altstack(void)123 INLINE fdb_status _dbg_init_altstack(void)
124 {
125     __sigstack.ss_sp = malloc(SIGSTKSZ);
126     __sigstack.ss_size = SIGSTKSZ;
127     __sigstack.ss_flags = 0;
128     return FDB_RESULT_SUCCESS;
129 }
130 
_dbg_install_handler(void)131 fdb_status _dbg_install_handler(void)
132 {
133     // -- install segmentation fault handler using sigaction ---
134     struct sigaction sa;
135     if (sigaltstack(&__sigstack, NULL) == -1) {
136         fprintf(stderr, "SIGSEGV AltStack failed to register\n");
137         return FDB_RESULT_INVALID_ARGS;
138     }
139     sigemptyset(&sa.sa_mask);
140     sa.sa_sigaction = sigsegv_handler;
141     sa.sa_flags = SA_RESTART | SA_ONSTACK | SA_SIGINFO;
142     if (sigaction(SIGSEGV, &sa, &caller_sigact) == -1) {
143         fprintf(stderr, "SIGSEGV handler failed to register\n");
144         return FDB_RESULT_INVALID_ARGS;
145     }
146     return FDB_RESULT_SUCCESS;
147 }
148 
_dbg_destroy_altstack()149 fdb_status _dbg_destroy_altstack()
150 {
151     if (__sigstack.ss_sp) {
152         free(__sigstack.ss_sp);
153         __sigstack.ss_sp = NULL;
154     }
155     return FDB_RESULT_SUCCESS;
156 }
157 
158 # else
_dbg_init_altstack()159 fdb_status _dbg_init_altstack() { return FDB_RESULT_SUCCESS; }
_dbg_destroy_altstack()160 fdb_status _dbg_destroy_altstack() { return FDB_RESULT_SUCCESS; }
_dbg_install_handler()161 fdb_status _dbg_install_handler() { return FDB_RESULT_SUCCESS; }
162 #endif // #if defined(__linux__) && !defined(__ANDROID__)
163 
_dbg_handle_crashes(const char *pathname)164 fdb_status _dbg_handle_crashes(const char *pathname)
165 {
166     minidump_dir = pathname;
167     _dbg_init_altstack(); // one time stack install
168     return _dbg_install_handler();
169 }
170 
fdb_assert_die(const char* expression, const char* file, int line, uint64_t val, uint64_t expected)171 void fdb_assert_die(const char* expression, const char* file, int line,
172                     uint64_t val, uint64_t expected) {
173     fprintf(stderr, "assertion failed [%s] at %s:%u (%p != %p)\n",
174             expression, file, line, (void*)val, (void*)expected);
175 
176     // Invoke the fatal error callback if registered.
177     if (fatal_error_callback != nullptr) {
178         fatal_error_callback();
179     }
180 
181     _dbg_hang_process(); // Only if HANG_ON_CRASH is set in env
182 
183     // Initialize breakpad to create minidump for the
184     // following abort
185     initialize_breakpad(minidump_dir);
186 
187     fflush(stderr);
188 
189     abort();
190 }
191 
dbg_print_buf(void *buf, uint64_t buflen, bool hex, int align)192 void dbg_print_buf(void *buf, uint64_t buflen, bool hex, int align)
193 {
194     if (buf) {
195         if (!hex) {
196             // plaintext
197             fprintf(stderr, "%.*s\n", (int)buflen, (char*)buf);
198         } else {
199             // hex dump
200             size_t i, j;
201             fprintf(stderr, "(hex) 0x%" _X64 ", %" _F64 " (0x%" _X64 ") bytes\n",
202                     (uint64_t)buf, buflen, buflen);
203             for (i=0;i<buflen;i+=align) {
204                 fprintf(stderr, "   %04x   ", (int)i);
205                 for (j=i; j<i+align; ++j){
206                     if (j<buflen) {
207                         fprintf(stderr, "%02x ", ((uint8_t*)buf)[j]);
208                     } else {
209                         fprintf(stderr, "   ");
210                     }
211                     if ((j+1)%8 == 0) {
212                         fprintf(stderr, " ");
213                     }
214                 }
215                 fprintf(stderr, " ");
216                 for (j=i; j<i+align && j<buflen; ++j){
217                     // print only readable ascii character
218                     fprintf(stderr, "%c",
219                      (0x20 <= ((char*)buf)[j] && ((char*)buf)[j] <= 0x7d)?
220                                ((char*)buf)[j] : '.'  );
221                 }
222                 fprintf(stderr, "\n");
223             }
224         }
225     } else {
226         fprintf(stderr, "(null)\n");
227     }
228 }
229 
230 // LCOV_EXCL_STOP
231