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/*======
5This file is part of PerconaFT.
6
7
8Copyright (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
56static void *backtrace_pointers[N_POINTERS];
57
58static uint64_t engine_status_num_rows = 0;
59
60typedef void (*malloc_stats_fun_t)(void);
61static malloc_stats_fun_t malloc_stats_f;
62
63void
64toku_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.
70static int (*toku_maybe_get_engine_status_text_p)(char* buff, int buffsize) = 0;
71static int (*toku_maybe_err_engine_status_p)(void) = 0;
72static void (*toku_maybe_set_env_panic_p)(int code, const char* msg) = 0;
73
74void 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
84bool toku_gdb_dump_on_assert = false;
85void (*do_assert_hook)(void) = NULL;
86
87void 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
115void 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))
145static void toku_do_backtrace_abort(void) {
146 db_env_do_backtrace(stderr);
147 abort();
148}
149
150
151static void
152set_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
162void
163toku_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
171void
172toku_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
180void
181toku_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
189void
190toku_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