mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-09 04:21:49 +08:00
7e0bd9ea7e
I spotted a problem with scoped_debug_start_end's move constructor. When constructing a scoped_debug_start_end through it, it doesn't disable the moved-from object, meaning there are now two objects that will do the side-effects of decrementing the debug_print_depth global and printing the "end" message. Decrementing the debug_print_depth global twice is actually problematic, because the increments and decrements get out of sync, meaning we should hit this assertion, in theory: gdb_assert (debug_print_depth > 0); However, in practice, we don't see that. This is because despite the move constructor being required for this to compile: 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; } ... it is never actually called, because compilers elide the move constructors all the way (the scoped_debug_start_end gets constructed directly in the instance of the top-level caller). To confirm this, I built GDB with -fno-elide-constructors, and now I see it: /home/simark/src/binutils-gdb/gdb/../gdbsupport/common-debug.h:147: internal-error: ~scoped_debug_start_end: Assertion `debug_print_depth > 0' failed. #9 0x00005614ba5f17c3 in internal_error_loc (file=0x5614b8749960 "/home/simark/src/binutils-gdb/gdb/../gdbsupport/common-debug.h", line=147, fmt=0x5614b8733fa0 "%s: Assertion `%s' failed.") at /home/simark/src/binutils-gdb/gdbsupport/errors.cc:58 #10 0x00005614b8e1b2e5 in scoped_debug_start_end<bool&>::~scoped_debug_start_end (this=0x7ffc6c5e7b40, __in_chrg=<optimized out>) at /home/simark/src/binutils-gdb/gdb/../gdbsupport/common-debug.h:147 #11 0x00005614b96dbe34 in make_scoped_debug_start_end<bool&> (pred=@0x5614baad7200: true, module=0x5614b891d840 "infrun", func=0x5614b891d800 "infrun_debug_show_threads", start_prefix=0x5614b891d7c0 "enter", end_prefix=0x5614b891d780 "exit", fmt=0x0) at /home/simark/src/binutils-gdb/gdb/../gdbsupport/common-debug.h:235 Fix this by adding an m_disabled field to scoped_debug_start_end, and setting it in the move constructor. Change-Id: Ie5213269c584837f751d2d11de831f45ae4a899f
275 lines
8.7 KiB
C++
275 lines
8.7 KiB
C++
/* Declarations for debug printing functions.
|
|
|
|
Copyright (C) 2014-2023 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 "gdbsupport/gdb_optional.h"
|
|
#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. */
|
|
gdb::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 */
|