[RFC] Implement 'set|show error-handling abort-execution|print-and-continue|silently-ignore

Philippe Waroquiers philippe.waroquiers@skynet.be
Sat May 22 16:34:31 GMT 2021


This is an RFC of an alternative approach to the 'ignore-errors' command
proposed by Tom de Vries (see
e.g. https://sourceware.org/pipermail/gdb-patches/2021-May/178944.html).
FTR, a more elaborate try-catch solution was posted here (
https://sourceware.org/bugzilla/show_bug.cgi?id=8487 ).

It is still missing NEWS, documentation and tests.

This implements a new setting 'error-handling':
  (gdb) help set error-handling
  Set error handling for command sequences.
  Indicates how GDB handles an error when executing a sequence of commands
  (such as when sourcing command file or executing a user-defined command).
  When set to "abort-execution" (the default), GDB reports the error and aborts
  the execution of the command sequence.  When set to "print-and-continue",
  GDB reports the error and continues the execution of the command sequence.
  When set to "silently-ignore", GDB silently handles the error and continues
  the execution of the command sequence.
  (gdb)

Some usage examples:
Assuming you define a user command:
  define problem1
    echo coucou\n
    make-some-error
    echo bidule\n
  end

  (gdb) problem1
  coucou
  Undefined command: "make-some-error".  Try "help".
  (gdb) with error-handling print-and-continue -- problem1
  coucou
  Undefined command: "make-some-error".  Try "help".
  bidule
  (gdb) with error-handling silently-ignore -- problem1
  coucou
  bidule
  (gdb) alias S = with error-handling silently-ignore --
  (gdb) alias C = with error-handling print-and-continue --
  (gdb) S problem1
  coucou
  bidule
  (gdb) C problem1
  coucou
  Undefined command: "make-some-error".  Try "help".
  bidule
  (gdb) S source some_command_file_with_errors
  (gdb)

Inside a script, you can control error handling for individual commands,
e.g.:
  (gdb) shell cat runproblem1.gdb
  echo coucou\n
  with error-handling print-and-continue --  make-some-error
  echo bidule\n
  make-some-other-error
  echo machin\n
  (gdb) source runproblem1.gdb
  coucou
  Undefined command: "make-some-error".  Try "help".
  bidule
  runproblem1.gdb:4: Error in sourced command file:
  Undefined command: "make-some-other-error".  Try "help".
  (gdb)

It can also be used to ignore errors or continue on errors in .gdbinit:

  $ gdb -eiex 'set error-handling print-and-continue'
  GNU gdb (GDB) 11.0.50.20210522-git
  ...
  coucou
  Undefined command: "make-some-error".  Try "help".
  bidule
  (gdb)
---
 gdb/top.c | 247 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 141 insertions(+), 106 deletions(-)

diff --git a/gdb/top.c b/gdb/top.c
index b9635368cac..2e968ea9b8e 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -142,6 +142,10 @@ show_confirm (struct ui_file *file, int from_tty,
 		    value);
 }
 
+/* How to handle errors when executing a sequence of GDB commands (i.e. a
+   command not from_tty).  */
+static const char *error_handling;
+
 /* Current working directory.  */
 
 char *current_directory;
@@ -578,113 +582,125 @@ execute_command (const char *p, int from_tty)
   while (*p == ' ' || *p == '\t')
     p++;
   if (*p)
-    {
-      const char *cmd = p;
-      const char *arg;
-      std::string default_args;
-      std::string default_args_and_arg;
-      int was_sync = current_ui->prompt_state == PROMPT_BLOCKED;
-
-      line = p;
-
-      /* If trace-commands is set then this will print this command.  */
-      print_command_trace ("%s", p);
-
-      c = lookup_cmd (&cmd, cmdlist, "", &default_args, 0, 1);
-      p = cmd;
-
-      scoped_restore save_repeat_args
-	= make_scoped_restore (&repeat_arguments, nullptr);
-      const char *args_pointer = p;
-
-      if (!default_args.empty ())
-	{
-	  if (*p != '\0')
-	    default_args_and_arg = default_args + ' ' + p;
-	  else
-	    default_args_and_arg = default_args;
-	  arg = default_args_and_arg.c_str ();
-	}
-      else
-	{
-	  /* Pass null arg rather than an empty one.  */
-	  arg = *p == '\0' ? nullptr : p;
-	}
-
-      /* FIXME: cagney/2002-02-02: The c->type test is pretty dodgy
-	 while the is_complete_command(cfunc) test is just plain
-	 bogus.  They should both be replaced by a test of the form
-	 c->strip_trailing_white_space_p.  */
-      /* NOTE: cagney/2002-02-02: The function.cfunc in the below
-	 can't be replaced with func.  This is because it is the
-	 cfunc, and not the func, that has the value that the
-	 is_complete_command hack is testing for.  */
-      /* Clear off trailing whitespace, except for set and complete
-	 command.  */
-      std::string without_whitespace;
-      if (arg
-	  && c->type != set_cmd
-	  && !is_complete_command (c))
-	{
-	  const char *old_end = arg + strlen (arg) - 1;
-	  p = old_end;
-	  while (p >= arg && (*p == ' ' || *p == '\t'))
-	    p--;
-	  if (p != old_end)
-	    {
-	      without_whitespace = std::string (arg, p + 1);
-	      arg = without_whitespace.c_str ();
-	    }
-	}
-
-      /* If this command has been pre-hooked, run the hook first.  */
-      execute_cmd_pre_hook (c);
-
-      if (c->deprecated_warn_user)
-	deprecated_cmd_warning (line, cmdlist);
-
-      /* c->user_commands would be NULL in the case of a python command.  */
-      if (c->theclass == class_user && c->user_commands)
-	execute_user_command (c, arg);
-      else if (c->theclass == class_user
-	       && c->is_prefix () && !c->allow_unknown)
-	/* If this is a user defined prefix that does not allow unknown
-	   (in other words, C is a prefix command and not a command
-	   that can be followed by its args), report the list of
-	   subcommands.  */
-	{
-	  std::string prefixname = c->prefixname ();
-          std::string prefixname_no_space
-	    = prefixname.substr (0, prefixname.length () - 1);
-	  printf_unfiltered
-	    ("\"%s\" must be followed by the name of a subcommand.\n",
-	     prefixname_no_space.c_str ());
-	  help_list (*c->subcommands, prefixname.c_str (), all_commands,
-		     gdb_stdout);
-	}
-      else if (c->type == set_cmd)
-	do_set_command (arg, from_tty, c);
-      else if (c->type == show_cmd)
-	do_show_command (arg, from_tty, c);
-      else if (c->is_command_class_help ())
-	error (_("That is not a command, just a help topic."));
-      else if (deprecated_call_command_hook)
-	deprecated_call_command_hook (c, arg, from_tty);
-      else
-	cmd_func (c, arg, from_tty);
-
-      maybe_wait_sync_command_done (was_sync);
-
-      /* If this command has been post-hooked, run the hook last.  */
-      execute_cmd_post_hook (c);
+    try
+      {
+	const char *cmd = p;
+	const char *arg;
+	std::string default_args;
+	std::string default_args_and_arg;
+	int was_sync = current_ui->prompt_state == PROMPT_BLOCKED;
+
+	line = p;
+
+	/* If trace-commands is set then this will print this command.  */
+	print_command_trace ("%s", p);
+
+	c = lookup_cmd (&cmd, cmdlist, "", &default_args, 0, 1);
+	p = cmd;
+
+	scoped_restore save_repeat_args
+	  = make_scoped_restore (&repeat_arguments, nullptr);
+	const char *args_pointer = p;
+
+	if (!default_args.empty ())
+	  {
+	    if (*p != '\0')
+	      default_args_and_arg = default_args + ' ' + p;
+	    else
+	      default_args_and_arg = default_args;
+	    arg = default_args_and_arg.c_str ();
+	  }
+	else
+	  {
+	    /* Pass null arg rather than an empty one.  */
+	    arg = *p == '\0' ? nullptr : p;
+	  }
+
+	/* FIXME: cagney/2002-02-02: The c->type test is pretty dodgy
+	   while the is_complete_command(cfunc) test is just plain
+	   bogus.  They should both be replaced by a test of the form
+	   c->strip_trailing_white_space_p.  */
+	/* NOTE: cagney/2002-02-02: The function.cfunc in the below
+	   can't be replaced with func.  This is because it is the
+	   cfunc, and not the func, that has the value that the
+	   is_complete_command hack is testing for.  */
+	/* Clear off trailing whitespace, except for set and complete
+	   command.  */
+	std::string without_whitespace;
+	if (arg
+	    && c->type != set_cmd
+	    && !is_complete_command (c))
+	  {
+	    const char *old_end = arg + strlen (arg) - 1;
+	    p = old_end;
+	    while (p >= arg && (*p == ' ' || *p == '\t'))
+	      p--;
+	    if (p != old_end)
+	      {
+		without_whitespace = std::string (arg, p + 1);
+		arg = without_whitespace.c_str ();
+	      }
+	  }
+
+	/* If this command has been pre-hooked, run the hook first.  */
+	execute_cmd_pre_hook (c);
+
+	if (c->deprecated_warn_user)
+	  deprecated_cmd_warning (line, cmdlist);
+
+	/* c->user_commands would be NULL in the case of a python command.  */
+	if (c->theclass == class_user && c->user_commands)
+	  execute_user_command (c, arg);
+	else if (c->theclass == class_user
+		 && c->is_prefix () && !c->allow_unknown)
+	  /* If this is a user defined prefix that does not allow unknown
+	     (in other words, C is a prefix command and not a command
+	     that can be followed by its args), report the list of
+	     subcommands.  */
+	  {
+	    std::string prefixname = c->prefixname ();
+	    std::string prefixname_no_space
+	      = prefixname.substr (0, prefixname.length () - 1);
+	    printf_unfiltered
+	      ("\"%s\" must be followed by the name of a subcommand.\n",
+	       prefixname_no_space.c_str ());
+	    help_list (*c->subcommands, prefixname.c_str (), all_commands,
+		       gdb_stdout);
+	  }
+	else if (c->type == set_cmd)
+	  do_set_command (arg, from_tty, c);
+	else if (c->type == show_cmd)
+	  do_show_command (arg, from_tty, c);
+	else if (c->is_command_class_help ())
+	  error (_("That is not a command, just a help topic."));
+	else if (deprecated_call_command_hook)
+	  deprecated_call_command_hook (c, arg, from_tty);
+	else
+	  cmd_func (c, arg, from_tty);
+
+	maybe_wait_sync_command_done (was_sync);
+
+	/* If this command has been post-hooked, run the hook last.  */
+	execute_cmd_post_hook (c);
+
+	if (repeat_arguments != NULL && cmd_start == saved_command_line)
+	  {
+	    gdb_assert (strlen (args_pointer) >= strlen (repeat_arguments));
+	    strcpy (saved_command_line + (args_pointer - cmd_start),
+		    repeat_arguments);
+	  }
+      }
+    catch (const gdb_exception &ex)
+      {
+	if (from_tty || strcmp (error_handling, "abort-execution") == 0)
+	  throw;
+	else if (strcmp (error_handling, "print-and-continue") == 0)
+	  exception_print (gdb_stderr, ex);
+	/* else silently-ignore.  */
 
-      if (repeat_arguments != NULL && cmd_start == saved_command_line)
-	{
-	  gdb_assert (strlen (args_pointer) >= strlen (repeat_arguments));
-	  strcpy (saved_command_line + (args_pointer - cmd_start),
-		  repeat_arguments);
-	}
-    }
+	/* See also execute_gdb_command.  */
+	async_enable_stdin ();
+      }
 
   /* Only perform the frame-language-change check if the command
      we just finished executing did not resume the inferior's execution.
@@ -2203,6 +2219,9 @@ init_gdb_version_vars (void)
 static void
 init_main (void)
 {
+  static const char *const error_handling_names[]
+    = { "abort-execution", "print-and-continue", "silently-ignore", NULL };
+
   struct cmd_list_element *c;
 
   /* Initialize the prompt to a simple "(gdb) " prompt or to whatever
@@ -2356,6 +2375,22 @@ affect future GDB sessions."),
 			       show_startup_quiet,
 			       &setlist, &showlist);
 
+  add_setshow_enum_cmd ("error-handling", class_support, error_handling_names,
+			&error_handling,
+			_("Set error handling for command sequences."),
+			_("Show error handling for command sequences."),
+			_("Indicates how GDB handles an error when executing a sequence of commands\n\
+(such as when sourcing command file or executing a user-defined command).\n\
+When set to \"abort-execution\" (the default), GDB reports the error and aborts\n\
+the execution of the command sequence.  When set to \"print-and-continue\",\n\
+GDB reports the error and continues the execution of the command sequence.\n\
+When set to \"silently-ignore\", GDB silently handles the error and continues\n\
+the execution of the command sequence."),
+			NULL,
+			NULL,
+			&setlist, &showlist);
+  error_handling = "abort-execution";
+
   c = add_cmd ("new-ui", class_support, new_ui_command, _("\
 Create a new UI.\n\
 Usage: new-ui INTERPRETER TTY\n\
-- 
2.20.1



More information about the Gdb-patches mailing list