| /* 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 COMMON_COMMON_DEBUG_H |
| #define COMMON_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 /* COMMON_COMMON_DEBUG_H */ |