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 |
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 | |
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 | |
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 | |
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)) |
145 | static void toku_do_backtrace_abort(void) { |
146 | db_env_do_backtrace(stderr); |
147 | abort(); |
148 | } |
149 | |
150 | |
151 | static void |
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 |
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 |
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 |
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 |
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 | |