xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/common-debug.h (revision 4439cfd0acf9c7dc90625e5cd83b2317a9ab8967)
1 /* Declarations for debug printing functions.
2 
3    Copyright (C) 2014-2024 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #ifndef COMMON_COMMON_DEBUG_H
21 #define COMMON_COMMON_DEBUG_H
22 
23 #include <optional>
24 #include "gdbsupport/preprocessor.h"
25 
26 #include <stdarg.h>
27 
28 /* Set to true to enable debugging of hardware breakpoint/
29    watchpoint support code.  */
30 
31 extern bool show_debug_regs;
32 
33 /* Print a formatted message to the appropriate channel for
34    debugging output for the client.  */
35 
36 extern void debug_printf (const char *format, ...)
37      ATTRIBUTE_PRINTF (1, 2);
38 
39 /* Print a formatted message to the appropriate channel for
40    debugging output for the client.  This function must be
41    provided by the client.  */
42 
43 extern void debug_vprintf (const char *format, va_list ap)
44      ATTRIBUTE_PRINTF (1, 0);
45 
46 /* Print a debug statement prefixed with the module and function name, and
47    with a newline at the end.  */
48 
49 extern void ATTRIBUTE_PRINTF (3, 4) debug_prefixed_printf
50   (const char *module, const char *func, const char *format, ...);
51 
52 /* Print a debug statement prefixed with the module and function name, and
53    with a newline at the end.  */
54 
55 extern void ATTRIBUTE_PRINTF (3, 0) debug_prefixed_vprintf
56   (const char *module, const char *func, const char *format, va_list args);
57 
58 /* Helper to define "_debug_print" macros.
59 
60    DEBUG_ENABLED_COND is an expression that evaluates to true if the debugging
61    statement is enabled and should be printed.
62 
63    The other arguments, as well as the name of the current function, are
64    forwarded to debug_prefixed_printf.  */
65 
66 #define debug_prefixed_printf_cond(debug_enabled_cond, module, fmt, ...) \
67   do \
68     { \
69       if (debug_enabled_cond) \
70 	debug_prefixed_printf (module, __func__, fmt, ##__VA_ARGS__); \
71     } \
72   while (0)
73 
74 #define debug_prefixed_printf_cond_nofunc(debug_enabled_cond, module, fmt, ...) \
75   do \
76     { \
77       if (debug_enabled_cond) \
78 	debug_prefixed_printf (module, nullptr, fmt, ##__VA_ARGS__); \
79     } \
80   while (0)
81 
82 /* Nesting depth of scoped_debug_start_end objects.  */
83 
84 extern int debug_print_depth;
85 
86 /* Print a message on construction and destruction, to denote the start and end
87    of an operation.  Increment DEBUG_PRINT_DEPTH on construction and decrement
88    it on destruction, such that nested debug statements will be printed with
89    an indent and appear "inside" this one.  */
90 
91 template<typename PT>
92 struct scoped_debug_start_end
93 {
94   /* DEBUG_ENABLED is a reference to a variable that indicates whether debugging
95      is enabled, so if the debug statements should be printed.  Is is read
96      separately at construction and destruction, such that the start statement
97      could be printed but not the end statement, or vice-versa.
98 
99      DEBUG_ENABLED should either be of type 'bool &' or should be a type
100      that can be invoked.
101 
102      MODULE and FUNC are forwarded to debug_prefixed_printf.
103 
104      START_PREFIX and END_PREFIX are the statements to print on construction and
105      destruction, respectively.
106 
107      If the FMT format string is non-nullptr, then a `: ` is appended to the
108      messages, followed by the rendering of that format string with ARGS.
109      The format string is rendered during construction and is re-used as is
110      for the message on exit.  */
111 
112   scoped_debug_start_end (PT &debug_enabled, const char *module,
113 			  const char *func, const char *start_prefix,
114 			  const char *end_prefix, const char *fmt,
115 			  va_list args)
116     ATTRIBUTE_NULL_PRINTF (7, 0)
117     : m_debug_enabled (debug_enabled),
118       m_module (module),
119       m_func (func),
120       m_end_prefix (end_prefix),
121       m_with_format (fmt != nullptr)
122   {
123     if (is_debug_enabled ())
124       {
125 	if (fmt != nullptr)
126 	  {
127 	    m_msg = string_vprintf (fmt, args);
128 	    debug_prefixed_printf (m_module, m_func, "%s: %s",
129 				   start_prefix, m_msg->c_str ());
130 	  }
131 	else
132 	  debug_prefixed_printf (m_module, m_func, "%s", start_prefix);
133 
134 	++debug_print_depth;
135 	m_must_decrement_print_depth = true;
136       }
137   }
138 
139   DISABLE_COPY_AND_ASSIGN (scoped_debug_start_end);
140 
141   scoped_debug_start_end (scoped_debug_start_end &&other)
142     : m_debug_enabled (other.m_debug_enabled),
143       m_module (other.m_module),
144       m_func (other.m_func),
145       m_end_prefix (other.m_end_prefix),
146       m_msg (other.m_msg),
147       m_with_format (other.m_with_format),
148       m_must_decrement_print_depth (other.m_must_decrement_print_depth),
149       m_disabled (other.m_disabled)
150   {
151     /* Avoid the moved-from object doing the side-effects in its destructor.  */
152     other.m_disabled = true;
153   }
154 
155   ~scoped_debug_start_end ()
156   {
157     if (m_disabled)
158       return;
159 
160     if (m_must_decrement_print_depth)
161       {
162 	gdb_assert (debug_print_depth > 0);
163 	--debug_print_depth;
164       }
165 
166     if (is_debug_enabled ())
167       {
168 	if (m_with_format)
169 	  {
170 	    if (m_msg.has_value ())
171 	      debug_prefixed_printf (m_module, m_func, "%s: %s",
172 				     m_end_prefix, m_msg->c_str ());
173 	    else
174 	      {
175 		/* A format string was passed to the constructor, but debug
176 		   control variable wasn't set at the time, so we don't have the
177 		   rendering of the format string.  */
178 		debug_prefixed_printf (m_module, m_func, "%s: <%s debugging was not enabled on entry>",
179 				       m_end_prefix, m_module);
180 	      }
181 	  }
182 	else
183 	  debug_prefixed_printf (m_module, m_func, "%s", m_end_prefix);
184       }
185   }
186 
187 private:
188 
189   /* This function is specialized based on the type PT.  Returns true if
190      M_DEBUG_ENABLED indicates this debug setting is enabled, otherwise,
191      return false.  */
192   bool is_debug_enabled () const;
193 
194   /* Reference to the debug setting, or a callback that can read the debug
195      setting.  Access the value of this by calling IS_DEBUG_ENABLED.  */
196   PT &m_debug_enabled;
197 
198   const char *m_module;
199   const char *m_func;
200   const char *m_end_prefix;
201 
202   /* The result of formatting the format string in the constructor.  */
203   std::optional<std::string> m_msg;
204 
205   /* True is a non-nullptr format was passed to the constructor.  */
206   bool m_with_format;
207 
208   /* This is used to handle the case where debugging is enabled during
209      construction but not during destruction, or vice-versa.  We want to make
210      sure there are as many increments are there are decrements.  */
211   bool m_must_decrement_print_depth = false;
212 
213   /* True if this object was moved from, and the destructor behavior must be
214      inhibited.  */
215   bool m_disabled = false;
216 };
217 
218 /* Implementation of is_debug_enabled when PT is an invokable type.  */
219 
220 template<typename PT>
221 inline bool
222 scoped_debug_start_end<PT>::is_debug_enabled () const
223 {
224   return m_debug_enabled ();
225 }
226 
227 /* Implementation of is_debug_enabled when PT is 'bool &'.  */
228 
229 template<>
230 inline bool
231 scoped_debug_start_end<bool &>::is_debug_enabled () const
232 {
233   return m_debug_enabled;
234 }
235 
236 /* Wrapper around the scoped_debug_start_end constructor to allow the
237    caller to create an object using 'auto' type, the actual type will be
238    based on the type of the PRED argument.  All arguments are forwarded to
239    the scoped_debug_start_end constructor.  */
240 
241 template<typename PT>
242 static inline scoped_debug_start_end<PT &> ATTRIBUTE_NULL_PRINTF (6, 7)
243 make_scoped_debug_start_end (PT &&pred, const char *module, const char *func,
244 			     const char *start_prefix,
245 			     const char *end_prefix, const char *fmt, ...)
246 {
247   va_list args;
248   va_start (args, fmt);
249   auto res = scoped_debug_start_end<PT &> (pred, module, func, start_prefix,
250 					   end_prefix, fmt, args);
251   va_end (args);
252 
253   return res;
254 }
255 
256 /* Helper to define a module-specific start/end debug macro.  */
257 
258 #define scoped_debug_start_end(debug_enabled, module, fmt, ...)		\
259   auto CONCAT(scoped_debug_start_end, __LINE__)				\
260     = make_scoped_debug_start_end (debug_enabled, module, 	\
261 				   __func__, "start", "end",	\
262 				   fmt, ##__VA_ARGS__)
263 
264 /* Helper to define a module-specific enter/exit debug macro.  This is a special
265    case of `scoped_debug_start_end` where the start and end messages are "enter"
266    and "exit", to denote entry and exit of a function.  */
267 
268 #define scoped_debug_enter_exit(debug_enabled, module)	\
269   auto CONCAT(scoped_debug_start_end, __LINE__)				\
270     = make_scoped_debug_start_end (debug_enabled, module, 	\
271 				   __func__, "enter", "exit",	\
272 				   nullptr)
273 
274 #endif /* COMMON_COMMON_DEBUG_H */
275