1 /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 // vim: ft=cpp:expandtab:ts=8:sw=4:softtabstop=4:
3 #ident "$Id$"
4 /*======
5 This file is part of PerconaFT.
6 
7 
8 Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved.
9 
10     PerconaFT is free software: you can redistribute it and/or modify
11     it under the terms of the GNU General Public License, version 2,
12     as published by the Free Software Foundation.
13 
14     PerconaFT is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
21 
22 ----------------------------------------
23 
24     PerconaFT is free software: you can redistribute it and/or modify
25     it under the terms of the GNU Affero General Public License, version 3,
26     as published by the Free Software Foundation.
27 
28     PerconaFT is distributed in the hope that it will be useful,
29     but WITHOUT ANY WARRANTY; without even the implied warranty of
30     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
31     GNU Affero General Public License for more details.
32 
33     You should have received a copy of the GNU Affero General Public License
34     along with PerconaFT.  If not, see <http://www.gnu.org/licenses/>.
35 ======= */
36 
37 #ident "Copyright (c) 2006, 2015, Percona and/or its affiliates. All rights reserved."
38 
39 #include <portability/toku_config.h>
40 
41 #include <toku_portability.h>
42 #include "toku_assert.h"
43 
44 #include <stdlib.h>
45 #include <stdio.h>
46 #if defined(HAVE_MALLOC_H)
47 # include <malloc.h>
48 #elif defined(HAVE_SYS_MALLOC_H)
49 # include <sys/malloc.h>
50 #endif
51 #include <dlfcn.h>
52 #include <execinfo.h>
53 
54 // These are statically allocated so that the backtrace can run without any calls to malloc()
55 #define N_POINTERS 1000
56 static void *backtrace_pointers[N_POINTERS];
57 
58 static uint64_t engine_status_num_rows = 0;
59 
60 typedef void (*malloc_stats_fun_t)(void);
61 static malloc_stats_fun_t malloc_stats_f;
62 
63 void
toku_assert_init(void)64 toku_assert_init(void)
65 {
66     malloc_stats_f = (malloc_stats_fun_t) dlsym(RTLD_DEFAULT, "malloc_stats");
67 }
68 
69 // Function pointers are zero by default so asserts can be used by ft-layer tests without an environment.
70 static int (*toku_maybe_get_engine_status_text_p)(char* buff, int buffsize) = 0;
71 static int (*toku_maybe_err_engine_status_p)(void) = 0;
72 static void (*toku_maybe_set_env_panic_p)(int code, const char* msg) = 0;
73 
toku_assert_set_fpointers(int (* toku_maybe_get_engine_status_text_pointer)(char *,int),int (* toku_maybe_err_engine_status_pointer)(void),void (* toku_maybe_set_env_panic_pointer)(int,const char *),uint64_t num_rows)74 void toku_assert_set_fpointers(int (*toku_maybe_get_engine_status_text_pointer)(char*, int),
75                                int (*toku_maybe_err_engine_status_pointer)(void),
76 			       void (*toku_maybe_set_env_panic_pointer)(int, const char*),
77                                uint64_t num_rows) {
78     toku_maybe_get_engine_status_text_p = toku_maybe_get_engine_status_text_pointer;
79     toku_maybe_err_engine_status_p = toku_maybe_err_engine_status_pointer;
80     toku_maybe_set_env_panic_p = toku_maybe_set_env_panic_pointer;
81     engine_status_num_rows = num_rows;
82 }
83 
84 bool toku_gdb_dump_on_assert = false;
85 void (*do_assert_hook)(void) = NULL;
86 
db_env_do_backtrace_errfunc(toku_env_err_func errfunc,const void * env)87 void db_env_do_backtrace_errfunc(toku_env_err_func errfunc, const void *env) {
88     // backtrace
89     int n = backtrace(backtrace_pointers, N_POINTERS);
90     errfunc(env, 0, "Backtrace: (Note: toku_do_assert=0x%p)\n", toku_do_assert);
91     char **syms = backtrace_symbols(backtrace_pointers, n);
92     if (syms) {
93         for (char **symstr = syms; symstr != NULL && (symstr - syms) < n; ++symstr) {
94             errfunc(env, 0, *symstr);
95         }
96         free(syms);
97     }
98 
99     if (engine_status_num_rows && toku_maybe_err_engine_status_p) {
100 	toku_maybe_err_engine_status_p();
101     } else {
102 	errfunc(env, 0, "Engine status function not available\n");
103     }
104     errfunc(env, 0, "Memory usage:\n");
105     if (malloc_stats_f) {
106         malloc_stats_f();
107     }
108 
109     if (do_assert_hook) do_assert_hook();
110     if (toku_gdb_dump_on_assert) {
111         toku_try_gdb_stack_trace(nullptr);
112     }
113 }
114 
db_env_do_backtrace(FILE * outf)115 void db_env_do_backtrace(FILE *outf) {
116     // backtrace
117     int n = backtrace(backtrace_pointers, N_POINTERS);
118     fprintf(outf, "Backtrace: (Note: toku_do_assert=0x%p)\n", toku_do_assert); fflush(outf);
119     backtrace_symbols_fd(backtrace_pointers, n, fileno(outf));
120 
121     fflush(outf);
122 
123     if (engine_status_num_rows && toku_maybe_get_engine_status_text_p) {
124 	int buffsize = engine_status_num_rows * 128;  // assume 128 characters per row (gross overestimate, should be safe)
125 	char buff[buffsize];
126 	toku_maybe_get_engine_status_text_p(buff, buffsize);
127 	fprintf(outf, "Engine status:\n%s\n", buff);
128     } else {
129 	fprintf(outf, "Engine status function not available\n");
130     }
131     fprintf(outf, "Memory usage:\n");
132     fflush(outf);	    // just in case malloc_stats() crashes, we still want engine status (and to know that malloc_stats() failed)
133     if (malloc_stats_f) {
134         malloc_stats_f();
135     }
136     fflush(outf);
137 
138     if (do_assert_hook) do_assert_hook();
139     if (toku_gdb_dump_on_assert) {
140         toku_try_gdb_stack_trace(nullptr);
141     }
142 }
143 
144 __attribute__((noreturn))
toku_do_backtrace_abort(void)145 static void toku_do_backtrace_abort(void) {
146     db_env_do_backtrace(stderr);
147     abort();
148 }
149 
150 
151 static void
set_panic_if_not_panicked(int caller_errno,char * msg)152 set_panic_if_not_panicked(int caller_errno, char * msg) {
153     int code = caller_errno ? caller_errno : -1;
154     if (toku_maybe_set_env_panic_p) {
155 	toku_maybe_set_env_panic_p(code, msg);
156     }
157 }
158 
159 
160 #define MSGLEN 1024
161 
162 void
toku_do_assert_fail(const char * expr_as_string,const char * function,const char * file,int line,int caller_errno)163 toku_do_assert_fail (const char *expr_as_string, const char *function, const char *file, int line, int caller_errno) {
164     char msg[MSGLEN];
165     snprintf(msg, MSGLEN, "%s:%d %s: Assertion `%s' failed (errno=%d)\n", file, line, function, expr_as_string, caller_errno);
166     perror(msg);
167     set_panic_if_not_panicked(caller_errno, msg);
168     toku_do_backtrace_abort();
169 }
170 
171 void
toku_do_assert_zero_fail(uintptr_t expr,const char * expr_as_string,const char * function,const char * file,int line,int caller_errno)172 toku_do_assert_zero_fail (uintptr_t expr, const char *expr_as_string, const char *function, const char *file, int line, int caller_errno) {
173     char msg[MSGLEN];
174     snprintf(msg, MSGLEN, "%s:%d %s: Assertion `%s == 0' failed (errno=%d) (%s=%" PRIuPTR ")\n", file, line, function, expr_as_string, caller_errno, expr_as_string, expr);
175     perror(msg);
176     set_panic_if_not_panicked(caller_errno, msg);
177     toku_do_backtrace_abort();
178 }
179 
180 void
toku_do_assert_expected_fail(uintptr_t expr,uintptr_t expected,const char * expr_as_string,const char * function,const char * file,int line,int caller_errno)181 toku_do_assert_expected_fail (uintptr_t expr, uintptr_t expected, const char *expr_as_string, const char *function, const char *file, int line, int caller_errno) {
182     char msg[MSGLEN];
183     snprintf(msg, MSGLEN, "%s:%d %s: Assertion `%s == %" PRIuPTR "' failed (errno=%d) (%s=%" PRIuPTR ")\n", file, line, function, expr_as_string, expected, caller_errno, expr_as_string, expr);
184     perror(msg);
185     set_panic_if_not_panicked(caller_errno, msg);
186     toku_do_backtrace_abort();
187 }
188 
189 void
toku_do_assert(int expr,const char * expr_as_string,const char * function,const char * file,int line,int caller_errno)190 toku_do_assert(int expr, const char *expr_as_string, const char *function, const char* file, int line, int caller_errno) {
191     if (expr == 0)
192         toku_do_assert_fail(expr_as_string, function, file, line, caller_errno);
193 }
194 
195