xref: /netbsd-src/external/gpl3/gdb/dist/gdb/complaints.c (revision 08210d1cc3e8ca69ac932314afacdbaa89aec4d9)
1 /* Support for complaint handling during symbol reading in GDB.
2 
3    Copyright (C) 1990-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 #include "complaints.h"
21 #include "command.h"
22 #include "cli/cli-cmds.h"
23 #include "run-on-main-thread.h"
24 #include "top.h"
25 #include "gdbsupport/selftest.h"
26 #include <unordered_map>
27 #if CXX_STD_THREAD
28 #include <mutex>
29 #endif
30 
31 /* Map format strings to counters.  */
32 
33 static std::unordered_map<const char *, int> counters;
34 
35 /* How many complaints about a particular thing should be printed
36    before we stop whining about it?  Default is no whining at all,
37    since so many systems have ill-constructed symbol files.  */
38 
39 int stop_whining = 0;
40 
41 #if CXX_STD_THREAD
42 static std::mutex complaint_mutex;
43 #endif /* CXX_STD_THREAD */
44 
45 /* See complaints.h.  */
46 
47 void
48 complaint_internal (const char *fmt, ...)
49 {
50   va_list args;
51 
52   {
53 #if CXX_STD_THREAD
54     std::lock_guard<std::mutex> guard (complaint_mutex);
55 #endif
56     if (++counters[fmt] > stop_whining)
57       return;
58   }
59 
60   va_start (args, fmt);
61 
62   warning_hook_handler handler = get_warning_hook_handler ();
63   if (handler != nullptr)
64     handler->warn (fmt, args);
65   else
66     {
67       gdb_puts (_("During symbol reading: "), gdb_stderr);
68       gdb_vprintf (gdb_stderr, fmt, args);
69       gdb_puts ("\n", gdb_stderr);
70     }
71 
72   va_end (args);
73 }
74 
75 /* See complaints.h.  */
76 
77 void
78 clear_complaints ()
79 {
80   counters.clear ();
81 }
82 
83 /* See complaints.h.  */
84 
85 thread_local complaint_interceptor *complaint_interceptor::g_complaint_interceptor;
86 
87 /* See complaints.h.  */
88 
89 complaint_interceptor::complaint_interceptor ()
90   : m_saved_complaint_interceptor (&g_complaint_interceptor, this),
91     m_saved_warning_hook (this)
92 {
93 }
94 
95 /* A helper that wraps a warning hook.  */
96 
97 static void
98 wrap_warning_hook (warning_hook_handler hook, ...)
99 {
100   va_list args;
101   va_start (args, hook);
102   hook->warn ("%s", args);
103   va_end (args);
104 }
105 
106 /* See complaints.h.  */
107 
108 void
109 re_emit_complaints (const complaint_collection &complaints)
110 {
111   gdb_assert (is_main_thread ());
112 
113   for (const std::string &str : complaints)
114     {
115       warning_hook_handler handler = get_warning_hook_handler ();
116       if (handler != nullptr)
117 	wrap_warning_hook (handler, str.c_str ());
118       else
119 	gdb_printf (gdb_stderr, _("During symbol reading: %s\n"),
120 		    str.c_str ());
121     }
122 }
123 
124 /* See complaints.h.  */
125 
126 void
127 complaint_interceptor::warn (const char *fmt, va_list args)
128 {
129 #if CXX_STD_THREAD
130   std::lock_guard<std::mutex> guard (complaint_mutex);
131 #endif
132   g_complaint_interceptor->m_complaints.insert (string_vprintf (fmt, args));
133 }
134 
135 static void
136 complaints_show_value (struct ui_file *file, int from_tty,
137 		       struct cmd_list_element *cmd, const char *value)
138 {
139   gdb_printf (file, _("Max number of complaints about incorrect"
140 		      " symbols is %s.\n"),
141 	      value);
142 }
143 
144 #if GDB_SELF_TEST
145 namespace selftests {
146 
147 /* Entry point for complaints unit tests.  */
148 
149 static void
150 test_complaints ()
151 {
152   std::unordered_map<const char *, int> tmp;
153   scoped_restore reset_counters = make_scoped_restore (&counters, tmp);
154   scoped_restore reset_stop_whining = make_scoped_restore (&stop_whining, 2);
155 
156 #define CHECK_COMPLAINT(STR, CNT)					\
157   do									\
158     {									\
159       std::string output;						\
160       execute_fn_to_string (output, []() { complaint (STR); }, false);	\
161       std::string expected						\
162 	= _("During symbol reading: ") + std::string (STR "\n");	\
163       SELF_CHECK (output == expected);					\
164       SELF_CHECK (counters[STR] == CNT);				\
165     } while (0)
166 
167 #define CHECK_COMPLAINT_SILENT(STR, CNT)				\
168   do									\
169     {									\
170       std::string output;						\
171       execute_fn_to_string (output, []() { complaint (STR); }, false);	\
172       SELF_CHECK (output.empty ());					\
173       SELF_CHECK (counters[STR] == CNT);				\
174     } while (0)
175 
176   CHECK_COMPLAINT ("maintenance complaint 0", 1);
177   CHECK_COMPLAINT ("maintenance complaint 0", 2);
178   CHECK_COMPLAINT_SILENT ("maintenance complaint 0", 3);
179   CHECK_COMPLAINT ("maintenance complaint 1", 1);
180   clear_complaints ();
181   CHECK_COMPLAINT ("maintenance complaint 0", 1);
182 
183 #undef CHECK_COMPLAINT
184 #undef CHECK_COMPLAINT_SILENT
185 }
186 
187 
188 } // namespace selftests
189 #endif /* GDB_SELF_TEST */
190 
191 void _initialize_complaints ();
192 void
193 _initialize_complaints ()
194 {
195   add_setshow_zinteger_cmd ("complaints", class_support,
196 			    &stop_whining, _("\
197 Set max number of complaints about incorrect symbols."), _("\
198 Show max number of complaints about incorrect symbols."), NULL,
199 			    NULL, complaints_show_value,
200 			    &setlist, &showlist);
201 
202 #if GDB_SELF_TEST
203   selftests::register_test ("complaints", selftests::test_complaints);
204 #endif /* GDB_SELF_TEST */
205 }
206