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