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.
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.
This commit is contained in:
Tom Tromey 2013-01-16 17:31:40 +00:00
parent 8ac3646fbb
commit ab04a2af2b
13 changed files with 971 additions and 137 deletions

View 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)

View 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 \

View File

@ -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.

508
gdb/break-catch-sig.c Normal file
View File

@ -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);
}

View 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. */

View 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

View 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

View 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}

View 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,

View 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;
}
/* This originates from attach_command(). We need to overwrite
the stop_signal here, because some kernels don't ignore a
SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call.
See more comments in inferior.h. On the other hand, if we
get a non-SIGSTOP, report it to the user - assume the backend
will handle the SIGSTOP if it should show up later.
Also consider that the attach is complete when we see a
SIGTRAP. Some systems (e.g. Windows), and stubs supporting
target extended-remote report it instead of a SIGSTOP
(e.g. gdbserver). We already rely on SIGTRAP being our
signal, so this is no exception.
Also consider that the attach is complete when we see a
GDB_SIGNAL_0. In non-stop mode, GDB will explicitly tell
the target to stop all threads of the inferior, in case the
low level attach operation doesn't stop them implicitly. If
they weren't stopped implicitly, then the stub will report a
GDB_SIGNAL_0, meaning: stopped for no particular reason
other than GDB's request. */
if (stop_soon == STOP_QUIETLY_NO_SIGSTOP
&& (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_STOP
|| ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
|| ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_0))
{
stop_stepping (ecs);
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
return;
}
/* See if there is a breakpoint/watchpoint/catchpoint/etc. that
handles this event. */
ecs->event_thread->control.stop_bpstat
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
stop_pc, ecs->ptid, &ecs->ws);
/* Following in case break condition called a
function. */
stop_print_frame = 1;
/* This is where we handle "moribund" watchpoints. Unlike
software breakpoints traps, hardware watchpoint traps are
always distinguishable from random traps. If no high-level
watchpoint is associated with the reported stop data address
anymore, then the bpstat does not explain the signal ---
simply make sure to ignore it if `stopped_by_watchpoint' is
set. */
if (debug_infrun
&& ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
&& !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat)
&& stopped_by_watchpoint)
fprintf_unfiltered (gdb_stdlog,
"infrun: no user watchpoint explains "
"watchpoint SIGTRAP, ignoring\n");
/* NOTE: cagney/2003-03-29: These two checks for a random signal
at one stage in the past included checks for an inferior
function call's call dummy's return breakpoint. The original
comment, that went with the test, read:
``End of a stack dummy. Some systems (e.g. Sony news) give
another signal besides SIGTRAP, so check here as well as
above.''
If someone ever tries to get call dummys on a
non-executable stack to work (where the target would stop
with something like a SIGSEGV), then those tests might need
to be re-instated. Given, however, that the tests were only
enabled when momentary breakpoints were not being used, I
suspect that it won't be the case.
NOTE: kettenis/2004-02-05: Indeed such checks don't seem to
be necessary for call dummies on a non-executable stack on
SPARC. */
if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
ecs->random_signal
= !(bpstat_explains_signal (ecs->event_thread->control.stop_bpstat)
|| stopped_by_watchpoint
|| ecs->event_thread->control.trap_expected
|| (ecs->event_thread->control.step_range_end
&& (ecs->event_thread->control.step_resume_breakpoint
== NULL)));
else
{
ecs->random_signal = !bpstat_explains_signal
(ecs->event_thread->control.stop_bpstat);
if (!ecs->random_signal)
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP;
}
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stopped\n");
stop_print_frame = 0;
stop_stepping (ecs);
return;
}
/* When we reach this point, we've pretty much decided
that the reason for stopping must've been a random
(unexpected) signal. */
/* This is originated from start_remote(), start_inferior() and
shared libraries hook functions. */
if (stop_soon == STOP_QUIETLY || stop_soon == STOP_QUIETLY_REMOTE)
{
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: quietly stopped\n");
stop_stepping (ecs);
return;
}
/* This originates from attach_command(). We need to overwrite
the stop_signal here, because some kernels don't ignore a
SIGSTOP in a subsequent ptrace(PTRACE_CONT,SIGSTOP) call.
See more comments in inferior.h. On the other hand, if we
get a non-SIGSTOP, report it to the user - assume the backend
will handle the SIGSTOP if it should show up later.
Also consider that the attach is complete when we see a
SIGTRAP. Some systems (e.g. Windows), and stubs supporting
target extended-remote report it instead of a SIGSTOP
(e.g. gdbserver). We already rely on SIGTRAP being our
signal, so this is no exception.
Also consider that the attach is complete when we see a
GDB_SIGNAL_0. In non-stop mode, GDB will explicitly tell
the target to stop all threads of the inferior, in case the
low level attach operation doesn't stop them implicitly. If
they weren't stopped implicitly, then the stub will report a
GDB_SIGNAL_0, meaning: stopped for no particular reason
other than GDB's request. */
if (stop_soon == STOP_QUIETLY_NO_SIGSTOP
&& (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_STOP
|| ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
|| ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_0))
{
stop_stepping (ecs);
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
return;
}
/* See if there is a breakpoint/watchpoint/catchpoint/etc. that
handles this event. */
ecs->event_thread->control.stop_bpstat
= bpstat_stop_status (get_regcache_aspace (get_current_regcache ()),
stop_pc, ecs->ptid, &ecs->ws);
/* Following in case break condition called a
function. */
stop_print_frame = 1;
/* This is where we handle "moribund" watchpoints. Unlike
software breakpoints traps, hardware watchpoint traps are
always distinguishable from random traps. If no high-level
watchpoint is associated with the reported stop data address
anymore, then the bpstat does not explain the signal ---
simply make sure to ignore it if `stopped_by_watchpoint' is
set. */
if (debug_infrun
&& ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
&& (bpstat_explains_signal (ecs->event_thread->control.stop_bpstat)
== BPSTAT_SIGNAL_NO)
&& stopped_by_watchpoint)
fprintf_unfiltered (gdb_stdlog,
"infrun: no user watchpoint explains "
"watchpoint SIGTRAP, ignoring\n");
/* NOTE: cagney/2003-03-29: These two checks for a random signal
at one stage in the past included checks for an inferior
function call's call dummy's return breakpoint. The original
comment, that went with the test, read:
``End of a stack dummy. Some systems (e.g. Sony news) give
another signal besides SIGTRAP, so check here as well as
above.''
If someone ever tries to get call dummys on a
non-executable stack to work (where the target would stop
with something like a SIGSEGV), then those tests might need
to be re-instated. Given, however, that the tests were only
enabled when momentary breakpoints were not being used, I
suspect that it won't be the case.
NOTE: kettenis/2004-02-05: Indeed such checks don't seem to
be necessary for call dummies on a non-executable stack on
SPARC. */
if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
ecs->random_signal
= !((bpstat_explains_signal (ecs->event_thread->control.stop_bpstat)
!= BPSTAT_SIGNAL_NO)
|| stopped_by_watchpoint
|| ecs->event_thread->control.trap_expected
|| (ecs->event_thread->control.step_range_end
&& (ecs->event_thread->control.step_resume_breakpoint
== NULL)));
else
ecs->random_signal = 1;
{
enum bpstat_signal_value sval;
sval = bpstat_explains_signal (ecs->event_thread->control.stop_bpstat);
ecs->random_signal = (sval == BPSTAT_SIGNAL_NO);
if (sval == BPSTAT_SIGNAL_HIDE)
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP;
}
process_event_stop_test:
@ -6205,7 +6211,8 @@ signal_cache_update (int signo)
signal_pass[signo] = (signal_stop[signo] == 0
&& signal_print[signo] == 0
&& signal_program[signo] == 1);
&& signal_program[signo] == 1
&& signal_catch[signo] == 0);
}
int
@ -6238,6 +6245,20 @@ signal_pass_update (int signo, int state)
return ret;
}
/* Update the global 'signal_catch' from INFO and notify the
target. */
void
signal_catch_update (const unsigned int *info)
{
int i;
for (i = 0; i < GDB_SIGNAL_LAST; ++i)
signal_catch[i] = info[i] > 0;
signal_cache_update (-1);
target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass);
}
static void
sig_print_header (void)
{
@ -7223,6 +7244,8 @@ leave it stopped or free to run as needed."),
xmalloc (sizeof (signal_print[0]) * numsigs);
signal_program = (unsigned char *)
xmalloc (sizeof (signal_program[0]) * numsigs);
signal_catch = (unsigned char *)
xmalloc (sizeof (signal_catch[0]) * numsigs);
signal_pass = (unsigned char *)
xmalloc (sizeof (signal_program[0]) * numsigs);
for (i = 0; i < numsigs; i++)
@ -7230,6 +7253,7 @@ leave it stopped or free to run as needed."),
signal_stop[i] = 1;
signal_print[i] = 1;
signal_program[i] = 1;
signal_catch[i] = 0;
}
/* Signals caused by debugger's own actions

View 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".

View File

@ -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 */
}

View File

@ -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
}