| /* Declarations for debug printing functions. | |
| Copyright (C) 2014-2024 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| #ifndef GDBSUPPORT_COMMON_DEBUG_H | |
| #define GDBSUPPORT_COMMON_DEBUG_H | |
| #include <optional> | |
| #include "gdbsupport/preprocessor.h" | |
| #include <stdarg.h> | |
| /* Set to true to enable debugging of hardware breakpoint/ | |
| watchpoint support code. */ | |
| extern bool show_debug_regs; | |
| /* Print a formatted message to the appropriate channel for | |
| debugging output for the client. */ | |
| extern void debug_printf (const char *format, ...) | |
| ATTRIBUTE_PRINTF (1, 2); | |
| /* Print a formatted message to the appropriate channel for | |
| debugging output for the client. This function must be | |
| provided by the client. */ | |
| extern void debug_vprintf (const char *format, va_list ap) | |
| ATTRIBUTE_PRINTF (1, 0); | |
| /* Print a debug statement prefixed with the module and function name, and | |
| with a newline at the end. */ | |
| extern void ATTRIBUTE_PRINTF (3, 4) debug_prefixed_printf | |
| (const char *module, const char *func, const char *format, ...); | |
| /* Print a debug statement prefixed with the module and function name, and | |
| with a newline at the end. */ | |
| extern void ATTRIBUTE_PRINTF (3, 0) debug_prefixed_vprintf | |
| (const char *module, const char *func, const char *format, va_list args); | |
| /* Helper to define "_debug_print" macros. | |
| DEBUG_ENABLED_COND is an expression that evaluates to true if the debugging | |
| statement is enabled and should be printed. | |
| The other arguments, as well as the name of the current function, are | |
| forwarded to debug_prefixed_printf. */ | |
| #define debug_prefixed_printf_cond(debug_enabled_cond, module, fmt, ...) \ | |
| do \ | |
| { \ | |
| if (debug_enabled_cond) \ | |
| debug_prefixed_printf (module, __func__, fmt, ##__VA_ARGS__); \ | |
| } \ | |
| while (0) | |
| #define debug_prefixed_printf_cond_nofunc(debug_enabled_cond, module, fmt, ...) \ | |
| do \ | |
| { \ | |
| if (debug_enabled_cond) \ | |
| debug_prefixed_printf (module, nullptr, fmt, ##__VA_ARGS__); \ | |
| } \ | |
| while (0) | |
| /* Nesting depth of scoped_debug_start_end objects. */ | |
| extern int debug_print_depth; | |
| /* Print a message on construction and destruction, to denote the start and end | |
| of an operation. Increment DEBUG_PRINT_DEPTH on construction and decrement | |
| it on destruction, such that nested debug statements will be printed with | |
| an indent and appear "inside" this one. */ | |
| template<typename PT> | |
| struct scoped_debug_start_end | |
| { | |
| /* DEBUG_ENABLED is a reference to a variable that indicates whether debugging | |
| is enabled, so if the debug statements should be printed. Is is read | |
| separately at construction and destruction, such that the start statement | |
| could be printed but not the end statement, or vice-versa. | |
| DEBUG_ENABLED should either be of type 'bool &' or should be a type | |
| that can be invoked. | |
| MODULE and FUNC are forwarded to debug_prefixed_printf. | |
| START_PREFIX and END_PREFIX are the statements to print on construction and | |
| destruction, respectively. | |
| If the FMT format string is non-nullptr, then a `: ` is appended to the | |
| messages, followed by the rendering of that format string with ARGS. | |
| The format string is rendered during construction and is re-used as is | |
| for the message on exit. */ | |
| scoped_debug_start_end (PT &debug_enabled, const char *module, | |
| const char *func, const char *start_prefix, | |
| const char *end_prefix, const char *fmt, | |
| va_list args) | |
| ATTRIBUTE_NULL_PRINTF (7, 0) | |
| : m_debug_enabled (debug_enabled), | |
| m_module (module), | |
| m_func (func), | |
| m_end_prefix (end_prefix), | |
| m_with_format (fmt != nullptr) | |
| { | |
| if (is_debug_enabled ()) | |
| { | |
| if (fmt != nullptr) | |
| { | |
| m_msg = string_vprintf (fmt, args); | |
| debug_prefixed_printf (m_module, m_func, "%s: %s", | |
| start_prefix, m_msg->c_str ()); | |
| } | |
| else | |
| debug_prefixed_printf (m_module, m_func, "%s", start_prefix); | |
| ++debug_print_depth; | |
| m_must_decrement_print_depth = true; | |
| } | |
| } | |
| DISABLE_COPY_AND_ASSIGN (scoped_debug_start_end); | |
| scoped_debug_start_end (scoped_debug_start_end &&other) | |
| : m_debug_enabled (other.m_debug_enabled), | |
| m_module (other.m_module), | |
| m_func (other.m_func), | |
| m_end_prefix (other.m_end_prefix), | |
| m_msg (other.m_msg), | |
| m_with_format (other.m_with_format), | |
| m_must_decrement_print_depth (other.m_must_decrement_print_depth), | |
| m_disabled (other.m_disabled) | |
| { | |
| /* Avoid the moved-from object doing the side-effects in its destructor. */ | |
| other.m_disabled = true; | |
| } | |
| ~scoped_debug_start_end () | |
| { | |
| if (m_disabled) | |
| return; | |
| if (m_must_decrement_print_depth) | |
| { | |
| gdb_assert (debug_print_depth > 0); | |
| --debug_print_depth; | |
| } | |
| if (is_debug_enabled ()) | |
| { | |
| if (m_with_format) | |
| { | |
| if (m_msg.has_value ()) | |
| debug_prefixed_printf (m_module, m_func, "%s: %s", | |
| m_end_prefix, m_msg->c_str ()); | |
| else | |
| { | |
| /* A format string was passed to the constructor, but debug | |
| control variable wasn't set at the time, so we don't have the | |
| rendering of the format string. */ | |
| debug_prefixed_printf (m_module, m_func, "%s: <%s debugging was not enabled on entry>", | |
| m_end_prefix, m_module); | |
| } | |
| } | |
| else | |
| debug_prefixed_printf (m_module, m_func, "%s", m_end_prefix); | |
| } | |
| } | |
| private: | |
| /* This function is specialized based on the type PT. Returns true if | |
| M_DEBUG_ENABLED indicates this debug setting is enabled, otherwise, | |
| return false. */ | |
| bool is_debug_enabled () const; | |
| /* Reference to the debug setting, or a callback that can read the debug | |
| setting. Access the value of this by calling IS_DEBUG_ENABLED. */ | |
| PT &m_debug_enabled; | |
| const char *m_module; | |
| const char *m_func; | |
| const char *m_end_prefix; | |
| /* The result of formatting the format string in the constructor. */ | |
| std::optional<std::string> m_msg; | |
| /* True is a non-nullptr format was passed to the constructor. */ | |
| bool m_with_format; | |
| /* This is used to handle the case where debugging is enabled during | |
| construction but not during destruction, or vice-versa. We want to make | |
| sure there are as many increments are there are decrements. */ | |
| bool m_must_decrement_print_depth = false; | |
| /* True if this object was moved from, and the destructor behavior must be | |
| inhibited. */ | |
| bool m_disabled = false; | |
| }; | |
| /* Implementation of is_debug_enabled when PT is an invokable type. */ | |
| template<typename PT> | |
| inline bool | |
| scoped_debug_start_end<PT>::is_debug_enabled () const | |
| { | |
| return m_debug_enabled (); | |
| } | |
| /* Implementation of is_debug_enabled when PT is 'bool &'. */ | |
| template<> | |
| inline bool | |
| scoped_debug_start_end<bool &>::is_debug_enabled () const | |
| { | |
| return m_debug_enabled; | |
| } | |
| /* Wrapper around the scoped_debug_start_end constructor to allow the | |
| caller to create an object using 'auto' type, the actual type will be | |
| based on the type of the PRED argument. All arguments are forwarded to | |
| the scoped_debug_start_end constructor. */ | |
| template<typename PT> | |
| static inline scoped_debug_start_end<PT &> ATTRIBUTE_NULL_PRINTF (6, 7) | |
| make_scoped_debug_start_end (PT &&pred, const char *module, const char *func, | |
| const char *start_prefix, | |
| const char *end_prefix, const char *fmt, ...) | |
| { | |
| va_list args; | |
| va_start (args, fmt); | |
| auto res = scoped_debug_start_end<PT &> (pred, module, func, start_prefix, | |
| end_prefix, fmt, args); | |
| va_end (args); | |
| return res; | |
| } | |
| /* Helper to define a module-specific start/end debug macro. */ | |
| #define scoped_debug_start_end(debug_enabled, module, fmt, ...) \ | |
| auto CONCAT(scoped_debug_start_end, __LINE__) \ | |
| = make_scoped_debug_start_end (debug_enabled, module, \ | |
| __func__, "start", "end", \ | |
| fmt, ##__VA_ARGS__) | |
| /* Helper to define a module-specific enter/exit debug macro. This is a special | |
| case of `scoped_debug_start_end` where the start and end messages are "enter" | |
| and "exit", to denote entry and exit of a function. */ | |
| #define scoped_debug_enter_exit(debug_enabled, module) \ | |
| auto CONCAT(scoped_debug_start_end, __LINE__) \ | |
| = make_scoped_debug_start_end (debug_enabled, module, \ | |
| __func__, "enter", "exit", \ | |
| nullptr) | |
| #endif /* GDBSUPPORT_COMMON_DEBUG_H */ |