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