xref: /6.6.0/forestdb/utils/debug.cc (revision effd4872)
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
12static uint8_t _global_dbg_switch[N_DBG_SWITCH];
13static void* _global_dbg_addr[N_DBG_SWITCH];
14static uint64_t _global_dbg_uint64_t[N_DBG_SWITCH];
15
16fdb_fatal_error_callback fatal_error_callback = nullptr;
17
18// minidump_dir used by breakpad
19static const char* minidump_dir = nullptr;
20
21// LCOV_EXCL_START
22void _dbg_sw_set(int n)
23{
24    _global_dbg_switch[n] = 1;
25}
26
27void _dbg_sw_clear(int n)
28{
29    _global_dbg_switch[n] = 0;
30}
31
32void _dbg_set_addr(int n, void *addr)
33{
34    _global_dbg_addr[n] = addr;
35}
36
37void * _dbg_get_addr(int n)
38{
39    return _global_dbg_addr[n];
40}
41
42void _dbg_set_uint64_t(int n, uint64_t val)
43{
44    _global_dbg_uint64_t[n] = val;
45}
46
47uint64_t _dbg_get_uint64_t(int n)
48{
49    return _global_dbg_uint64_t[n];
50}
51
52int _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
58static 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
81void sigalrm_handler(int sig) {
82    fdb_assert(false, false, false);
83}
84
85static struct sigaction caller_sigact;
86static stack_t __sigstack;
87static 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
123INLINE 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
131fdb_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
149fdb_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
159fdb_status _dbg_init_altstack() { return FDB_RESULT_SUCCESS; }
160fdb_status _dbg_destroy_altstack() { return FDB_RESULT_SUCCESS; }
161fdb_status _dbg_install_handler() { return FDB_RESULT_SUCCESS; }
162#endif // #if defined(__linux__) && !defined(__ANDROID__)
163
164fdb_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
171void 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
192void 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