xref: /netbsd-src/external/gpl3/gdb/dist/gdb/break-catch-sig.c (revision c68264a1fd872573fd6468c005f29677462884ab)
1 /* Everything about signal catchpoints, for GDB.
2 
3    Copyright (C) 2011-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 "arch-utils.h"
21 #include <ctype.h>
22 #include "breakpoint.h"
23 #include "cli/cli-cmds.h"
24 #include "inferior.h"
25 #include "infrun.h"
26 #include "annotate.h"
27 #include "valprint.h"
28 #include "cli/cli-utils.h"
29 #include "completer.h"
30 #include "cli/cli-style.h"
31 #include "cli/cli-decode.h"
32 
33 #include <string>
34 
35 #define INTERNAL_SIGNAL(x) ((x) == GDB_SIGNAL_TRAP || (x) == GDB_SIGNAL_INT)
36 
37 /* An instance of this type is used to represent a signal
38    catchpoint.  */
39 
40 struct signal_catchpoint : public catchpoint
41 {
42   signal_catchpoint (struct gdbarch *gdbarch, bool temp,
43 		     std::vector<gdb_signal> &&sigs,
44 		     bool catch_all_)
45     : catchpoint (gdbarch, temp, nullptr),
46       signals_to_be_caught (std::move (sigs)),
47       catch_all (catch_all_)
48   {
49   }
50 
51   int insert_location (struct bp_location *) override;
52   int remove_location (struct bp_location *,
53 		       enum remove_bp_reason reason) override;
54   int breakpoint_hit (const struct bp_location *bl,
55 		      const address_space *aspace,
56 		      CORE_ADDR bp_addr,
57 		      const target_waitstatus &ws) override;
58   enum print_stop_action print_it (const bpstat *bs) const override;
59   bool print_one (const bp_location **) const override;
60   void print_mention () const override;
61   void print_recreate (struct ui_file *fp) const override;
62   bool explains_signal (enum gdb_signal) override;
63 
64   /* Signal numbers used for the 'catch signal' feature.  If no signal
65      has been specified for filtering, it is empty.  Otherwise,
66      it holds a list of all signals to be caught.  */
67 
68   std::vector<gdb_signal> signals_to_be_caught;
69 
70   /* If SIGNALS_TO_BE_CAUGHT is empty, then all "ordinary" signals are
71      caught.  If CATCH_ALL is true, then internal signals are caught
72      as well.  If SIGNALS_TO_BE_CAUGHT is not empty, then this field
73      is ignored.  */
74 
75   bool catch_all;
76 };
77 
78 /* Count of each signal.  */
79 
80 static unsigned int signal_catch_counts[GDB_SIGNAL_LAST];
81 
82 
83 
84 /* A convenience wrapper for gdb_signal_to_name that returns the
85    integer value if the name is not known.  */
86 
87 static const char *
88 signal_to_name_or_int (enum gdb_signal sig)
89 {
90   const char *result = gdb_signal_to_name (sig);
91 
92   if (strcmp (result, "?") == 0)
93     result = plongest (sig);
94 
95   return result;
96 }
97 
98 
99 
100 /* Implement the "insert_location" method for signal catchpoints.  */
101 
102 int
103 signal_catchpoint::insert_location (struct bp_location *bl)
104 {
105   signal_catchpoint *c
106     = gdb::checked_static_cast<signal_catchpoint *> (bl->owner);
107 
108   if (!c->signals_to_be_caught.empty ())
109     {
110       for (gdb_signal iter : c->signals_to_be_caught)
111 	++signal_catch_counts[iter];
112     }
113   else
114     {
115       for (int i = 0; i < GDB_SIGNAL_LAST; ++i)
116 	{
117 	  if (c->catch_all || !INTERNAL_SIGNAL (i))
118 	    ++signal_catch_counts[i];
119 	}
120     }
121 
122   signal_catch_update (signal_catch_counts);
123 
124   return 0;
125 }
126 
127 /* Implement the "remove_location" method for signal catchpoints.  */
128 
129 int
130 signal_catchpoint::remove_location (struct bp_location *bl,
131 				    enum remove_bp_reason reason)
132 {
133   signal_catchpoint *c
134     = gdb::checked_static_cast<signal_catchpoint *> (bl->owner);
135 
136   if (!c->signals_to_be_caught.empty ())
137     {
138       for (gdb_signal iter : c->signals_to_be_caught)
139 	{
140 	  gdb_assert (signal_catch_counts[iter] > 0);
141 	  --signal_catch_counts[iter];
142 	}
143     }
144   else
145     {
146       for (int i = 0; i < GDB_SIGNAL_LAST; ++i)
147 	{
148 	  if (c->catch_all || !INTERNAL_SIGNAL (i))
149 	    {
150 	      gdb_assert (signal_catch_counts[i] > 0);
151 	      --signal_catch_counts[i];
152 	    }
153 	}
154     }
155 
156   signal_catch_update (signal_catch_counts);
157 
158   return 0;
159 }
160 
161 /* Implement the "breakpoint_hit" method for signal catchpoints.  */
162 
163 int
164 signal_catchpoint::breakpoint_hit (const struct bp_location *bl,
165 				   const address_space *aspace,
166 				   CORE_ADDR bp_addr,
167 				   const target_waitstatus &ws)
168 {
169   const signal_catchpoint *c
170     = gdb::checked_static_cast<const signal_catchpoint *> (bl->owner);
171   gdb_signal signal_number;
172 
173   if (ws.kind () != TARGET_WAITKIND_STOPPED)
174     return 0;
175 
176   signal_number = ws.sig ();
177 
178   /* If we are catching specific signals in this breakpoint, then we
179      must guarantee that the called signal is the same signal we are
180      catching.  */
181   if (!c->signals_to_be_caught.empty ())
182     {
183       for (gdb_signal iter : c->signals_to_be_caught)
184 	if (signal_number == iter)
185 	  return 1;
186       /* Not the same.  */
187       return 0;
188     }
189   else
190     return c->catch_all || !INTERNAL_SIGNAL (signal_number);
191 }
192 
193 /* Implement the "print_it" method for signal catchpoints.  */
194 
195 enum print_stop_action
196 signal_catchpoint::print_it (const bpstat *bs) const
197 {
198   struct target_waitstatus last;
199   const char *signal_name;
200   struct ui_out *uiout = current_uiout;
201 
202   get_last_target_status (nullptr, nullptr, &last);
203 
204   signal_name = signal_to_name_or_int (last.sig ());
205 
206   annotate_catchpoint (number);
207   maybe_print_thread_hit_breakpoint (uiout);
208 
209   gdb_printf (_("Catchpoint %d (signal %s), "), number, signal_name);
210 
211   return PRINT_SRC_AND_LOC;
212 }
213 
214 /* Implement the "print_one" method for signal catchpoints.  */
215 
216 bool
217 signal_catchpoint::print_one (const bp_location **last_loc) const
218 {
219   struct value_print_options opts;
220   struct ui_out *uiout = current_uiout;
221 
222   get_user_print_options (&opts);
223 
224   /* Field 4, the address, is omitted (which makes the columns
225      not line up too nicely with the headers, but the effect
226      is relatively readable).  */
227   if (opts.addressprint)
228     uiout->field_skip ("addr");
229   annotate_field (5);
230 
231   if (signals_to_be_caught.size () > 1)
232     uiout->text ("signals \"");
233   else
234     uiout->text ("signal \"");
235 
236   if (!signals_to_be_caught.empty ())
237     {
238       std::string text;
239 
240       bool first = true;
241       for (gdb_signal iter : signals_to_be_caught)
242 	{
243 	  const char *name = signal_to_name_or_int (iter);
244 
245 	  if (!first)
246 	    text += " ";
247 	  first = false;
248 
249 	  text += name;
250 	}
251       uiout->field_string ("what", text);
252     }
253   else
254     uiout->field_string ("what",
255 			 catch_all ? "<any signal>" : "<standard signals>",
256 			 metadata_style.style ());
257   uiout->text ("\" ");
258 
259   if (uiout->is_mi_like_p ())
260     uiout->field_string ("catch-type", "signal");
261 
262   return true;
263 }
264 
265 /* Implement the "print_mention" method for signal catchpoints.  */
266 
267 void
268 signal_catchpoint::print_mention () const
269 {
270   if (!signals_to_be_caught.empty ())
271     {
272       if (signals_to_be_caught.size () > 1)
273 	gdb_printf (_("Catchpoint %d (signals"), number);
274       else
275 	gdb_printf (_("Catchpoint %d (signal"), number);
276 
277       for (gdb_signal iter : signals_to_be_caught)
278 	{
279 	  const char *name = signal_to_name_or_int (iter);
280 
281 	  gdb_printf (" %s", name);
282 	}
283       gdb_printf (")");
284     }
285   else if (catch_all)
286     gdb_printf (_("Catchpoint %d (any signal)"), number);
287   else
288     gdb_printf (_("Catchpoint %d (standard signals)"), number);
289 }
290 
291 /* Implement the "print_recreate" method for signal catchpoints.  */
292 
293 void
294 signal_catchpoint::print_recreate (struct ui_file *fp) const
295 {
296   gdb_printf (fp, "catch signal");
297 
298   if (!signals_to_be_caught.empty ())
299     {
300       for (gdb_signal iter : signals_to_be_caught)
301 	gdb_printf (fp, " %s", signal_to_name_or_int (iter));
302     }
303   else if (catch_all)
304     gdb_printf (fp, " all");
305   gdb_putc ('\n', fp);
306 }
307 
308 /* Implement the "explains_signal" method for signal catchpoints.  */
309 
310 bool
311 signal_catchpoint::explains_signal (enum gdb_signal sig)
312 {
313   return true;
314 }
315 
316 /* Create a new signal catchpoint.  TEMPFLAG is true if this should be
317    a temporary catchpoint.  FILTER is the list of signals to catch; it
318    can be empty, meaning all signals.  CATCH_ALL is a flag indicating
319    whether signals used internally by gdb should be caught; it is only
320    valid if FILTER is NULL.  If FILTER is empty and CATCH_ALL is zero,
321    then internal signals like SIGTRAP are not caught.  */
322 
323 static void
324 create_signal_catchpoint (int tempflag, std::vector<gdb_signal> &&filter,
325 			  bool catch_all)
326 {
327   struct gdbarch *gdbarch = get_current_arch ();
328 
329   std::unique_ptr<signal_catchpoint> c
330     (new signal_catchpoint (gdbarch, tempflag, std::move (filter), catch_all));
331 
332   install_breakpoint (0, std::move (c), 1);
333 }
334 
335 
336 /* Splits the argument using space as delimiter.  Returns a filter
337    list, which is empty if no filtering is required.  */
338 
339 static std::vector<gdb_signal>
340 catch_signal_split_args (const char *arg, bool *catch_all)
341 {
342   std::vector<gdb_signal> result;
343   bool first = true;
344 
345   while (*arg != '\0')
346     {
347       int num;
348       gdb_signal signal_number;
349       char *endptr;
350 
351       std::string one_arg = extract_arg (&arg);
352       if (one_arg.empty ())
353 	break;
354 
355       /* Check for the special flag "all".  */
356       if (one_arg == "all")
357 	{
358 	  arg = skip_spaces (arg);
359 	  if (*arg != '\0' || !first)
360 	    error (_("'all' cannot be caught with other signals"));
361 	  *catch_all = true;
362 	  gdb_assert (result.empty ());
363 	  return result;
364 	}
365 
366       first = false;
367 
368       /* Check if the user provided a signal name or a number.  */
369       num = (int) strtol (one_arg.c_str (), &endptr, 0);
370       if (*endptr == '\0')
371 	signal_number = gdb_signal_from_command (num);
372       else
373 	{
374 	  signal_number = gdb_signal_from_name (one_arg.c_str ());
375 	  if (signal_number == GDB_SIGNAL_UNKNOWN)
376 	    error (_("Unknown signal name '%s'."), one_arg.c_str ());
377 	}
378 
379       result.push_back (signal_number);
380     }
381 
382   result.shrink_to_fit ();
383   return result;
384 }
385 
386 /* Implement the "catch signal" command.  */
387 
388 static void
389 catch_signal_command (const char *arg, int from_tty,
390 		      struct cmd_list_element *command)
391 {
392   int tempflag;
393   bool catch_all = false;
394   std::vector<gdb_signal> filter;
395 
396   tempflag = command->context () == CATCH_TEMPORARY;
397 
398   arg = skip_spaces (arg);
399 
400   /* The allowed syntax is:
401      catch signal
402      catch signal <name | number> [<name | number> ... <name | number>]
403 
404      Let's check if there's a signal name.  */
405 
406   if (arg != NULL)
407     filter = catch_signal_split_args (arg, &catch_all);
408 
409   create_signal_catchpoint (tempflag, std::move (filter), catch_all);
410 }
411 
412 void _initialize_break_catch_sig ();
413 void
414 _initialize_break_catch_sig ()
415 {
416   add_catch_command ("signal", _("\
417 Catch signals by their names and/or numbers.\n\
418 Usage: catch signal [[NAME|NUMBER] [NAME|NUMBER]...|all]\n\
419 Arguments say which signals to catch.  If no arguments\n\
420 are given, every \"normal\" signal will be caught.\n\
421 The argument \"all\" means to also catch signals used by GDB.\n\
422 Arguments, if given, should be one or more signal names\n\
423 (if your system supports that), or signal numbers."),
424 		     catch_signal_command,
425 		     signal_completer,
426 		     CATCH_PERMANENT,
427 		     CATCH_TEMPORARY);
428 }
429