2013-01-03 Pedro Alves <palves@redhat.com>
authorTom Tromey <tromey@redhat.com>
Wed, 16 Jan 2013 17:31:40 +0000 (17:31 +0000)
committerTom Tromey <tromey@redhat.com>
Wed, 16 Jan 2013 17:31:40 +0000 (17:31 +0000)
    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.
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.

13 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/break-catch-sig.c [new file with mode: 0644]
gdb/breakpoint.c
gdb/breakpoint.h
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/inferior.h
gdb/infrun.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/catch-signal.c [new file with mode: 0644]
gdb/testsuite/gdb.base/catch-signal.exp [new file with mode: 0644]

index f595d2e328da525acd012b5d6e40502176f6f629..90dd0c945ce93db20031b5b7f0bfb46d2387c444 100644 (file)
@@ -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)
index b065d41bac867e28cf2e51bbe34e84dea2372c66..beb5bcb8591da428465adf0f1bce9ca49262d036 100644 (file)
@@ -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 \
index 36bbd129d58f57ae29ad185584aaf7c848846478..49d8447ed6deea5152928550a07921db9f2e0939 100644 (file)
--- 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 (file)
index 0000000..89783b0
--- /dev/null
@@ -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;
+
+\f
+
+/* 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;
+}
+
+\f
+
+/* 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);
+}
index bd6ceb89da72d15f5ba9b1c564b511a4f50d6095..3ad988516e169027c81e1f6c8f26442320e13689 100644 (file)
@@ -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.  */
index 52864f57cbcbf3ef0601f1d69c87cd0e3f924717..df9d36615a6b356b227f226a2e295ccadd5536f7 100644 (file)
@@ -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
index 906e56f9f7288cba690fdcf843d14811796da71c..5281095b302414d789760d361e704d56c0e5e7fa 100644 (file)
@@ -1,3 +1,8 @@
+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
index 89dc01086bd79c53ea908ce7b1323ebe6bf424f5..0fb6601610641bb9f515b818e655dcf2ea0a8c6e 100644 (file)
@@ -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}
index 0260e539742cd571c32cf319c5154cee491e2f8f..196d03362cded384bb1023ece35b6b7f39dd361d 100644 (file)
@@ -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,
index 910501603da0d1c4547ee2e713428eb6f5eac673..c0fbf0b6dce2327d50b68b0cf7e1a8e7f0c1dc1c 100644 (file)
@@ -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
index 21d01ccf9a719e47cb3301db3d455428aaaa4960..b883eac173684d24473d081b82d04702030b0c23 100644 (file)
@@ -1,3 +1,8 @@
+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".
diff --git a/gdb/testsuite/gdb.base/catch-signal.c b/gdb/testsuite/gdb.base/catch-signal.c
new file mode 100644 (file)
index 0000000..9c68185
--- /dev/null
@@ -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 (file)
index 0000000..6c103af
--- /dev/null
@@ -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
+}