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