This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFA: [2/2] catch signal


This rebases the "catch signal" patch on top of the previous
"catch-type" patch.  It addresses all the earlier comments.

Built and regtested on x86-64 Fedora 16.

Tom

2013-01-03  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-03  Tom Tromey  <tromey@redhat.com>

	* gdb.texinfo (Set Catchpoints): Document "catch signal".
	(Signals): Likewise.

2013-01-03  Tom Tromey  <tromey@redhat.com>

	* gdb.base/catch-signal.c: New file.
	* gdb.base/catch-signal.exp: New file.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b065d41..beb5bcb 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 75a2119..5f93d07 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -60,6 +60,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 0000000..89783b0
--- /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 81271c8..8961558 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
@@ -8358,7 +8376,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,
@@ -12874,7 +12892,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,
@@ -12893,6 +12919,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 52864f5..df9d366 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/gdb.texinfo b/gdb/doc/gdb.texinfo
index fd8e58e..ddb4244 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4241,6 +4241,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}
@@ -5333,6 +5358,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 0260e53..196d033 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 9105016..c0fbf0b 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/gdb.base/catch-signal.c b/gdb/testsuite/gdb.base/catch-signal.c
new file mode 100644
index 0000000..9c68185
--- /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 0000000..6c103af
--- /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
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]