xref: /6.0.3/forestdb/utils/debug.cc (revision e984c92f)
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 *u = (ucontext *)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    free(__sigstack.ss_sp);
152    return FDB_RESULT_SUCCESS;
153}
154
155# else
156fdb_status _dbg_init_altstack() { return FDB_RESULT_SUCCESS; }
157fdb_status _dbg_destroy_altstack() { return FDB_RESULT_SUCCESS; }
158fdb_status _dbg_install_handler() { return FDB_RESULT_SUCCESS; }
159#endif // #if defined(__linux__) && !defined(__ANDROID__)
160
161fdb_status _dbg_handle_crashes(const char *pathname)
162{
163    minidump_dir = pathname;
164    _dbg_init_altstack(); // one time stack install
165    return _dbg_install_handler();
166}
167
168void fdb_assert_die(const char* expression, const char* file, int line,
169                    uint64_t val, uint64_t expected) {
170    fprintf(stderr, "assertion failed [%s] at %s:%u (%p != %p)\n",
171            expression, file, line, (void*)val, (void*)expected);
172
173    // Invoke the fatal error callback if registered.
174    if (fatal_error_callback != nullptr) {
175        fatal_error_callback();
176    }
177
178    _dbg_hang_process(); // Only if HANG_ON_CRASH is set in env
179
180    // Initialize breakpad to create minidump for the
181    // following abort
182    initialize_breakpad(minidump_dir);
183
184    fflush(stderr);
185
186    abort();
187}
188
189void dbg_print_buf(void *buf, uint64_t buflen, bool hex, int align)
190{
191    if (buf) {
192        if (!hex) {
193            // plaintext
194            fprintf(stderr, "%.*s\n", (int)buflen, (char*)buf);
195        } else {
196            // hex dump
197            size_t i, j;
198            fprintf(stderr, "(hex) 0x%" _X64 ", %" _F64 " (0x%" _X64 ") bytes\n",
199                    (uint64_t)buf, buflen, buflen);
200            for (i=0;i<buflen;i+=align) {
201                fprintf(stderr, "   %04x   ", (int)i);
202                for (j=i; j<i+align; ++j){
203                    if (j<buflen) {
204                        fprintf(stderr, "%02x ", ((uint8_t*)buf)[j]);
205                    } else {
206                        fprintf(stderr, "   ");
207                    }
208                    if ((j+1)%8 == 0) {
209                        fprintf(stderr, " ");
210                    }
211                }
212                fprintf(stderr, " ");
213                for (j=i; j<i+align && j<buflen; ++j){
214                    // print only readable ascii character
215                    fprintf(stderr, "%c",
216                     (0x20 <= ((char*)buf)[j] && ((char*)buf)[j] <= 0x7d)?
217                               ((char*)buf)[j] : '.'  );
218                }
219                fprintf(stderr, "\n");
220            }
221        }
222    } else {
223        fprintf(stderr, "(null)\n");
224    }
225}
226
227// LCOV_EXCL_STOP
228