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