gdbsupport: move fast_hash to gdbsupport/common-utils.h
[binutils-gdb.git] / gdbsupport / common-debug.h
index 9934ec543d28cf7a03affd63299f328f2dae9e8d..ec36d88fdea29a7b4bbfe0ccd2314a33fee19ac7 100644 (file)
@@ -1,6 +1,6 @@
 /* Declarations for debug printing functions.
 
-   Copyright (C) 2014-2020 Free Software Foundation, Inc.
+   Copyright (C) 2014-2023 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 #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.  */
 
@@ -38,4 +43,213 @@ extern void debug_printf (const char *format, ...)
 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) = default;
+
+  ~scoped_debug_start_end ()
+  {
+    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;
+};
+
+/* 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 */