Commit | Line | Data |
---|---|---|
ab04a2af TT |
1 | /* Everything about signal catchpoints, for GDB. |
2 | ||
61baf725 | 3 | Copyright (C) 2011-2017 Free Software Foundation, Inc. |
ab04a2af TT |
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" | |
45741a9c | 26 | #include "infrun.h" |
ab04a2af TT |
27 | #include "annotate.h" |
28 | #include "valprint.h" | |
29 | #include "cli/cli-utils.h" | |
30 | #include "completer.h" | |
5809899d TT |
31 | |
32 | #include <string> | |
ab04a2af TT |
33 | |
34 | #define INTERNAL_SIGNAL(x) ((x) == GDB_SIGNAL_TRAP || (x) == GDB_SIGNAL_INT) | |
35 | ||
36 | typedef enum gdb_signal gdb_signal_type; | |
37 | ||
38 | DEF_VEC_I (gdb_signal_type); | |
39 | ||
40 | /* An instance of this type is used to represent a signal catchpoint. | |
c1fc2657 | 41 | A breakpoint is really of this type iff its ops pointer points to |
ab04a2af TT |
42 | SIGNAL_CATCHPOINT_OPS. */ |
43 | ||
c1fc2657 | 44 | struct signal_catchpoint : public breakpoint |
ab04a2af | 45 | { |
c1fc2657 | 46 | ~signal_catchpoint () override; |
ab04a2af TT |
47 | |
48 | /* Signal numbers used for the 'catch signal' feature. If no signal | |
49 | has been specified for filtering, its value is NULL. Otherwise, | |
50 | it holds a list of all signals to be caught. */ | |
51 | ||
52 | VEC (gdb_signal_type) *signals_to_be_caught; | |
53 | ||
54 | /* If SIGNALS_TO_BE_CAUGHT is NULL, then all "ordinary" signals are | |
55 | caught. If CATCH_ALL is non-zero, then internal signals are | |
56 | caught as well. If SIGNALS_TO_BE_CAUGHT is non-NULL, then this | |
57 | field is ignored. */ | |
58 | ||
59 | int catch_all; | |
60 | }; | |
61 | ||
62 | /* The breakpoint_ops structure to be used in signal catchpoints. */ | |
63 | ||
64 | static struct breakpoint_ops signal_catchpoint_ops; | |
65 | ||
66 | /* Count of each signal. */ | |
67 | ||
68 | static unsigned int *signal_catch_counts; | |
69 | ||
70 | \f | |
71 | ||
72 | /* A convenience wrapper for gdb_signal_to_name that returns the | |
73 | integer value if the name is not known. */ | |
74 | ||
75 | static const char * | |
76 | signal_to_name_or_int (enum gdb_signal sig) | |
77 | { | |
78 | const char *result = gdb_signal_to_name (sig); | |
79 | ||
80 | if (strcmp (result, "?") == 0) | |
81 | result = plongest (sig); | |
82 | ||
83 | return result; | |
84 | } | |
85 | ||
86 | \f | |
87 | ||
c1fc2657 | 88 | /* signal_catchpoint destructor. */ |
ab04a2af | 89 | |
c1fc2657 | 90 | signal_catchpoint::~signal_catchpoint () |
ab04a2af | 91 | { |
c1fc2657 | 92 | VEC_free (gdb_signal_type, this->signals_to_be_caught); |
ab04a2af TT |
93 | } |
94 | ||
95 | /* Implement the "insert_location" breakpoint_ops method for signal | |
96 | catchpoints. */ | |
97 | ||
98 | static int | |
99 | signal_catchpoint_insert_location (struct bp_location *bl) | |
100 | { | |
9a3c8263 | 101 | struct signal_catchpoint *c = (struct signal_catchpoint *) bl->owner; |
ab04a2af TT |
102 | int i; |
103 | ||
104 | if (c->signals_to_be_caught != NULL) | |
105 | { | |
106 | gdb_signal_type iter; | |
107 | ||
108 | for (i = 0; | |
109 | VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); | |
110 | i++) | |
111 | ++signal_catch_counts[iter]; | |
112 | } | |
113 | else | |
114 | { | |
115 | for (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" breakpoint_ops method for signal | |
128 | catchpoints. */ | |
129 | ||
130 | static int | |
73971819 PA |
131 | signal_catchpoint_remove_location (struct bp_location *bl, |
132 | enum remove_bp_reason reason) | |
ab04a2af | 133 | { |
9a3c8263 | 134 | struct signal_catchpoint *c = (struct signal_catchpoint *) bl->owner; |
ab04a2af TT |
135 | int i; |
136 | ||
137 | if (c->signals_to_be_caught != NULL) | |
138 | { | |
139 | gdb_signal_type iter; | |
140 | ||
141 | for (i = 0; | |
142 | VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); | |
143 | i++) | |
144 | { | |
145 | gdb_assert (signal_catch_counts[iter] > 0); | |
146 | --signal_catch_counts[iter]; | |
147 | } | |
148 | } | |
149 | else | |
150 | { | |
151 | for (i = 0; i < GDB_SIGNAL_LAST; ++i) | |
152 | { | |
153 | if (c->catch_all || !INTERNAL_SIGNAL (i)) | |
154 | { | |
155 | gdb_assert (signal_catch_counts[i] > 0); | |
156 | --signal_catch_counts[i]; | |
157 | } | |
158 | } | |
159 | } | |
160 | ||
161 | signal_catch_update (signal_catch_counts); | |
162 | ||
163 | return 0; | |
164 | } | |
165 | ||
166 | /* Implement the "breakpoint_hit" breakpoint_ops method for signal | |
167 | catchpoints. */ | |
168 | ||
169 | static int | |
170 | signal_catchpoint_breakpoint_hit (const struct bp_location *bl, | |
171 | struct address_space *aspace, | |
172 | CORE_ADDR bp_addr, | |
173 | const struct target_waitstatus *ws) | |
174 | { | |
9a3c8263 SM |
175 | const struct signal_catchpoint *c |
176 | = (const struct signal_catchpoint *) bl->owner; | |
ab04a2af TT |
177 | gdb_signal_type signal_number; |
178 | ||
179 | if (ws->kind != TARGET_WAITKIND_STOPPED) | |
180 | return 0; | |
181 | ||
182 | signal_number = ws->value.sig; | |
183 | ||
184 | /* If we are catching specific signals in this breakpoint, then we | |
185 | must guarantee that the called signal is the same signal we are | |
186 | catching. */ | |
187 | if (c->signals_to_be_caught) | |
188 | { | |
189 | int i; | |
190 | gdb_signal_type iter; | |
191 | ||
192 | for (i = 0; | |
193 | VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); | |
194 | i++) | |
195 | if (signal_number == iter) | |
96f7d3f1 | 196 | return 1; |
ab04a2af | 197 | /* Not the same. */ |
96f7d3f1 PW |
198 | gdb_assert (!iter); |
199 | return 0; | |
ab04a2af | 200 | } |
96f7d3f1 PW |
201 | else |
202 | return c->catch_all || !INTERNAL_SIGNAL (signal_number); | |
ab04a2af TT |
203 | } |
204 | ||
205 | /* Implement the "print_it" breakpoint_ops method for signal | |
206 | catchpoints. */ | |
207 | ||
208 | static enum print_stop_action | |
209 | signal_catchpoint_print_it (bpstat bs) | |
210 | { | |
211 | struct breakpoint *b = bs->breakpoint_at; | |
212 | ptid_t ptid; | |
213 | struct target_waitstatus last; | |
214 | const char *signal_name; | |
f303dbd6 | 215 | struct ui_out *uiout = current_uiout; |
ab04a2af TT |
216 | |
217 | get_last_target_status (&ptid, &last); | |
218 | ||
219 | signal_name = signal_to_name_or_int (last.value.sig); | |
220 | ||
221 | annotate_catchpoint (b->number); | |
f303dbd6 | 222 | maybe_print_thread_hit_breakpoint (uiout); |
ab04a2af | 223 | |
f303dbd6 | 224 | printf_filtered (_("Catchpoint %d (signal %s), "), b->number, signal_name); |
ab04a2af TT |
225 | |
226 | return PRINT_SRC_AND_LOC; | |
227 | } | |
228 | ||
229 | /* Implement the "print_one" breakpoint_ops method for signal | |
230 | catchpoints. */ | |
231 | ||
232 | static void | |
233 | signal_catchpoint_print_one (struct breakpoint *b, | |
234 | struct bp_location **last_loc) | |
235 | { | |
9a3c8263 | 236 | struct signal_catchpoint *c = (struct signal_catchpoint *) b; |
ab04a2af TT |
237 | struct value_print_options opts; |
238 | struct ui_out *uiout = current_uiout; | |
239 | ||
240 | get_user_print_options (&opts); | |
241 | ||
242 | /* Field 4, the address, is omitted (which makes the columns | |
243 | not line up too nicely with the headers, but the effect | |
244 | is relatively readable). */ | |
245 | if (opts.addressprint) | |
112e8700 | 246 | uiout->field_skip ("addr"); |
ab04a2af TT |
247 | annotate_field (5); |
248 | ||
249 | if (c->signals_to_be_caught | |
250 | && VEC_length (gdb_signal_type, c->signals_to_be_caught) > 1) | |
112e8700 | 251 | uiout->text ("signals \""); |
ab04a2af | 252 | else |
112e8700 | 253 | uiout->text ("signal \""); |
ab04a2af TT |
254 | |
255 | if (c->signals_to_be_caught) | |
256 | { | |
257 | int i; | |
258 | gdb_signal_type iter; | |
5809899d | 259 | std::string text; |
ab04a2af TT |
260 | |
261 | for (i = 0; | |
262 | VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); | |
263 | i++) | |
264 | { | |
265 | const char *name = signal_to_name_or_int (iter); | |
266 | ||
267 | if (i > 0) | |
5809899d TT |
268 | text += " "; |
269 | text += name; | |
ab04a2af | 270 | } |
112e8700 | 271 | uiout->field_string ("what", text.c_str ()); |
ab04a2af TT |
272 | } |
273 | else | |
112e8700 | 274 | uiout->field_string ("what", |
ab04a2af | 275 | c->catch_all ? "<any signal>" : "<standard signals>"); |
112e8700 | 276 | uiout->text ("\" "); |
ab04a2af | 277 | |
112e8700 SM |
278 | if (uiout->is_mi_like_p ()) |
279 | uiout->field_string ("catch-type", "signal"); | |
ab04a2af TT |
280 | } |
281 | ||
282 | /* Implement the "print_mention" breakpoint_ops method for signal | |
283 | catchpoints. */ | |
284 | ||
285 | static void | |
286 | signal_catchpoint_print_mention (struct breakpoint *b) | |
287 | { | |
9a3c8263 | 288 | struct signal_catchpoint *c = (struct signal_catchpoint *) b; |
ab04a2af TT |
289 | |
290 | if (c->signals_to_be_caught) | |
291 | { | |
292 | int i; | |
293 | gdb_signal_type iter; | |
294 | ||
295 | if (VEC_length (gdb_signal_type, c->signals_to_be_caught) > 1) | |
296 | printf_filtered (_("Catchpoint %d (signals"), b->number); | |
297 | else | |
298 | printf_filtered (_("Catchpoint %d (signal"), b->number); | |
299 | ||
300 | for (i = 0; | |
301 | VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); | |
302 | i++) | |
303 | { | |
304 | const char *name = signal_to_name_or_int (iter); | |
305 | ||
306 | printf_filtered (" %s", name); | |
307 | } | |
308 | printf_filtered (")"); | |
309 | } | |
310 | else if (c->catch_all) | |
311 | printf_filtered (_("Catchpoint %d (any signal)"), b->number); | |
312 | else | |
313 | printf_filtered (_("Catchpoint %d (standard signals)"), b->number); | |
314 | } | |
315 | ||
316 | /* Implement the "print_recreate" breakpoint_ops method for signal | |
317 | catchpoints. */ | |
318 | ||
319 | static void | |
320 | signal_catchpoint_print_recreate (struct breakpoint *b, struct ui_file *fp) | |
321 | { | |
9a3c8263 | 322 | struct signal_catchpoint *c = (struct signal_catchpoint *) b; |
ab04a2af TT |
323 | |
324 | fprintf_unfiltered (fp, "catch signal"); | |
325 | ||
326 | if (c->signals_to_be_caught) | |
327 | { | |
328 | int i; | |
329 | gdb_signal_type iter; | |
330 | ||
331 | for (i = 0; | |
332 | VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); | |
333 | i++) | |
334 | fprintf_unfiltered (fp, " %s", signal_to_name_or_int (iter)); | |
335 | } | |
336 | else if (c->catch_all) | |
337 | fprintf_unfiltered (fp, " all"); | |
c780cc2f | 338 | fputc_unfiltered ('\n', fp); |
ab04a2af TT |
339 | } |
340 | ||
341 | /* Implement the "explains_signal" breakpoint_ops method for signal | |
342 | catchpoints. */ | |
343 | ||
47591c29 | 344 | static int |
427cd150 | 345 | signal_catchpoint_explains_signal (struct breakpoint *b, enum gdb_signal sig) |
ab04a2af | 346 | { |
47591c29 | 347 | return 1; |
ab04a2af TT |
348 | } |
349 | ||
350 | /* Create a new signal catchpoint. TEMPFLAG is true if this should be | |
351 | a temporary catchpoint. FILTER is the list of signals to catch; it | |
352 | can be NULL, meaning all signals. CATCH_ALL is a flag indicating | |
353 | whether signals used internally by gdb should be caught; it is only | |
354 | valid if FILTER is NULL. If FILTER is NULL and CATCH_ALL is zero, | |
355 | then internal signals like SIGTRAP are not caught. */ | |
356 | ||
357 | static void | |
358 | create_signal_catchpoint (int tempflag, VEC (gdb_signal_type) *filter, | |
359 | int catch_all) | |
360 | { | |
361 | struct signal_catchpoint *c; | |
362 | struct gdbarch *gdbarch = get_current_arch (); | |
363 | ||
4d01a485 | 364 | c = new signal_catchpoint (); |
c1fc2657 | 365 | init_catchpoint (c, gdbarch, tempflag, NULL, &signal_catchpoint_ops); |
ab04a2af TT |
366 | c->signals_to_be_caught = filter; |
367 | c->catch_all = catch_all; | |
368 | ||
c1fc2657 | 369 | install_breakpoint (0, c, 1); |
ab04a2af TT |
370 | } |
371 | ||
372 | ||
373 | /* Splits the argument using space as delimiter. Returns an xmalloc'd | |
374 | filter list, or NULL if no filtering is required. */ | |
375 | ||
376 | static VEC (gdb_signal_type) * | |
377 | catch_signal_split_args (char *arg, int *catch_all) | |
378 | { | |
379 | VEC (gdb_signal_type) *result = NULL; | |
380 | struct cleanup *cleanup = make_cleanup (VEC_cleanup (gdb_signal_type), | |
381 | &result); | |
382 | int first = 1; | |
383 | ||
384 | while (*arg != '\0') | |
385 | { | |
386 | int num; | |
387 | gdb_signal_type signal_number; | |
388 | char *one_arg, *endptr; | |
389 | struct cleanup *inner_cleanup; | |
390 | ||
391 | one_arg = extract_arg (&arg); | |
392 | if (one_arg == NULL) | |
393 | break; | |
394 | inner_cleanup = make_cleanup (xfree, one_arg); | |
395 | ||
396 | /* Check for the special flag "all". */ | |
397 | if (strcmp (one_arg, "all") == 0) | |
398 | { | |
399 | arg = skip_spaces (arg); | |
400 | if (*arg != '\0' || !first) | |
401 | error (_("'all' cannot be caught with other signals")); | |
402 | *catch_all = 1; | |
403 | gdb_assert (result == NULL); | |
404 | do_cleanups (inner_cleanup); | |
405 | discard_cleanups (cleanup); | |
406 | return NULL; | |
407 | } | |
408 | ||
409 | first = 0; | |
410 | ||
411 | /* Check if the user provided a signal name or a number. */ | |
412 | num = (int) strtol (one_arg, &endptr, 0); | |
413 | if (*endptr == '\0') | |
414 | signal_number = gdb_signal_from_command (num); | |
415 | else | |
416 | { | |
417 | signal_number = gdb_signal_from_name (one_arg); | |
418 | if (signal_number == GDB_SIGNAL_UNKNOWN) | |
419 | error (_("Unknown signal name '%s'."), one_arg); | |
420 | } | |
421 | ||
422 | VEC_safe_push (gdb_signal_type, result, signal_number); | |
423 | do_cleanups (inner_cleanup); | |
424 | } | |
425 | ||
426 | discard_cleanups (cleanup); | |
427 | return result; | |
428 | } | |
429 | ||
430 | /* Implement the "catch signal" command. */ | |
431 | ||
432 | static void | |
433 | catch_signal_command (char *arg, int from_tty, | |
434 | struct cmd_list_element *command) | |
435 | { | |
436 | int tempflag, catch_all = 0; | |
437 | VEC (gdb_signal_type) *filter; | |
ab04a2af TT |
438 | |
439 | tempflag = get_cmd_context (command) == CATCH_TEMPORARY; | |
440 | ||
441 | arg = skip_spaces (arg); | |
442 | ||
443 | /* The allowed syntax is: | |
444 | catch signal | |
445 | catch signal <name | number> [<name | number> ... <name | number>] | |
446 | ||
447 | Let's check if there's a signal name. */ | |
448 | ||
449 | if (arg != NULL) | |
450 | filter = catch_signal_split_args (arg, &catch_all); | |
451 | else | |
452 | filter = NULL; | |
453 | ||
454 | create_signal_catchpoint (tempflag, filter, catch_all); | |
455 | } | |
456 | ||
457 | static void | |
458 | initialize_signal_catchpoint_ops (void) | |
459 | { | |
460 | struct breakpoint_ops *ops; | |
461 | ||
462 | initialize_breakpoint_ops (); | |
463 | ||
464 | ops = &signal_catchpoint_ops; | |
465 | *ops = base_breakpoint_ops; | |
ab04a2af TT |
466 | ops->insert_location = signal_catchpoint_insert_location; |
467 | ops->remove_location = signal_catchpoint_remove_location; | |
468 | ops->breakpoint_hit = signal_catchpoint_breakpoint_hit; | |
469 | ops->print_it = signal_catchpoint_print_it; | |
470 | ops->print_one = signal_catchpoint_print_one; | |
471 | ops->print_mention = signal_catchpoint_print_mention; | |
472 | ops->print_recreate = signal_catchpoint_print_recreate; | |
473 | ops->explains_signal = signal_catchpoint_explains_signal; | |
474 | } | |
475 | ||
476 | initialize_file_ftype _initialize_break_catch_sig; | |
477 | ||
478 | void | |
479 | _initialize_break_catch_sig (void) | |
480 | { | |
481 | initialize_signal_catchpoint_ops (); | |
482 | ||
483 | signal_catch_counts = XCNEWVEC (unsigned int, GDB_SIGNAL_LAST); | |
484 | ||
485 | add_catch_command ("signal", _("\ | |
486 | Catch signals by their names and/or numbers.\n\ | |
487 | Usage: catch signal [[NAME|NUMBER] [NAME|NUMBER]...|all]\n\ | |
488 | Arguments say which signals to catch. If no arguments\n\ | |
489 | are given, every \"normal\" signal will be caught.\n\ | |
490 | The argument \"all\" means to also catch signals used by GDB.\n\ | |
491 | Arguments, if given, should be one or more signal names\n\ | |
492 | (if your system supports that), or signal numbers."), | |
493 | catch_signal_command, | |
494 | signal_completer, | |
495 | CATCH_PERMANENT, | |
496 | CATCH_TEMPORARY); | |
497 | } |