RFC: implement "catch signal"

Jan Kratochvil jan.kratochvil@redhat.com
Sun Dec 2 09:38:00 GMT 2012


On Fri, 16 Nov 2012 20:06:40 +0100, Tom Tromey wrote:
[...]
> --- /dev/null
> +++ b/gdb/break-catch-sig.c
> @@ -0,0 +1,496 @@
> +/* 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"
> +
> +#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.  The list elements
> +     are allocated with xmalloc.  */
> +  VEC (gdb_signal_type) *signals_to_be_caught;
> +

Missing comment
/* If SIGNALS_TO_BE_CAUGHT is NULL then all the signals are caught,
   INTERNAL_SIGNAL are additionally caught only iff CATCH_ALL.  If
   SIGNALS_TO_BE_CAUGHT is not NULL then CATCH_ALL is undefined.  */

> +  int catch_all;
> +};
> +
> +/* The breakpoint_ops structure to be used in signal catchpoints.  */
> +
> +static struct breakpoint_ops signal_catchpoint_ops;
> +


> +/* An object of this type holds global information about all signal
> +   catchpoints.  */
> +
> +struct signal_catch_info
> +{
> +  /* Count of each signal.  */
> +
> +  unsigned int *counts;
> +};
> +
> +/* Global information about all signal catchpoints.  */
> +
> +static struct signal_catch_info signal_catch_info;

It is a single global variable, I find it overengineered.


[...]
> +/* 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;
> +  struct signal_catch_info *info = &signal_catch_info;
> +  int i;
> +  gdb_signal_type iter;
> +
> +  if (c->signals_to_be_caught != NULL)
> +    {
> +      for (i = 0;
> +	   VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter);
> +	   i++)

Here could be > 0 assert.

> +	--info->counts[iter];
> +    }
> +  else
> +    {
> +      for (i = 0; i < GDB_SIGNAL_LAST; ++i)
> +	{
> +	  if (c->catch_all || !INTERNAL_SIGNAL (i))
> +	    ++info->counts[i];

Here should be --.  And possibly an assert.

> +	}
> +    }
> +
> +  signal_catch_update (info->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 = gdb_signal_to_name (last.value.sig);

Here could be signal_to_name_or_int as signal number is not separately printed
here.

> +
> +  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);

Empty line before comment.

> +  /* 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;
> +      char *text = xstrdup ("");
> +
> +      for (i = 0;
> +           VEC_iterate (gdb_signal_type, c->signals_to_be_caught, i, iter);
> +           i++)
> +        {
> +          char *x = text;
> +	  const char *name = signal_to_name_or_int (iter);
> +
> +	  text = xstrprintf (i > 0 ? "%s, %s" : "%s%s", text, name);
> +	  xfree (x);
> +        }
> +      ui_out_field_string (uiout, "what", text);

>From the MI point of view ", " delimited signal names in a single field are
ugly.

> +      xfree (text);
> +    }
> +  else
> +    ui_out_field_string (uiout, "what",
> +			 c->catch_all ? "<any signal>" : "<standard signals>");
> +  ui_out_text (uiout, "\" ");
> +}
> +
> +/* 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);

Sometimes the delimited is " " and sometimes ", ", it may be for example less
convenient for copy-pastes.

(gdb) catch signal SIGQUIT SIGCHLD SIGINT
Catchpoint 1 (signals SIGQUIT SIGCHLD SIGINT)
(gdb) info breakpoints 
Num     Type           Disp Enb Address            What
1       catchpoint     keep y                      signals "SIGQUIT, SIGCHLD, SIGINT" 


> +        }
> +      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");
> +}
[...]
> +void
> +_initialize_break_catch_sig (void)
> +{
> +  initialize_signal_catchpoint_ops ();
> +
> +  signal_catch_info.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]...|all]\n\

For example:
	Usage: catch signal [[NAME|NUMBER] [NAME|NUMBER]...|all]\n\

to see there the delimiter is " " and not ", ".  Maybe not needed.


> +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);
> +}
[...]
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -318,6 +318,9 @@ static unsigned char *signal_stop;
>  static unsigned char *signal_print;
>  static unsigned char *signal_program;
>  
> +/* Table of signals that are registered with "catch signal".  */

Not descriptive enough:
/* Array of GDB_SIGNAL_LAST size with the count how many each signal is
   registered by "catch signal".  */

Although then also for signal_stop+signal_print+signal_program above:

/* Tables of how to react to signals; the user sets them.  */
->
/* Array of GDB_SIGNAL_LAST size with boolean value whether to "handle stop",
   "handle print" or "handle pass" respectively.  */


> +static unsigned int *signal_catch;
> +
>  /* Table of signals that the target may silently handle.
>     This is automatically determined from the flags above,
>     and simply cached here.  */
[...]
> @@ -6251,6 +6255,18 @@ signal_pass_update (int signo, int state)
>    return ret;
>  }
>  
> +/* Update the global 'signal_catch' from INFO and notify the
> +   target.  */
> +
> +void
> +signal_catch_update (unsigned int *info)
> +{
> +  memcpy (signal_catch, info, GDB_SIGNAL_LAST * sizeof (unsigned int));
> +  signal_cache_update (-1);
> +  target_pass_signals ((int) GDB_SIGNAL_LAST, signal_pass);

I do not see why to call target_program_signals here.

> +  target_program_signals ((int) GDB_SIGNAL_LAST, signal_program);
> +}
> +
>  static void
>  sig_print_header (void)
>  {
[...]
> --- /dev/null
> +++ b/gdb/testsuite/gdb.base/catch-signal.c
[...]
> +int
> +main ()
> +{
> +  signal (SIGHUP, handle);
> +  signal (SIGUSR1, SIG_IGN);
> +
> +  kill (getpid (), SIGHUP);	/* first HUP */

Why not raise ().

> +
> +  kill (getpid (), SIGHUP);	/* second HUP */
> +
> +  kill (getpid (), SIGHUP);	/* third HUP */
> +
> +  kill (getpid (), SIGHUP);	/* fourth HUP */
> +}
> +
[...]


Thanks,
Jan



More information about the Gdb-patches mailing list