/src/mysql-server/include/my_dbug.h
Line | Count | Source |
1 | | /* Copyright (c) 2000, 2025, Oracle and/or its affiliates. |
2 | | |
3 | | This program is free software; you can redistribute it and/or modify |
4 | | it under the terms of the GNU General Public License, version 2.0, |
5 | | as published by the Free Software Foundation. |
6 | | |
7 | | This program is designed to work with certain software (including |
8 | | but not limited to OpenSSL) that is licensed under separate terms, |
9 | | as designated in a particular file or component or in included license |
10 | | documentation. The authors of MySQL hereby grant you an additional |
11 | | permission to link the program and your derivative works with the |
12 | | separately licensed software that they have either included with |
13 | | the program or referenced in the documentation. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, |
16 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | | GNU General Public License, version 2.0, for more details. |
19 | | |
20 | | You should have received a copy of the GNU General Public License |
21 | | along with this program; if not, write to the Free Software |
22 | | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
23 | | |
24 | | #ifndef MY_DBUG_INCLUDED |
25 | | #define MY_DBUG_INCLUDED |
26 | | |
27 | | /** |
28 | | @file include/my_dbug.h |
29 | | */ |
30 | | |
31 | | #ifdef MY_MSCRT_DEBUG |
32 | | #include <crtdbg.h> |
33 | | #endif |
34 | | #include <stdlib.h> |
35 | | |
36 | | #include "my_compiler.h" |
37 | | |
38 | | #include <string.h> |
39 | | |
40 | | #if !defined(NDEBUG) |
41 | | #include <assert.h> // IWYU pragma: keep |
42 | | #include <stdio.h> |
43 | | #endif |
44 | | |
45 | | /** |
46 | | Calls our own implementation of abort, if specified, or std's abort(). |
47 | | */ |
48 | | [[noreturn]] void my_abort(); |
49 | | /** |
50 | | Sets a new function to be called on my_abort(). |
51 | | |
52 | | @param new_my_abort_func pointer to a new my_abort function. It can't be |
53 | | [[noreturn]] as pointers to methods can't have attributes. |
54 | | */ |
55 | | void set_my_abort(void (*new_my_abort_func)()); |
56 | | |
57 | | #if !defined(NDEBUG) |
58 | | |
59 | | struct _db_stack_frame_ { |
60 | | const char *func; /* function name of the previous stack frame */ |
61 | | int func_len; /* how much to print from func */ |
62 | | const char *file; /* filename of the function of previous frame */ |
63 | | unsigned int level; /* this nesting level, highest bit enables tracing */ |
64 | | struct _db_stack_frame_ *prev; /* pointer to the previous frame */ |
65 | | }; |
66 | | |
67 | | struct CODE_STATE; |
68 | | |
69 | | extern int _db_keyword_(struct CODE_STATE *, const char *, int); |
70 | | extern int _db_explain_(struct CODE_STATE *cs, char *buf, size_t len); |
71 | | extern int _db_explain_init_(char *buf, size_t len); |
72 | | extern int _db_is_pushed_(void); |
73 | | extern void _db_process_(const char *name); |
74 | | extern void _db_push_(const char *control); |
75 | | extern void _db_pop_(void); |
76 | | extern void _db_set_(const char *control); |
77 | | extern void _db_set_init_(const char *control); |
78 | | extern void _db_enter_(const char *_func_, int func_len, const char *_file_, |
79 | | unsigned int _line_, |
80 | | struct _db_stack_frame_ *_stack_frame_); |
81 | | extern void _db_return_(unsigned int _line_, |
82 | | struct _db_stack_frame_ *_stack_frame_); |
83 | | extern void _db_pargs_(unsigned int _line_, const char *keyword); |
84 | | extern int _db_enabled_(); |
85 | | extern void _db_doprnt_(const char *format, ...) |
86 | | MY_ATTRIBUTE((format(printf, 1, 2))); |
87 | | extern void _db_dump_(unsigned int _line_, const char *keyword, |
88 | | const unsigned char *memory, size_t length); |
89 | | extern void _db_end_(void); |
90 | | extern void _db_lock_file_(void); |
91 | | extern void _db_unlock_file_(void); |
92 | | extern FILE *_db_fp_(void); |
93 | | extern void _db_flush_(); |
94 | | |
95 | | #ifdef __cplusplus |
96 | | |
97 | | #if defined(__GNUC__) |
98 | | // GCC, Clang, and compatible compilers. |
99 | | #define DBUG_PRETTY_FUNCTION __PRETTY_FUNCTION__ |
100 | | #elif defined(__FUNCSIG__) |
101 | | // For MSVC; skips the __cdecl. (__PRETTY_FUNCTION__ in GCC is not a |
102 | | // preprocessor constant, but __FUNCSIG__ in MSVC is.) |
103 | | #define DBUG_PRETTY_FUNCTION strchr(__FUNCSIG__, ' ') + 1 |
104 | | #else |
105 | | // Standard C++; does not include the class name. |
106 | | #define DBUG_PRETTY_FUNCTION __func__ |
107 | | #endif |
108 | | |
109 | | /** |
110 | | A RAII helper to do DBUG_ENTER / DBUG_RETURN for you automatically. Use like |
111 | | this: |
112 | | |
113 | | int foo() { |
114 | | DBUG_TRACE; |
115 | | return 42; |
116 | | } |
117 | | */ |
118 | | class AutoDebugTrace { |
119 | | public: |
120 | | AutoDebugTrace(const char *function, const char *filename, int line) { |
121 | | // Remove the return type, if it's there. |
122 | | const char *begin = strchr(function, ' '); |
123 | | if (begin != nullptr) { |
124 | | function = begin + 1; |
125 | | } |
126 | | |
127 | | // Cut it off at the first parenthesis; the argument list is |
128 | | // often too long to be interesting. |
129 | | const char *end = strchr(function, '('); |
130 | | |
131 | | if (end == nullptr) { |
132 | | _db_enter_(function, static_cast<int>(strlen(function)), filename, line, |
133 | | &m_stack_frame); |
134 | | } else { |
135 | | _db_enter_(function, static_cast<int>(end - function), filename, line, |
136 | | &m_stack_frame); |
137 | | } |
138 | | } |
139 | | |
140 | | ~AutoDebugTrace() { _db_return_(0, &m_stack_frame); } |
141 | | |
142 | | private: |
143 | | _db_stack_frame_ m_stack_frame; |
144 | | }; |
145 | | |
146 | | #define DBUG_TRACE \ |
147 | | const AutoDebugTrace _db_trace(DBUG_PRETTY_FUNCTION, __FILE__, __LINE__) |
148 | | |
149 | | #endif |
150 | | |
151 | | #define DBUG_ENTER(a) \ |
152 | | struct _db_stack_frame_ _db_stack_frame_; \ |
153 | | _db_enter_(a, ::strlen(a), __FILE__, __LINE__, &_db_stack_frame_) |
154 | | |
155 | | #define DBUG_RETURN(a1) \ |
156 | | do { \ |
157 | | _db_return_(__LINE__, &_db_stack_frame_); \ |
158 | | return (a1); \ |
159 | | } while (0) |
160 | | #define DBUG_VOID_RETURN \ |
161 | | do { \ |
162 | | _db_return_(__LINE__, &_db_stack_frame_); \ |
163 | | return; \ |
164 | | } while (0) |
165 | | #define DBUG_EXECUTE(keyword, a1) \ |
166 | | do { \ |
167 | | if (_db_keyword_(nullptr, (keyword), 0)) { \ |
168 | | a1 \ |
169 | | } \ |
170 | | } while (0) |
171 | | #define DBUG_EXECUTE_IF(keyword, a1) \ |
172 | | do { \ |
173 | | if (_db_keyword_(nullptr, (keyword), 1)) { \ |
174 | | a1 \ |
175 | | } \ |
176 | | } while (0) |
177 | | #define DBUG_EVALUATE(keyword, a1, a2) \ |
178 | | (_db_keyword_(nullptr, (keyword), 0) ? (a1) : (a2)) |
179 | | #define DBUG_EVALUATE_IF(keyword, a1, a2) \ |
180 | | (_db_keyword_(nullptr, (keyword), 1) ? (a1) : (a2)) |
181 | | #define DBUG_PRINT(keyword, arglist) \ |
182 | | do { \ |
183 | | _db_pargs_(__LINE__, keyword); \ |
184 | | if (_db_enabled_()) { \ |
185 | | _db_doprnt_ arglist; \ |
186 | | } \ |
187 | | } while (0) |
188 | | |
189 | | #define DBUG_PUSH(a1) _db_push_(a1) |
190 | | #define DBUG_POP() _db_pop_() |
191 | | #define DBUG_SET(a1) _db_set_(a1) |
192 | | #define DBUG_SET_INITIAL(a1) _db_set_init_(a1) |
193 | | #define DBUG_PROCESS(a1) _db_process_(a1) |
194 | | #define DBUG_FILE _db_fp_() |
195 | | #define DBUG_DUMP(keyword, a1, a2) _db_dump_(__LINE__, keyword, a1, a2) |
196 | | #define DBUG_END() _db_end_() |
197 | | #define DBUG_LOCK_FILE _db_lock_file_() |
198 | | #define DBUG_UNLOCK_FILE _db_unlock_file_() |
199 | | #define DBUG_EXPLAIN(buf, len) _db_explain_(nullptr, (buf), (len)) |
200 | | #define DBUG_EXPLAIN_INITIAL(buf, len) _db_explain_init_((buf), (len)) |
201 | | #ifndef _WIN32 |
202 | | #define DBUG_ABORT() (_db_flush_(), my_abort()) |
203 | | #define DBUG_EXIT() (_db_flush_(), exit(2)) |
204 | | #else |
205 | | #include <crtdbg.h> |
206 | | |
207 | | #define DBUG_ABORT() \ |
208 | | (_db_flush_(), (void)_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE), \ |
209 | | (void)_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR), my_abort()) |
210 | | #define DBUG_EXIT() \ |
211 | | (_db_flush_(), (void)_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE), \ |
212 | | (void)_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR), _exit(2)) |
213 | | #endif |
214 | | |
215 | | /* |
216 | | Make the program fail, without creating a core file. |
217 | | abort() will send SIGABRT which (most likely) generates core. |
218 | | Use SIGKILL instead, which cannot be caught. |
219 | | We also pause the current thread, until the signal is actually delivered. |
220 | | An alternative would be to use _exit(EXIT_FAILURE), |
221 | | but then valgrind would report lots of memory leaks. |
222 | | */ |
223 | | #ifdef _WIN32 |
224 | | #define DBUG_SUICIDE() DBUG_EXIT() |
225 | | #else |
226 | | [[noreturn]] extern void _db_suicide_(); |
227 | | extern void _db_flush_gcov_(); |
228 | | #define DBUG_SUICIDE() (_db_flush_(), _db_suicide_()) |
229 | | #endif |
230 | | |
231 | | #else /* No debugger */ |
232 | | |
233 | | #ifdef __cplusplus |
234 | | #define DBUG_TRACE \ |
235 | 383k | do { \ |
236 | 383k | } while (false) |
237 | | #endif |
238 | | #define DBUG_ENTER(a1) |
239 | | #define DBUG_RETURN(a1) \ |
240 | | do { \ |
241 | | return (a1); \ |
242 | | } while (0) |
243 | | #define DBUG_VOID_RETURN \ |
244 | | do { \ |
245 | | return; \ |
246 | | } while (0) |
247 | | #define DBUG_EXECUTE(keyword, a1) \ |
248 | | do { \ |
249 | | } while (0) |
250 | | #define DBUG_EXECUTE_IF(keyword, a1) \ |
251 | 542k | do { \ |
252 | 542k | } while (0) |
253 | | #define DBUG_EVALUATE(keyword, a1, a2) (a2) |
254 | | #define DBUG_EVALUATE_IF(keyword, a1, a2) (a2) |
255 | | #define DBUG_PRINT(keyword, arglist) \ |
256 | 204k | do { \ |
257 | 204k | } while (0) |
258 | | #define DBUG_PUSH(a1) \ |
259 | | do { \ |
260 | | } while (0) |
261 | | #define DBUG_SET(a1) \ |
262 | | do { \ |
263 | | } while (0) |
264 | | #define DBUG_SET_INITIAL(a1) \ |
265 | | do { \ |
266 | | } while (0) |
267 | | #define DBUG_POP() \ |
268 | | do { \ |
269 | | } while (0) |
270 | | #define DBUG_PROCESS(a1) \ |
271 | 1 | do { \ |
272 | 1 | } while (0) |
273 | | #define DBUG_DUMP(keyword, a1, a2) \ |
274 | | do { \ |
275 | | } while (0) |
276 | | #define DBUG_END() \ |
277 | 0 | do { \ |
278 | 0 | } while (0) |
279 | | #define DBUG_LOCK_FILE \ |
280 | | do { \ |
281 | | } while (0) |
282 | 0 | #define DBUG_FILE (stderr) |
283 | | #define DBUG_UNLOCK_FILE \ |
284 | | do { \ |
285 | | } while (0) |
286 | | #define DBUG_EXPLAIN(buf, len) |
287 | | #define DBUG_EXPLAIN_INITIAL(buf, len) |
288 | | #define DBUG_ABORT() \ |
289 | | do { \ |
290 | | } while (0) |
291 | | #define DBUG_SUICIDE() \ |
292 | | do { \ |
293 | | } while (0) |
294 | | |
295 | | #endif |
296 | | |
297 | | #ifdef __cplusplus |
298 | | #if !defined(NDEBUG) |
299 | | #include <sstream> |
300 | | #include <string> |
301 | | |
302 | | /* |
303 | | A C++ interface to the DBUG_PRINT macro. The DBUG_LOG macro takes two |
304 | | arguments. The first argument is the keyword, as that of the |
305 | | DBUG_PRINT. The 2nd argument 'v' will be passed to a C++ output stream. |
306 | | This enables the use of C++ style output stream operator. In the code, it |
307 | | will be used as follows: |
308 | | |
309 | | DBUG_LOG("blob", "space: " << space_id); |
310 | | |
311 | | Note: DBUG_PRINT() has a limitation of 1024 bytes for a single |
312 | | print out. So, this limitation is there for DBUG_LOG macro also. |
313 | | */ |
314 | | |
315 | | #define DBUG_LOG(keyword, v) \ |
316 | | do { \ |
317 | | _db_pargs_(__LINE__, keyword); \ |
318 | | if (_db_enabled_()) { \ |
319 | | std::ostringstream sout; \ |
320 | | sout << v; \ |
321 | | DBUG_PRINT(keyword, ("%s", sout.str().c_str())); \ |
322 | | } \ |
323 | | } while (0) |
324 | | |
325 | | /** |
326 | | Shortcut for printing a variable name and its value in DBUG_LOG output. |
327 | | |
328 | | Use like: |
329 | | |
330 | | DBUG_LOG("info", DBUG_VAR(i) << " " << DBUG_VAR(thd->query)); |
331 | | |
332 | | Example output for the above might be: |
333 | | |
334 | | i=[4711] thd->query=[INSERT INTO t VALUES (1)] |
335 | | */ |
336 | | #define DBUG_VAR(v) #v << "=[" << (v) << "]" |
337 | | |
338 | | #else /* NDEBUG */ |
339 | | #define DBUG_LOG(keyword, v) \ |
340 | | do { \ |
341 | | } while (0) |
342 | | #define DBUG_VAR(v) "" |
343 | | #endif /* NDEBUG */ |
344 | | |
345 | | /** |
346 | | A type-safe interface to DBUG_EXECUTE_IF, where the debug action to |
347 | | activate when the keyword is provided is given as a callable object |
348 | | (typically a lambda). |
349 | | |
350 | | @note The body of the callable will be checked by the compiler even |
351 | | in optimized mode. |
352 | | |
353 | | @param keyword String literal which will enable this debug action. |
354 | | @param clos Callable object taking no arguments which will be |
355 | | called in debug mode if the keyword is enabled. |
356 | | */ |
357 | | template <class DBGCLOS> |
358 | | inline void dbug(const char *keyword [[maybe_unused]], |
359 | 0 | DBGCLOS &&clos [[maybe_unused]]) { |
360 | 0 | DBUG_EXECUTE_IF(keyword, clos();); |
361 | 0 | } Unexecuted instantiation: my_file.cc:void dbug<file_info::RegisterFilename(int, char const*, file_info::OpenType)::$_0>(char const*, file_info::RegisterFilename(int, char const*, file_info::OpenType)::$_0&&) Unexecuted instantiation: my_file.cc:void dbug<file_info::UnregisterFilename(int)::$_0>(char const*, file_info::UnregisterFilename(int)::$_0&&) Unexecuted instantiation: my_file.cc:void dbug<file_info::UnregisterFilename(int)::$_1>(char const*, file_info::UnregisterFilename(int)::$_1&&) Unexecuted instantiation: my_file.cc:void dbug<file_info::UnregisterFilename(int)::$_2>(char const*, file_info::UnregisterFilename(int)::$_2&&) |
362 | | |
363 | | #endif /* __cplusplus */ |
364 | | #endif /* MY_DBUG_INCLUDED */ |