| /* Support for complaint handling during symbol reading in GDB. | 
 |  | 
 |    Copyright (C) 1990-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/>.  */ | 
 |  | 
 | #include "defs.h" | 
 | #include "complaints.h" | 
 | #include "command.h" | 
 | #include "gdbcmd.h" | 
 | #include "gdbsupport/selftest.h" | 
 | #include <unordered_map> | 
 | #include <mutex> | 
 |  | 
 | /* Map format strings to counters.  */ | 
 |  | 
 | static std::unordered_map<const char *, int> counters; | 
 |  | 
 | /* How many complaints about a particular thing should be printed | 
 |    before we stop whining about it?  Default is no whining at all, | 
 |    since so many systems have ill-constructed symbol files.  */ | 
 |  | 
 | int stop_whining = 0; | 
 |  | 
 | #if CXX_STD_THREAD | 
 | static std::mutex complaint_mutex; | 
 | #endif /* CXX_STD_THREAD */ | 
 |  | 
 | /* See complaints.h.  */ | 
 |  | 
 | void | 
 | complaint_internal (const char *fmt, ...) | 
 | { | 
 |   va_list args; | 
 |  | 
 |   { | 
 | #if CXX_STD_THREAD | 
 |     std::lock_guard<std::mutex> guard (complaint_mutex); | 
 | #endif | 
 |     if (++counters[fmt] > stop_whining) | 
 |       return; | 
 |   } | 
 |  | 
 |   va_start (args, fmt); | 
 |  | 
 |   if (deprecated_warning_hook) | 
 |     (*deprecated_warning_hook) (fmt, args); | 
 |   else | 
 |     { | 
 |       gdb_puts (_("During symbol reading: "), gdb_stderr); | 
 |       gdb_vprintf (gdb_stderr, fmt, args); | 
 |       gdb_puts ("\n", gdb_stderr); | 
 |     } | 
 |  | 
 |   va_end (args); | 
 | } | 
 |  | 
 | /* See complaints.h.  */ | 
 |  | 
 | void | 
 | clear_complaints () | 
 | { | 
 |   counters.clear (); | 
 | } | 
 |  | 
 | /* See complaints.h.  */ | 
 |  | 
 | complaint_interceptor *complaint_interceptor::g_complaint_interceptor; | 
 |  | 
 | /* See complaints.h.  */ | 
 |  | 
 | complaint_interceptor::complaint_interceptor () | 
 |   : m_saved_warning_hook (deprecated_warning_hook) | 
 | { | 
 |   /* These cannot be stacked.  */ | 
 |   gdb_assert (g_complaint_interceptor == nullptr); | 
 |   g_complaint_interceptor = this; | 
 |   deprecated_warning_hook = issue_complaint; | 
 | } | 
 |  | 
 | /* A helper that wraps a warning hook.  */ | 
 |  | 
 | static void | 
 | wrap_warning_hook (void (*hook) (const char *, va_list), ...) | 
 | { | 
 |   va_list args; | 
 |   va_start (args, hook); | 
 |   hook ("%s", args); | 
 |   va_end (args); | 
 | } | 
 |  | 
 | /* See complaints.h.  */ | 
 |  | 
 | complaint_interceptor::~complaint_interceptor () | 
 | { | 
 |   for (const std::string &str : m_complaints) | 
 |     { | 
 |       if (m_saved_warning_hook) | 
 | 	wrap_warning_hook (m_saved_warning_hook, str.c_str ()); | 
 |       else | 
 | 	gdb_printf (gdb_stderr, _("During symbol reading: %s\n"), | 
 | 		    str.c_str ()); | 
 |     } | 
 |  | 
 |   g_complaint_interceptor = nullptr; | 
 |   deprecated_warning_hook = m_saved_warning_hook; | 
 | } | 
 |  | 
 | /* See complaints.h.  */ | 
 |  | 
 | void | 
 | complaint_interceptor::issue_complaint (const char *fmt, va_list args) | 
 | { | 
 | #if CXX_STD_THREAD | 
 |   std::lock_guard<std::mutex> guard (complaint_mutex); | 
 | #endif | 
 |   g_complaint_interceptor->m_complaints.insert (string_vprintf (fmt, args)); | 
 | } | 
 |  | 
 | static void | 
 | complaints_show_value (struct ui_file *file, int from_tty, | 
 | 		       struct cmd_list_element *cmd, const char *value) | 
 | { | 
 |   gdb_printf (file, _("Max number of complaints about incorrect" | 
 | 		      " symbols is %s.\n"), | 
 | 	      value); | 
 | } | 
 |  | 
 | #if GDB_SELF_TEST | 
 | namespace selftests { | 
 |  | 
 | /* Entry point for complaints unit tests.  */ | 
 |  | 
 | static void | 
 | test_complaints () | 
 | { | 
 |   std::unordered_map<const char *, int> tmp; | 
 |   scoped_restore reset_counters = make_scoped_restore (&counters, tmp); | 
 |   scoped_restore reset_stop_whining = make_scoped_restore (&stop_whining, 2); | 
 |  | 
 | #define CHECK_COMPLAINT(STR, CNT)					\ | 
 |   do									\ | 
 |     {									\ | 
 |       std::string output;						\ | 
 |       execute_fn_to_string (output, []() { complaint (STR); }, false);	\ | 
 |       std::string expected						\ | 
 | 	= _("During symbol reading: ") + std::string (STR "\n");	\ | 
 |       SELF_CHECK (output == expected);					\ | 
 |       SELF_CHECK (counters[STR] == CNT);				\ | 
 |     } while (0) | 
 |  | 
 | #define CHECK_COMPLAINT_SILENT(STR, CNT)				\ | 
 |   do									\ | 
 |     {									\ | 
 |       std::string output;						\ | 
 |       execute_fn_to_string (output, []() { complaint (STR); }, false);	\ | 
 |       SELF_CHECK (output.empty ());					\ | 
 |       SELF_CHECK (counters[STR] == CNT);				\ | 
 |     } while (0) | 
 |  | 
 |   CHECK_COMPLAINT ("maintenance complaint 0", 1); | 
 |   CHECK_COMPLAINT ("maintenance complaint 0", 2); | 
 |   CHECK_COMPLAINT_SILENT ("maintenance complaint 0", 3); | 
 |   CHECK_COMPLAINT ("maintenance complaint 1", 1); | 
 |   clear_complaints (); | 
 |   CHECK_COMPLAINT ("maintenance complaint 0", 1); | 
 |  | 
 | #undef CHECK_COMPLAINT | 
 | #undef CHECK_COMPLAINT_SILENT | 
 | } | 
 |  | 
 |  | 
 | } // namespace selftests | 
 | #endif /* GDB_SELF_TEST */ | 
 |  | 
 | void _initialize_complaints (); | 
 | void | 
 | _initialize_complaints () | 
 | { | 
 |   add_setshow_zinteger_cmd ("complaints", class_support,  | 
 | 			    &stop_whining, _("\ | 
 | Set max number of complaints about incorrect symbols."), _("\ | 
 | Show max number of complaints about incorrect symbols."), NULL, | 
 | 			    NULL, complaints_show_value, | 
 | 			    &setlist, &showlist); | 
 |  | 
 | #if GDB_SELF_TEST | 
 |   selftests::register_test ("complaints", selftests::test_complaints); | 
 | #endif /* GDB_SELF_TEST */ | 
 | } |