[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