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