From ab04a2af2bacfc1062c907630bee6e345dbd2ea9 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 16 Jan 2013 17:31:40 +0000 Subject: [PATCH] 2013-01-03 Pedro Alves Tom Tromey PR cli/7221: * NEWS: Add "catch signal". * breakpoint.c (base_breakpoint_ops): No longer static. (bpstat_explains_signal): New function. (init_catchpoint): No longer static. (base_breakpoint_explains_signal): New function. (base_breakpoint_ops): Initialize new field. * breakpoint.h (enum bpstat_signal_value): New. (struct breakpoint_ops) : New field. (bpstat_explains_signal): Remove macro, declare as function. (base_breakpoint_ops, init_catchpoint): Declare. * break-catch-sig.c: New file. * inferior.h (signal_catch_update): Declare. * infrun.c (signal_catch): New global. (handle_syscall_event): Update for change to bpstat_explains_signal. (handle_inferior_event): Likewise. Always handle random signals via bpstats. (signal_cache_update): Check signal_catch. (signal_catch_update): New function. (_initialize_infrun): Initialize signal_catch. * Makefile.in (SFILES): Add break-catch-sig.c. (COMMON_OBS): Add break-catch-sig.o. gdb/doc * gdb.texinfo (Set Catchpoints): Document "catch signal". (Signals): Likewise. gdb/testsuite * gdb.base/catch-signal.c: New file. * gdb.base/catch-signal.exp: New file. --- gdb/ChangeLog | 27 ++ gdb/Makefile.in | 5 +- gdb/NEWS | 4 + gdb/break-catch-sig.c | 508 ++++++++++++++++++++++++ gdb/breakpoint.c | 43 +- gdb/breakpoint.h | 35 +- gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 29 ++ gdb/inferior.h | 2 + gdb/infrun.c | 270 +++++++------ gdb/testsuite/ChangeLog | 5 + gdb/testsuite/gdb.base/catch-signal.c | 46 +++ gdb/testsuite/gdb.base/catch-signal.exp | 129 ++++++ 13 files changed, 971 insertions(+), 137 deletions(-) create mode 100644 gdb/break-catch-sig.c create mode 100644 gdb/testsuite/gdb.base/catch-signal.c create mode 100644 gdb/testsuite/gdb.base/catch-signal.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index f595d2e328..90dd0c945c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,30 @@ +2013-01-16 Pedro Alves + Tom Tromey + + PR cli/7221: + * NEWS: Add "catch signal". + * breakpoint.c (base_breakpoint_ops): No longer static. + (bpstat_explains_signal): New function. + (init_catchpoint): No longer static. + (base_breakpoint_explains_signal): New function. + (base_breakpoint_ops): Initialize new field. + * breakpoint.h (enum bpstat_signal_value): New. + (struct breakpoint_ops) : New field. + (bpstat_explains_signal): Remove macro, declare as function. + (base_breakpoint_ops, init_catchpoint): Declare. + * break-catch-sig.c: New file. + * inferior.h (signal_catch_update): Declare. + * infrun.c (signal_catch): New global. + (handle_syscall_event): Update for change to + bpstat_explains_signal. + (handle_inferior_event): Likewise. Always handle random signals + via bpstats. + (signal_cache_update): Check signal_catch. + (signal_catch_update): New function. + (_initialize_infrun): Initialize signal_catch. + * Makefile.in (SFILES): Add break-catch-sig.c. + (COMMON_OBS): Add break-catch-sig.o. + 2013-01-16 Tom Tromey * breakpoint.c (print_one_catch_fork, print_one_catch_vfork) diff --git a/gdb/Makefile.in b/gdb/Makefile.in index b065d41bac..beb5bcb859 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -699,7 +699,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ agent.c \ bcache.c \ bfd-target.c \ - block.c blockframe.c breakpoint.c buildsym.c \ + block.c blockframe.c breakpoint.c break-catch-sig.c buildsym.c \ c-exp.y c-lang.c c-typeprint.c c-valprint.c \ charset.c cleanups.c cli-out.c coffread.c coff-pe-read.c \ complaints.c completer.c continuations.c corefile.c corelow.c \ @@ -866,7 +866,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ auto-load.o auxv.o \ agent.o \ bfd-target.o \ - blockframe.o breakpoint.o findvar.o regcache.o cleanups.o \ + blockframe.o breakpoint.o break-catch-sig.o \ + findvar.o regcache.o cleanups.o \ charset.o continuations.o corelow.o disasm.o dummy-frame.o dfp.o \ source.o value.o eval.o valops.o valarith.o valprint.o printcmd.o \ block.o symtab.o psymtab.o symfile.o symmisc.o linespec.o dictionary.o \ diff --git a/gdb/NEWS b/gdb/NEWS index 36bbd129d5..49d8447ed6 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -65,6 +65,10 @@ Lynx 178 PowerPC powerpc-*-lynx*178 * New commands (for set/show, see "New options" below) +catch signal + Catch signals. This is similar to "handle", but allows commands and + conditions to be attached. + maint info bfds List the BFDs known to GDB. diff --git a/gdb/break-catch-sig.c b/gdb/break-catch-sig.c new file mode 100644 index 0000000000..89783b03a8 --- /dev/null +++ b/gdb/break-catch-sig.c @@ -0,0 +1,508 @@ +/* Everything about signal catchpoints, for GDB. + + Copyright (C) 2011, 2012 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "defs.h" +#include "arch-utils.h" +#include +#include "breakpoint.h" +#include "gdbcmd.h" +#include "inferior.h" +#include "annotate.h" +#include "valprint.h" +#include "cli/cli-utils.h" +#include "completer.h" +#include "gdb_obstack.h" + +#define INTERNAL_SIGNAL(x) ((x) == GDB_SIGNAL_TRAP || (x) == GDB_SIGNAL_INT) + +typedef enum gdb_signal gdb_signal_type; + +DEF_VEC_I (gdb_signal_type); + +/* An instance of this type is used to represent a signal catchpoint. + It includes a "struct breakpoint" as a kind of base class; users + downcast to "struct breakpoint *" when needed. A breakpoint is + really of this type iff its ops pointer points to + SIGNAL_CATCHPOINT_OPS. */ + +struct signal_catchpoint +{ + /* The base class. */ + + struct breakpoint base; + + /* Signal numbers used for the 'catch signal' feature. If no signal + has been specified for filtering, its value is NULL. Otherwise, + it holds a list of all signals to be caught. */ + + VEC (gdb_signal_type) *signals_to_be_caught; + + /* If SIGNALS_TO_BE_CAUGHT is NULL, then all "ordinary" signals are + caught. If CATCH_ALL is non-zero, then internal signals are + caught as well. If SIGNALS_TO_BE_CAUGHT is non-NULL, then this + field is ignored. */ + + int catch_all; +}; + +/* The breakpoint_ops structure to be used in signal catchpoints. */ + +static struct breakpoint_ops signal_catchpoint_ops; + +/* Count of each signal. */ + +static unsigned int *signal_catch_counts; + + + +/* A convenience wrapper for gdb_signal_to_name that returns the + integer value if the name is not known. */ + +static const char * +signal_to_name_or_int (enum gdb_signal sig) +{ + const char *result = gdb_signal_to_name (sig); + + if (strcmp (result, "?") == 0) + result = plongest (sig); + + return result; +} + + + +/* Implement the "dtor" breakpoint_ops method for signal + catchpoints. */ + +static void +signal_catchpoint_dtor (struct breakpoint *b) +{ + struct signal_catchpoint *c = (struct signal_catchpoint *) b; + + VEC_free (gdb_signal_type, c->signals_to_be_caught); + + base_breakpoint_ops.dtor (b); +} + +/* Implement the "insert_location" breakpoint_ops method for signal + catchpoints. */ + +static int +signal_catchpoint_insert_location (struct bp_location *bl) +{ + struct signal_catchpoint *c = (void *) bl->owner; + int i; + + if (c->signals_to_be_caught != NULL) + { + gdb_signal_type iter; + + for (i = 0; + VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); + i++) + ++signal_catch_counts[iter]; + } + else + { + for (i = 0; i < GDB_SIGNAL_LAST; ++i) + { + if (c->catch_all || !INTERNAL_SIGNAL (i)) + ++signal_catch_counts[i]; + } + } + + signal_catch_update (signal_catch_counts); + + return 0; +} + +/* Implement the "remove_location" breakpoint_ops method for signal + catchpoints. */ + +static int +signal_catchpoint_remove_location (struct bp_location *bl) +{ + struct signal_catchpoint *c = (void *) bl->owner; + int i; + + if (c->signals_to_be_caught != NULL) + { + gdb_signal_type iter; + + for (i = 0; + VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); + i++) + { + gdb_assert (signal_catch_counts[iter] > 0); + --signal_catch_counts[iter]; + } + } + else + { + for (i = 0; i < GDB_SIGNAL_LAST; ++i) + { + if (c->catch_all || !INTERNAL_SIGNAL (i)) + { + gdb_assert (signal_catch_counts[i] > 0); + --signal_catch_counts[i]; + } + } + } + + signal_catch_update (signal_catch_counts); + + return 0; +} + +/* Implement the "breakpoint_hit" breakpoint_ops method for signal + catchpoints. */ + +static int +signal_catchpoint_breakpoint_hit (const struct bp_location *bl, + struct address_space *aspace, + CORE_ADDR bp_addr, + const struct target_waitstatus *ws) +{ + const struct signal_catchpoint *c = (void *) bl->owner; + gdb_signal_type signal_number; + + if (ws->kind != TARGET_WAITKIND_STOPPED) + return 0; + + signal_number = ws->value.sig; + + /* If we are catching specific signals in this breakpoint, then we + must guarantee that the called signal is the same signal we are + catching. */ + if (c->signals_to_be_caught) + { + int i; + gdb_signal_type iter; + + for (i = 0; + VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); + i++) + if (signal_number == iter) + break; + /* Not the same. */ + if (!iter) + return 0; + } + + return c->catch_all || !INTERNAL_SIGNAL (signal_number); +} + +/* Implement the "print_it" breakpoint_ops method for signal + catchpoints. */ + +static enum print_stop_action +signal_catchpoint_print_it (bpstat bs) +{ + struct breakpoint *b = bs->breakpoint_at; + ptid_t ptid; + struct target_waitstatus last; + const char *signal_name; + + get_last_target_status (&ptid, &last); + + signal_name = signal_to_name_or_int (last.value.sig); + + annotate_catchpoint (b->number); + + printf_filtered (_("\nCatchpoint %d (signal %s), "), b->number, signal_name); + + return PRINT_SRC_AND_LOC; +} + +/* Implement the "print_one" breakpoint_ops method for signal + catchpoints. */ + +static void +signal_catchpoint_print_one (struct breakpoint *b, + struct bp_location **last_loc) +{ + struct signal_catchpoint *c = (void *) b; + struct value_print_options opts; + struct ui_out *uiout = current_uiout; + + get_user_print_options (&opts); + + /* Field 4, the address, is omitted (which makes the columns + not line up too nicely with the headers, but the effect + is relatively readable). */ + if (opts.addressprint) + ui_out_field_skip (uiout, "addr"); + annotate_field (5); + + if (c->signals_to_be_caught + && VEC_length (gdb_signal_type, c->signals_to_be_caught) > 1) + ui_out_text (uiout, "signals \""); + else + ui_out_text (uiout, "signal \""); + + if (c->signals_to_be_caught) + { + int i; + gdb_signal_type iter; + struct obstack text; + struct cleanup *cleanup; + + obstack_init (&text); + cleanup = make_cleanup_obstack_free (&text); + + for (i = 0; + VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); + i++) + { + const char *name = signal_to_name_or_int (iter); + + if (i > 0) + obstack_grow (&text, " ", 1); + obstack_grow (&text, name, strlen (name)); + } + obstack_grow (&text, "", 1); + ui_out_field_string (uiout, "what", obstack_base (&text)); + do_cleanups (cleanup); + } + else + ui_out_field_string (uiout, "what", + c->catch_all ? "" : ""); + ui_out_text (uiout, "\" "); + + if (ui_out_is_mi_like_p (uiout)) + ui_out_field_string (uiout, "catch-type", "signal"); +} + +/* Implement the "print_mention" breakpoint_ops method for signal + catchpoints. */ + +static void +signal_catchpoint_print_mention (struct breakpoint *b) +{ + struct signal_catchpoint *c = (void *) b; + + if (c->signals_to_be_caught) + { + int i; + gdb_signal_type iter; + + if (VEC_length (gdb_signal_type, c->signals_to_be_caught) > 1) + printf_filtered (_("Catchpoint %d (signals"), b->number); + else + printf_filtered (_("Catchpoint %d (signal"), b->number); + + for (i = 0; + VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); + i++) + { + const char *name = signal_to_name_or_int (iter); + + printf_filtered (" %s", name); + } + printf_filtered (")"); + } + else if (c->catch_all) + printf_filtered (_("Catchpoint %d (any signal)"), b->number); + else + printf_filtered (_("Catchpoint %d (standard signals)"), b->number); +} + +/* Implement the "print_recreate" breakpoint_ops method for signal + catchpoints. */ + +static void +signal_catchpoint_print_recreate (struct breakpoint *b, struct ui_file *fp) +{ + struct signal_catchpoint *c = (void *) b; + + fprintf_unfiltered (fp, "catch signal"); + + if (c->signals_to_be_caught) + { + int i; + gdb_signal_type iter; + + for (i = 0; + VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter); + i++) + fprintf_unfiltered (fp, " %s", signal_to_name_or_int (iter)); + } + else if (c->catch_all) + fprintf_unfiltered (fp, " all"); +} + +/* Implement the "explains_signal" breakpoint_ops method for signal + catchpoints. */ + +static enum bpstat_signal_value +signal_catchpoint_explains_signal (struct breakpoint *b) +{ + return BPSTAT_SIGNAL_PASS; +} + +/* Create a new signal catchpoint. TEMPFLAG is true if this should be + a temporary catchpoint. FILTER is the list of signals to catch; it + can be NULL, meaning all signals. CATCH_ALL is a flag indicating + whether signals used internally by gdb should be caught; it is only + valid if FILTER is NULL. If FILTER is NULL and CATCH_ALL is zero, + then internal signals like SIGTRAP are not caught. */ + +static void +create_signal_catchpoint (int tempflag, VEC (gdb_signal_type) *filter, + int catch_all) +{ + struct signal_catchpoint *c; + struct gdbarch *gdbarch = get_current_arch (); + + c = XNEW (struct signal_catchpoint); + init_catchpoint (&c->base, gdbarch, tempflag, NULL, &signal_catchpoint_ops); + c->signals_to_be_caught = filter; + c->catch_all = catch_all; + + install_breakpoint (0, &c->base, 1); +} + + +/* Splits the argument using space as delimiter. Returns an xmalloc'd + filter list, or NULL if no filtering is required. */ + +static VEC (gdb_signal_type) * +catch_signal_split_args (char *arg, int *catch_all) +{ + VEC (gdb_signal_type) *result = NULL; + struct cleanup *cleanup = make_cleanup (VEC_cleanup (gdb_signal_type), + &result); + int first = 1; + + while (*arg != '\0') + { + int num; + gdb_signal_type signal_number; + char *one_arg, *endptr; + struct cleanup *inner_cleanup; + + one_arg = extract_arg (&arg); + if (one_arg == NULL) + break; + inner_cleanup = make_cleanup (xfree, one_arg); + + /* Check for the special flag "all". */ + if (strcmp (one_arg, "all") == 0) + { + arg = skip_spaces (arg); + if (*arg != '\0' || !first) + error (_("'all' cannot be caught with other signals")); + *catch_all = 1; + gdb_assert (result == NULL); + do_cleanups (inner_cleanup); + discard_cleanups (cleanup); + return NULL; + } + + first = 0; + + /* Check if the user provided a signal name or a number. */ + num = (int) strtol (one_arg, &endptr, 0); + if (*endptr == '\0') + signal_number = gdb_signal_from_command (num); + else + { + signal_number = gdb_signal_from_name (one_arg); + if (signal_number == GDB_SIGNAL_UNKNOWN) + error (_("Unknown signal name '%s'."), one_arg); + } + + VEC_safe_push (gdb_signal_type, result, signal_number); + do_cleanups (inner_cleanup); + } + + discard_cleanups (cleanup); + return result; +} + +/* Implement the "catch signal" command. */ + +static void +catch_signal_command (char *arg, int from_tty, + struct cmd_list_element *command) +{ + int tempflag, catch_all = 0; + VEC (gdb_signal_type) *filter; + struct gdbarch *gdbarch = get_current_arch (); + + tempflag = get_cmd_context (command) == CATCH_TEMPORARY; + + arg = skip_spaces (arg); + + /* The allowed syntax is: + catch signal + catch signal [ ... ] + + Let's check if there's a signal name. */ + + if (arg != NULL) + filter = catch_signal_split_args (arg, &catch_all); + else + filter = NULL; + + create_signal_catchpoint (tempflag, filter, catch_all); +} + +static void +initialize_signal_catchpoint_ops (void) +{ + struct breakpoint_ops *ops; + + initialize_breakpoint_ops (); + + ops = &signal_catchpoint_ops; + *ops = base_breakpoint_ops; + ops->dtor = signal_catchpoint_dtor; + ops->insert_location = signal_catchpoint_insert_location; + ops->remove_location = signal_catchpoint_remove_location; + ops->breakpoint_hit = signal_catchpoint_breakpoint_hit; + ops->print_it = signal_catchpoint_print_it; + ops->print_one = signal_catchpoint_print_one; + ops->print_mention = signal_catchpoint_print_mention; + ops->print_recreate = signal_catchpoint_print_recreate; + ops->explains_signal = signal_catchpoint_explains_signal; +} + +initialize_file_ftype _initialize_break_catch_sig; + +void +_initialize_break_catch_sig (void) +{ + initialize_signal_catchpoint_ops (); + + signal_catch_counts = XCNEWVEC (unsigned int, GDB_SIGNAL_LAST); + + add_catch_command ("signal", _("\ +Catch signals by their names and/or numbers.\n\ +Usage: catch signal [[NAME|NUMBER] [NAME|NUMBER]...|all]\n\ +Arguments say which signals to catch. If no arguments\n\ +are given, every \"normal\" signal will be caught.\n\ +The argument \"all\" means to also catch signals used by GDB.\n\ +Arguments, if given, should be one or more signal names\n\ +(if your system supports that), or signal numbers."), + catch_signal_command, + signal_completer, + CATCH_PERMANENT, + CATCH_TEMPORARY); +} diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index bd6ceb89da..3ad988516e 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -279,14 +279,9 @@ static struct bp_location **get_first_locp_gte_addr (CORE_ADDR address); static int strace_marker_p (struct breakpoint *b); -static void init_catchpoint (struct breakpoint *b, - struct gdbarch *gdbarch, int tempflag, - char *cond_string, - const struct breakpoint_ops *ops); - /* The abstract base class all breakpoint_ops structures inherit from. */ -static struct breakpoint_ops base_breakpoint_ops; +struct breakpoint_ops base_breakpoint_ops; /* The breakpoint_ops structure to be inherited by all breakpoint_ops that are implemented on top of software or hardware breakpoints @@ -4152,6 +4147,29 @@ bpstat_find_breakpoint (bpstat bsp, struct breakpoint *breakpoint) return NULL; } +/* See breakpoint.h. */ + +enum bpstat_signal_value +bpstat_explains_signal (bpstat bsp) +{ + enum bpstat_signal_value result = BPSTAT_SIGNAL_NO; + + for (; bsp != NULL; bsp = bsp->next) + { + /* Ensure that, if we ever entered this loop, then we at least + return BPSTAT_SIGNAL_HIDE. */ + enum bpstat_signal_value newval = BPSTAT_SIGNAL_HIDE; + + if (bsp->breakpoint_at != NULL) + newval = bsp->breakpoint_at->ops->explains_signal (bsp->breakpoint_at); + + if (newval > result) + result = newval; + } + + return result; +} + /* Put in *NUM the breakpoint number of the first breakpoint we are stopped at. *BSP upon return is a bpstat which points to the remaining breakpoints stopped at (but which is not guaranteed to be @@ -8349,7 +8367,7 @@ syscall_catchpoint_p (struct breakpoint *b) not NULL, then store it in the breakpoint. OPS, if not NULL, is the breakpoint_ops structure associated to the catchpoint. */ -static void +void init_catchpoint (struct breakpoint *b, struct gdbarch *gdbarch, int tempflag, char *cond_string, @@ -12865,7 +12883,15 @@ base_breakpoint_decode_linespec (struct breakpoint *b, char **s, internal_error_pure_virtual_called (); } -static struct breakpoint_ops base_breakpoint_ops = +/* The default 'explains_signal' method. */ + +static enum bpstat_signal_value +base_breakpoint_explains_signal (struct breakpoint *b) +{ + return BPSTAT_SIGNAL_HIDE; +} + +struct breakpoint_ops base_breakpoint_ops = { base_breakpoint_dtor, base_breakpoint_allocate_location, @@ -12884,6 +12910,7 @@ static struct breakpoint_ops base_breakpoint_ops = base_breakpoint_create_sals_from_address, base_breakpoint_create_breakpoints_sal, base_breakpoint_decode_linespec, + base_breakpoint_explains_signal }; /* Default breakpoint_ops methods. */ diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 52864f57cb..df9d36615a 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -474,6 +474,22 @@ struct bp_location char *source_file; }; +/* Return values for bpstat_explains_signal. Note that the order of + the constants is important here; they are compared directly in + bpstat_explains_signal. */ + +enum bpstat_signal_value + { + /* bpstat does not explain this signal. */ + BPSTAT_SIGNAL_NO = 0, + + /* bpstat explains this signal; signal should not be delivered. */ + BPSTAT_SIGNAL_HIDE, + + /* bpstat explains this signal; signal should be delivered. */ + BPSTAT_SIGNAL_PASS + }; + /* This structure is a collection of function pointers that, if available, will be called instead of the performing the default action for this bptype. */ @@ -588,6 +604,12 @@ struct breakpoint_ops This function is called inside `addr_string_to_sals'. */ void (*decode_linespec) (struct breakpoint *, char **, struct symtabs_and_lines *); + + /* Return true if this breakpoint explains a signal, but the signal + should still be delivered to the inferior. This is used to make + 'catch signal' interact properly with 'handle'; see + bpstat_explains_signal. */ + enum bpstat_signal_value (*explains_signal) (struct breakpoint *); }; /* Helper for breakpoint_ops->print_recreate implementations. Prints @@ -980,10 +1002,9 @@ struct bpstat_what bpstat_what (bpstat); bpstat bpstat_find_breakpoint (bpstat, struct breakpoint *); /* Nonzero if a signal that we got in wait() was due to circumstances - explained by the BS. */ -/* Currently that is true if we have hit a breakpoint, or if there is - a watchpoint enabled. */ -#define bpstat_explains_signal(bs) ((bs) != NULL) + explained by the bpstat; and the signal should therefore not be + delivered. */ +extern enum bpstat_signal_value bpstat_explains_signal (bpstat); /* Nonzero is this bpstat causes a stop. */ extern int bpstat_causes_stop (bpstat); @@ -1183,6 +1204,7 @@ extern void awatch_command_wrapper (char *, int, int); extern void rwatch_command_wrapper (char *, int, int); extern void tbreak_command (char *, int); +extern struct breakpoint_ops base_breakpoint_ops; extern struct breakpoint_ops bkpt_breakpoint_ops; extern struct breakpoint_ops tracepoint_breakpoint_ops; @@ -1215,6 +1237,11 @@ extern void int tempflag, int from_tty); +extern void init_catchpoint (struct breakpoint *b, + struct gdbarch *gdbarch, int tempflag, + char *cond_string, + const struct breakpoint_ops *ops); + /* Add breakpoint B on the breakpoint list, and notify the user, the target and breakpoint_created observers of its existence. If INTERNAL is non-zero, the breakpoint number will be allocated from diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 906e56f9f7..5281095b30 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2013-01-16 Tom Tromey + + * gdb.texinfo (Set Catchpoints): Document "catch signal". + (Signals): Likewise. + 2013-01-16 Tom Tromey * gdb.texinfo (GDB/MI Breakpoint Information): Document diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 89dc01086b..0fb6601610 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -4234,6 +4234,31 @@ The loading or unloading of a shared library. If @var{regexp} is given, then the catchpoint will stop only if the regular expression matches one of the affected libraries. +@item signal @r{[}@var{signal}@dots{} @r{|} @samp{all}@r{]} +The delivery of a signal. + +With no arguments, this catchpoint will catch any signal that is not +used internally by @value{GDBN}, specifically, all signals except +@samp{SIGTRAP} and @samp{SIGINT}. + +With the argument @samp{all}, all signals, including those used by +@value{GDBN}, will be caught. This argument cannot be used with other +signal names. + +Otherwise, the arguments are a list of signal names as given to +@code{handle} (@pxref{Signals}). Only signals specified in this list +will be caught. + +One reason that @code{catch signal} can be more useful than +@code{handle} is that you can attach commands and conditions to the +catchpoint. + +When a signal is caught by a catchpoint, the signal's @code{stop} and +@code{print} settings, as specified by @code{handle}, are ignored. +However, whether the signal is still delivered to the inferior depends +on the @code{pass} setting; this can be changed in the catchpoint's +commands. + @end table @item tcatch @var{event} @@ -5326,6 +5351,10 @@ Similar, but print information only about the specified signal number. @code{info handle} is an alias for @code{info signals}. +@item catch signal @r{[}@var{signal}@dots{} @r{|} @samp{all}@r{]} +Set a catchpoint for the indicated signals. @xref{Set Catchpoints}, +for details about this command. + @kindex handle @item handle @var{signal} @r{[}@var{keywords}@dots{}@r{]} Change the way @value{GDBN} handles signal @var{signal}. @var{signal} diff --git a/gdb/inferior.h b/gdb/inferior.h index 0260e53974..196d03362c 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -632,6 +632,8 @@ extern void update_observer_mode (void); extern void update_signals_program_target (void); +extern void signal_catch_update (const unsigned int *); + /* In some circumstances we allow a command to specify a numeric signal. The idea is to keep these circumstances limited so that users (and scripts) develop portable habits. For comparison, diff --git a/gdb/infrun.c b/gdb/infrun.c index 910501603d..c0fbf0b6dc 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -318,6 +318,12 @@ static unsigned char *signal_stop; static unsigned char *signal_print; static unsigned char *signal_program; +/* Table of signals that are registered with "catch signal". A + non-zero entry indicates that the signal is caught by some "catch + signal" command. This has size GDB_SIGNAL_LAST, to accommodate all + signals. */ +static unsigned char *signal_catch; + /* Table of signals that the target may silently handle. This is automatically determined from the flags above, and simply cached here. */ @@ -3079,6 +3085,8 @@ handle_syscall_event (struct execution_control_state *ecs) if (catch_syscall_enabled () > 0 && catching_syscall_number (syscall_number) > 0) { + enum bpstat_signal_value sval; + if (debug_infrun) fprintf_unfiltered (gdb_stdlog, "infrun: syscall number = '%d'\n", syscall_number); @@ -3086,8 +3094,9 @@ handle_syscall_event (struct execution_control_state *ecs) ecs->event_thread->control.stop_bpstat = bpstat_stop_status (get_regcache_aspace (regcache), stop_pc, ecs->ptid, &ecs->ws); - ecs->random_signal - = !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat); + + sval = bpstat_explains_signal (ecs->event_thread->control.stop_bpstat); + ecs->random_signal = sval == BPSTAT_SIGNAL_NO; if (!ecs->random_signal) { @@ -3319,6 +3328,7 @@ handle_inferior_event (struct execution_control_state *ecs) if (stop_soon == NO_STOP_QUIETLY) { struct regcache *regcache; + enum bpstat_signal_value sval; if (!ptid_equal (ecs->ptid, inferior_ptid)) context_switch (ecs->ptid); @@ -3329,8 +3339,10 @@ handle_inferior_event (struct execution_control_state *ecs) ecs->event_thread->control.stop_bpstat = bpstat_stop_status (get_regcache_aspace (regcache), stop_pc, ecs->ptid, &ecs->ws); - ecs->random_signal - = !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat); + + sval + = bpstat_explains_signal (ecs->event_thread->control.stop_bpstat); + ecs->random_signal = sval == BPSTAT_SIGNAL_NO; if (!ecs->random_signal) { @@ -3628,7 +3640,8 @@ handle_inferior_event (struct execution_control_state *ecs) = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()), stop_pc, ecs->ptid, &ecs->ws); ecs->random_signal - = !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat); + = (bpstat_explains_signal (ecs->event_thread->control.stop_bpstat) + == BPSTAT_SIGNAL_NO); /* Note that this may be referenced from inside bpstat_stop_status above, through inferior_has_execd. */ @@ -4133,128 +4146,121 @@ handle_inferior_event (struct execution_control_state *ecs) will be made according to the signal handling tables. */ if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP - || stop_soon == STOP_QUIETLY || stop_soon == STOP_QUIETLY_NO_SIGSTOP - || stop_soon == STOP_QUIETLY_REMOTE) + && stop_after_trap) { - if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP - && stop_after_trap) - { - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: stopped\n"); - stop_print_frame = 0; - stop_stepping (ecs); - return; - } - - /* This is originated from start_remote(), start_inferior() and - shared libraries hook functions. */ - if (stop_soon == STOP_QUIETLY || stop_soon == STOP_QUIETLY_REMOTE) - { - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: quietly stopped\n"); - stop_stepping (ecs); - return; - } - - /* This originates from attach_command(). We need to overwrite - the stop_signal here, because some kernels don't ignore a - SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call. - See more comments in inferior.h. On the other hand, if we - get a non-SIGSTOP, report it to the user - assume the backend - will handle the SIGSTOP if it should show up later. - - Also consider that the attach is complete when we see a - SIGTRAP. Some systems (e.g. Windows), and stubs supporting - target extended-remote report it instead of a SIGSTOP - (e.g. gdbserver). We already rely on SIGTRAP being our - signal, so this is no exception. - - Also consider that the attach is complete when we see a - GDB_SIGNAL_0. In non-stop mode, GDB will explicitly tell - the target to stop all threads of the inferior, in case the - low level attach operation doesn't stop them implicitly. If - they weren't stopped implicitly, then the stub will report a - GDB_SIGNAL_0, meaning: stopped for no particular reason - other than GDB's request. */ - if (stop_soon == STOP_QUIETLY_NO_SIGSTOP - && (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_STOP - || ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP - || ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_0)) - { - stop_stepping (ecs); - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; - return; - } - - /* See if there is a breakpoint/watchpoint/catchpoint/etc. that - handles this event. */ - ecs->event_thread->control.stop_bpstat - = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()), - stop_pc, ecs->ptid, &ecs->ws); - - /* Following in case break condition called a - function. */ - stop_print_frame = 1; - - /* This is where we handle "moribund" watchpoints. Unlike - software breakpoints traps, hardware watchpoint traps are - always distinguishable from random traps. If no high-level - watchpoint is associated with the reported stop data address - anymore, then the bpstat does not explain the signal --- - simply make sure to ignore it if `stopped_by_watchpoint' is - set. */ - - if (debug_infrun - && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP - && !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat) - && stopped_by_watchpoint) - fprintf_unfiltered (gdb_stdlog, - "infrun: no user watchpoint explains " - "watchpoint SIGTRAP, ignoring\n"); - - /* NOTE: cagney/2003-03-29: These two checks for a random signal - at one stage in the past included checks for an inferior - function call's call dummy's return breakpoint. The original - comment, that went with the test, read: - - ``End of a stack dummy. Some systems (e.g. Sony news) give - another signal besides SIGTRAP, so check here as well as - above.'' - - If someone ever tries to get call dummys on a - non-executable stack to work (where the target would stop - with something like a SIGSEGV), then those tests might need - to be re-instated. Given, however, that the tests were only - enabled when momentary breakpoints were not being used, I - suspect that it won't be the case. - - NOTE: kettenis/2004-02-05: Indeed such checks don't seem to - be necessary for call dummies on a non-executable stack on - SPARC. */ - - if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) - ecs->random_signal - = !(bpstat_explains_signal (ecs->event_thread->control.stop_bpstat) - || stopped_by_watchpoint - || ecs->event_thread->control.trap_expected - || (ecs->event_thread->control.step_range_end - && (ecs->event_thread->control.step_resume_breakpoint - == NULL))); - else - { - ecs->random_signal = !bpstat_explains_signal - (ecs->event_thread->control.stop_bpstat); - if (!ecs->random_signal) - ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; - } + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: stopped\n"); + stop_print_frame = 0; + stop_stepping (ecs); + return; } - /* When we reach this point, we've pretty much decided - that the reason for stopping must've been a random - (unexpected) signal. */ + /* This is originated from start_remote(), start_inferior() and + shared libraries hook functions. */ + if (stop_soon == STOP_QUIETLY || stop_soon == STOP_QUIETLY_REMOTE) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "infrun: quietly stopped\n"); + stop_stepping (ecs); + return; + } + /* This originates from attach_command(). We need to overwrite + the stop_signal here, because some kernels don't ignore a + SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call. + See more comments in inferior.h. On the other hand, if we + get a non-SIGSTOP, report it to the user - assume the backend + will handle the SIGSTOP if it should show up later. + + Also consider that the attach is complete when we see a + SIGTRAP. Some systems (e.g. Windows), and stubs supporting + target extended-remote report it instead of a SIGSTOP + (e.g. gdbserver). We already rely on SIGTRAP being our + signal, so this is no exception. + + Also consider that the attach is complete when we see a + GDB_SIGNAL_0. In non-stop mode, GDB will explicitly tell + the target to stop all threads of the inferior, in case the + low level attach operation doesn't stop them implicitly. If + they weren't stopped implicitly, then the stub will report a + GDB_SIGNAL_0, meaning: stopped for no particular reason + other than GDB's request. */ + if (stop_soon == STOP_QUIETLY_NO_SIGSTOP + && (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_STOP + || ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP + || ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_0)) + { + stop_stepping (ecs); + ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0; + return; + } + + /* See if there is a breakpoint/watchpoint/catchpoint/etc. that + handles this event. */ + ecs->event_thread->control.stop_bpstat + = bpstat_stop_status (get_regcache_aspace (get_current_regcache ()), + stop_pc, ecs->ptid, &ecs->ws); + + /* Following in case break condition called a + function. */ + stop_print_frame = 1; + + /* This is where we handle "moribund" watchpoints. Unlike + software breakpoints traps, hardware watchpoint traps are + always distinguishable from random traps. If no high-level + watchpoint is associated with the reported stop data address + anymore, then the bpstat does not explain the signal --- + simply make sure to ignore it if `stopped_by_watchpoint' is + set. */ + + if (debug_infrun + && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP + && (bpstat_explains_signal (ecs->event_thread->control.stop_bpstat) + == BPSTAT_SIGNAL_NO) + && stopped_by_watchpoint) + fprintf_unfiltered (gdb_stdlog, + "infrun: no user watchpoint explains " + "watchpoint SIGTRAP, ignoring\n"); + + /* NOTE: cagney/2003-03-29: These two checks for a random signal + at one stage in the past included checks for an inferior + function call's call dummy's return breakpoint. The original + comment, that went with the test, read: + + ``End of a stack dummy. Some systems (e.g. Sony news) give + another signal besides SIGTRAP, so check here as well as + above.'' + + If someone ever tries to get call dummys on a + non-executable stack to work (where the target would stop + with something like a SIGSEGV), then those tests might need + to be re-instated. Given, however, that the tests were only + enabled when momentary breakpoints were not being used, I + suspect that it won't be the case. + + NOTE: kettenis/2004-02-05: Indeed such checks don't seem to + be necessary for call dummies on a non-executable stack on + SPARC. */ + + if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP) + ecs->random_signal + = !((bpstat_explains_signal (ecs->event_thread->control.stop_bpstat) + != BPSTAT_SIGNAL_NO) + || stopped_by_watchpoint + || ecs->event_thread->control.trap_expected + || (ecs->event_thread->control.step_range_end + && (ecs->event_thread->control.step_resume_breakpoint + == NULL))); else - ecs->random_signal = 1; + { + enum bpstat_signal_value sval; + + sval = bpstat_explains_signal (ecs->event_thread->control.stop_bpstat); + ecs->random_signal = (sval == BPSTAT_SIGNAL_NO); + + if (sval == BPSTAT_SIGNAL_HIDE) + ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP; + } process_event_stop_test: @@ -6205,7 +6211,8 @@ signal_cache_update (int signo) signal_pass[signo] = (signal_stop[signo] == 0 && signal_print[signo] == 0 - && signal_program[signo] == 1); + && signal_program[signo] == 1 + && signal_catch[signo] == 0); } int @@ -6238,6 +6245,20 @@ signal_pass_update (int signo, int state) return ret; } +/* Update the global 'signal_catch' from INFO and notify the + target. */ + +void +signal_catch_update (const unsigned int *info) +{ + int i; + + for (i = 0; i < GDB_SIGNAL_LAST; ++i) + signal_catch[i] = info[i] > 0; + signal_cache_update (-1); + target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass); +} + static void sig_print_header (void) { @@ -7223,6 +7244,8 @@ leave it stopped or free to run as needed."), xmalloc (sizeof (signal_print[0]) * numsigs); signal_program = (unsigned char *) xmalloc (sizeof (signal_program[0]) * numsigs); + signal_catch = (unsigned char *) + xmalloc (sizeof (signal_catch[0]) * numsigs); signal_pass = (unsigned char *) xmalloc (sizeof (signal_program[0]) * numsigs); for (i = 0; i < numsigs; i++) @@ -7230,6 +7253,7 @@ leave it stopped or free to run as needed."), signal_stop[i] = 1; signal_print[i] = 1; signal_program[i] = 1; + signal_catch[i] = 0; } /* Signals caused by debugger's own actions diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 21d01ccf9a..b883eac173 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2013-01-16 Tom Tromey + + * gdb.base/catch-signal.c: New file. + * gdb.base/catch-signal.exp: New file. + 2013-01-16 Tom Tromey * gdb.mi/mi-catch-load.exp: Look for "catch-type". diff --git a/gdb/testsuite/gdb.base/catch-signal.c b/gdb/testsuite/gdb.base/catch-signal.c new file mode 100644 index 0000000000..9c681852f1 --- /dev/null +++ b/gdb/testsuite/gdb.base/catch-signal.c @@ -0,0 +1,46 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2012 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include +#include + +void +do_nothing (void) +{ +} + +void +handle (int sig) +{ + do_nothing (); /* handle marker */ +} + +int +main () +{ + signal (SIGHUP, handle); + signal (SIGUSR1, SIG_IGN); + + raise (SIGHUP); /* first HUP */ + + raise (SIGHUP); /* second HUP */ + + raise (SIGHUP); /* third HUP */ + + raise (SIGHUP); /* fourth HUP */ +} + diff --git a/gdb/testsuite/gdb.base/catch-signal.exp b/gdb/testsuite/gdb.base/catch-signal.exp new file mode 100644 index 0000000000..6c103af59b --- /dev/null +++ b/gdb/testsuite/gdb.base/catch-signal.exp @@ -0,0 +1,129 @@ +# Copyright 2012 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if [target_info exists gdb,nosignals] { + verbose "Skipping sigall.exp because of nosignals." + continue +} + +standard_testfile + +if {[prepare_for_testing $testfile.exp $testfile $srcfile debug]} { + return -1 +} + +proc test_catch_signal {signame} { + global srcfile + + with_test_prefix $signame { + if {![runto_main]} { + return -1 + } + + # Test "catch signal" without arguments. + # Don't let the signal be handled otherwise. + gdb_breakpoint ${srcfile}:[gdb_get_line_number "first HUP"] + gdb_continue_to_breakpoint "first HUP" + gdb_test "handle SIGHUP nostop noprint pass" \ + "SIGHUP.*No.*No.*Yes.*" + gdb_test "catch signal" "Catchpoint .*" + gdb_test "continue" "Catchpoint .*" + + # Now ensure that the "pass" setting worked, and also that we did not + # see gdb's SIGTRAP. + gdb_breakpoint ${srcfile}:[gdb_get_line_number "handle marker"] + gdb_continue_to_breakpoint "handle marker" + + delete_breakpoints + + # Catch just $SIGNAME. + gdb_breakpoint ${srcfile}:[gdb_get_line_number "second HUP"] + gdb_continue_to_breakpoint "second HUP" + gdb_test "catch signal $signame" "Catchpoint .*" + gdb_test "continue" "Catchpoint .*" + delete_breakpoints + + # Catch just SIGUSR1 -- but it isn't sent. + gdb_breakpoint ${srcfile}:[gdb_get_line_number "third HUP"] + gdb_continue_to_breakpoint "third HUP" + gdb_test "handle SIGUSR1 nostop noprint pass" \ + "SIGUSR1.*No.*No.*Yes.*" + gdb_test "catch signal SIGUSR1" "Catchpoint .*" + + # Also verify that if we set SIGHUP to "nopass", then it is + # still not delivered. + gdb_breakpoint ${srcfile}:[gdb_get_line_number "handle marker"] + gdb_test "handle SIGHUP nostop noprint nopass" \ + "SIGHUP.*No.*No.*No.*" + + gdb_breakpoint ${srcfile}:[gdb_get_line_number "fourth HUP"] + gdb_continue_to_breakpoint "fourth HUP" + delete_breakpoints + } +} + +# Test with symbolic signal. +test_catch_signal SIGHUP + +# Test with numeric signal. +clean_restart $testfile +test_catch_signal 1 + +# Test with two signals in catchpoint. +clean_restart $testfile +test_catch_signal "SIGHUP SIGUSR2" + +# +# Coverage tests. +# + +gdb_test "catch signal SIGZARDOZ" "Unknown signal name 'SIGZARDOZ'." +gdb_test "catch signal all" "Catchpoint .*" +gdb_test "catch signal all SIGHUP" "'all' cannot be caught with other signals" +gdb_test "catch signal SIGHUP all" "'all' cannot be caught with other signals" + +set i 0 +foreach {arg desc} {"" "standard signals" \ + SIGHUP SIGHUP \ + "SIGHUP SIGUSR2" "SIGHUP SIGUSR2" \ + all "any signal"} { + delete_breakpoints + gdb_test "catch signal $arg" "Catchpoint .*" \ + "set catchpoint '$arg' for printing" + gdb_test "info break" "$decimal.*catchpoint.*signal.*$desc.*" \ + "info break for '$arg'" + gdb_test "save breakpoints [standard_output_file bps.$i]" \ + "Saved to file .*bps.$i.*" \ + "save breakpoints for '$arg'" + + set filename [remote_upload host [standard_output_file bps.$i] \ + [standard_output_file bps-local.$i]] + set fd [open $filename] + set contents [read -nonewline $fd] + close $fd + + if {$arg == ""} { + set pattern "catch signal" + } else { + set pattern "catch signal $arg" + } + if {[string match $pattern $contents]} { + pass "results of save breakpoints for '$arg'" + } else { + fail "results of save breakpoints for '$arg'" + } + + incr i +}