+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)
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 \
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 \
* 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.
--- /dev/null
+/* 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);
+}
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
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
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,
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,
base_breakpoint_create_sals_from_address,
base_breakpoint_create_breakpoints_sal,
base_breakpoint_decode_linespec,
+ base_breakpoint_explains_signal
};
/* Default breakpoint_ops methods. */
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. */
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
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);
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;
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
+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
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}
@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}
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,
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. */
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);
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)
{
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);
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)
{
= 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. */
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:
signal_pass[signo] = (signal_stop[signo] == 0
&& signal_print[signo] == 0
- && signal_program[signo] == 1);
+ && signal_program[signo] == 1
+ && signal_catch[signo] == 0);
}
int
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)
{
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++)
signal_stop[i] = 1;
signal_print[i] = 1;
signal_program[i] = 1;
+ signal_catch[i] = 0;
}
/* Signals caused by debugger's own actions
+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".
--- /dev/null
+/* 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 */
+}
+
--- /dev/null
+# 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
+}