xref: /6.0.3/forestdb/utils/debug.cc (revision d18182a1)
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// to profile first install perf
58// echo 0 > /proc/sys/kernel/kptr_restrict
59#if defined(__linux__) && !defined(__ANDROID__)
60#include <string.h>
61#include <dlfcn.h>
62#include <ucontext.h>
63#include <unistd.h>
64#include <sys/types.h>
65#include <sys/stat.h>
66#include <signal.h>
67
68void sigalrm_handler(int sig) {
69    fdb_assert(false, false, false);
70}
71
72static struct sigaction caller_sigact;
73static stack_t __sigstack;
74static void sigsegv_handler(int sig, siginfo_t *siginfo, void *context)
75{
76    ucontext *u = (ucontext *)context;
77#ifdef REG_RIP // Test if the Program Counter is 64 bits
78    unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_RIP];
79#else // 32 bit machine, PC is stored in %eip register
80    unsigned char *pc = (unsigned char *)u->uc_mcontext.gregs[REG_EIP];
81#endif // REG_RIP for 64-bit machines
82
83    Dl_info info;
84    if (dladdr(pc, &info)) { // Determine location of the segfault..
85        if (strstr(info.dli_fname, "libforestdb")) {
86            fprintf(stderr,
87                    "Caught SIGSEGV in libforestdb at %s\n", info.dli_sname);
88            // first restore original handler whatever it may be..
89            // so that if BREAKPAD is not available we get a core dump..
90            sigaction(SIGSEGV, &caller_sigact, NULL);
91            initialize_breakpad(minidump_dir);
92            return; // let breakpad dump backtrace and crash..
93        }
94    }
95    // If not from forestdb, and caller has a signal handler, invoke it..
96    if (caller_sigact.sa_sigaction && caller_sigact.sa_flags & SA_SIGINFO) {
97        caller_sigact.sa_sigaction(sig, siginfo, context);
98    } else if (caller_sigact.sa_sigaction) { // Not in forestdb and no handler from caller
99        caller_sigact.sa_handler(sig);
100    } else {
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        initialize_breakpad(minidump_dir); // let breakpad handle it..
105    }
106}
107
108INLINE fdb_status _dbg_init_altstack(void)
109{
110    __sigstack.ss_sp = malloc(SIGSTKSZ);
111    __sigstack.ss_size = SIGSTKSZ;
112    __sigstack.ss_flags = 0;
113    return FDB_RESULT_SUCCESS;
114}
115
116fdb_status _dbg_install_handler(void)
117{
118    // -- install segmentation fault handler using sigaction ---
119    struct sigaction sa;
120    if (sigaltstack(&__sigstack, NULL) == -1) {
121        fprintf(stderr, "AltStack failed to register\n");
122        return FDB_RESULT_INVALID_ARGS;
123    }
124    sigemptyset(&sa.sa_mask);
125    sa.sa_sigaction = sigsegv_handler;
126    sa.sa_flags = SA_RESTART | SA_ONSTACK | SA_SIGINFO;
127    if (sigaction(SIGSEGV, &sa, &caller_sigact) == -1) {
128        fprintf(stderr, "SIGSEGV handler failed to register\n");
129        return FDB_RESULT_INVALID_ARGS;
130    }
131    return FDB_RESULT_SUCCESS;
132}
133
134fdb_status _dbg_destroy_altstack()
135{
136    free(__sigstack.ss_sp);
137    return FDB_RESULT_SUCCESS;
138}
139
140# else
141fdb_status _dbg_init_altstack() { return FDB_RESULT_SUCCESS; }
142fdb_status _dbg_destroy_altstack() { return FDB_RESULT_SUCCESS; }
143fdb_status _dbg_install_handler() { return FDB_RESULT_SUCCESS; }
144#endif // #if defined(__linux__) && !defined(__ANDROID__)
145
146fdb_status _dbg_handle_crashes(const char *pathname)
147{
148    minidump_dir = pathname;
149    _dbg_init_altstack(); // one time stack install
150    return _dbg_install_handler();
151}
152
153void fdb_assert_die(const char* expression, const char* file, int line,
154                    uint64_t val, uint64_t expected) {
155    fprintf(stderr, "assertion failed [%s] at %s:%u (%p != %p)\n",
156            expression, file, line, (void*)val, (void*)expected);
157
158    // Invoke the fatal error callback if registered.
159    if (fatal_error_callback != nullptr) {
160        fatal_error_callback();
161    }
162
163    char *hang_process = getenv("HANG_ON_ASSERTION");
164    if (hang_process) {
165        fprintf(stderr, "Hanging process...");
166        fprintf(stderr, "\n");
167        while (1) {
168            usleep(1000);
169        }
170    }
171
172    // Initialize breakpad to create minidump for the
173    // following abort
174    initialize_breakpad(minidump_dir);
175
176    fflush(stderr);
177
178    abort();
179}
180
181void dbg_print_buf(void *buf, uint64_t buflen, bool hex, int align)
182{
183    if (buf) {
184        if (!hex) {
185            // plaintext
186            fprintf(stderr, "%.*s\n", (int)buflen, (char*)buf);
187        } else {
188            // hex dump
189            size_t i, j;
190            fprintf(stderr, "(hex) 0x%" _X64 ", %" _F64 " (0x%" _X64 ") bytes\n",
191                    (uint64_t)buf, buflen, buflen);
192            for (i=0;i<buflen;i+=align) {
193                fprintf(stderr, "   %04x   ", (int)i);
194                for (j=i; j<i+align; ++j){
195                    if (j<buflen) {
196                        fprintf(stderr, "%02x ", ((uint8_t*)buf)[j]);
197                    } else {
198                        fprintf(stderr, "   ");
199                    }
200                    if ((j+1)%8 == 0) {
201                        fprintf(stderr, " ");
202                    }
203                }
204                fprintf(stderr, " ");
205                for (j=i; j<i+align && j<buflen; ++j){
206                    // print only readable ascii character
207                    fprintf(stderr, "%c",
208                     (0x20 <= ((char*)buf)[j] && ((char*)buf)[j] <= 0x7d)?
209                               ((char*)buf)[j] : '.'  );
210                }
211                fprintf(stderr, "\n");
212            }
213        }
214    } else {
215        fprintf(stderr, "(null)\n");
216    }
217}
218
219// LCOV_EXCL_STOP
220