summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog27
-rw-r--r--gdb/Makefile.in5
-rw-r--r--gdb/NEWS4
-rw-r--r--gdb/break-catch-sig.c508
-rw-r--r--gdb/breakpoint.c43
-rw-r--r--gdb/breakpoint.h35
-rw-r--r--gdb/doc/ChangeLog5
-rw-r--r--gdb/doc/gdb.texinfo29
-rw-r--r--gdb/inferior.h2
-rw-r--r--gdb/infrun.c256
-rw-r--r--gdb/testsuite/ChangeLog5
-rw-r--r--gdb/testsuite/gdb.base/catch-signal.c46
-rw-r--r--gdb/testsuite/gdb.base/catch-signal.exp129
13 files changed, 964 insertions, 130 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index f595d2e328d..90dd0c945ce 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,30 @@
+2013-01-16 Pedro Alves <palves@redhat.com>
+ Tom Tromey <tromey@redhat.com>
+
+ 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) <explains_signal>: 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 <tromey@redhat.com>
* breakpoint.c (print_one_catch_fork, print_one_catch_vfork)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b065d41bac8..beb5bcb8591 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 36bbd129d58..49d8447ed6d 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 00000000000..89783b03a8b
--- /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 <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "arch-utils.h"
+#include <ctype.h>
+#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 ? "<any signal>" : "<standard signals>");
+ 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 <name | number> [<name | number> ... <name | number>]
+
+ 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 bd6ceb89da7..3ad988516e1 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 52864f57cbc..df9d36615a6 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 906e56f9f72..5281095b302 100644
--- a/gdb/doc/ChangeLog
+++ b/gdb/doc/ChangeLog
@@ -1,5 +1,10 @@
2013-01-16 Tom Tromey <tromey@redhat.com>
+ * gdb.texinfo (Set Catchpoints): Document "catch signal".
+ (Signals): Likewise.
+
+2013-01-16 Tom Tromey <tromey@redhat.com>
+
* gdb.texinfo (GDB/MI Breakpoint Information): Document
"catch-type" field.
(GDB/MI Catchpoint Commands): Add "catch-type" to examples.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 89dc01086bd..0fb66016106 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 0260e539742..196d03362cd 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 910501603da..c0fbf0b6dce 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;
- }
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "infrun: stopped\n");
+ stop_print_frame = 0;
+ 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;
- }
+ /* 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;
+ }
- /* 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);
+ /* 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;
+ }
- /* 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");
+ /* 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);
- /* 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:
+ /* Following in case break condition called a
+ function. */
+ stop_print_frame = 1;
- ``End of a stack dummy. Some systems (e.g. Sony news) give
- another signal besides SIGTRAP, so check here as well as
- above.''
+ /* 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");
- 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: 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:
- NOTE: kettenis/2004-02-05: Indeed such checks don't seem to
- be necessary for call dummies on a non-executable stack on
- SPARC. */
+ ``End of a stack dummy. Some systems (e.g. Sony news) give
+ another signal besides SIGTRAP, so check here as well as
+ above.''
- 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 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.
- /* When we reach this point, we've pretty much decided
- that the reason for stopping must've been a random
- (unexpected) signal. */
+ 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 21d01ccf9a7..b883eac1736 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,10 @@
2013-01-16 Tom Tromey <tromey@redhat.com>
+ * gdb.base/catch-signal.c: New file.
+ * gdb.base/catch-signal.exp: New file.
+
+2013-01-16 Tom Tromey <tromey@redhat.com>
+
* gdb.mi/mi-catch-load.exp: Look for "catch-type".
2013-01-15 Jan Kratochvil <jan.kratochvil@redhat.com>
diff --git a/gdb/testsuite/gdb.base/catch-signal.c b/gdb/testsuite/gdb.base/catch-signal.c
new file mode 100644
index 00000000000..9c681852f11
--- /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 <http://www.gnu.org/licenses/>. */
+
+#include <signal.h>
+#include <unistd.h>
+
+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 00000000000..6c103af59b6
--- /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 <http://www.gnu.org/licenses/>.
+
+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
+}