diff options
author | Pedro Alves <palves@redhat.com> | 2019-07-03 13:34:20 +0100 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2019-07-03 13:35:45 +0100 |
commit | fdbc98707b0ab48fd8ca3ac37acefa120496baf6 (patch) | |
tree | 5894a73e3e58bcd087918752ce0e0790ec4c14bf /gdb/cli | |
parent | c6ac893109fdc2c3fce2b7457adccdc10f235354 (diff) | |
download | gdb-fdbc98707b0ab48fd8ca3ac37acefa120496baf6.zip gdb-fdbc98707b0ab48fd8ca3ac37acefa120496baf6.tar.gz gdb-fdbc98707b0ab48fd8ca3ac37acefa120496baf6.tar.bz2 |
Introduce the "with" command
( See original discussion and prototype here:
https://sourceware.org/ml/gdb-patches/2019-05/msg00570.html )
(gdb) help with
Temporarily set SETTING to VALUE, run COMMAND, and restore SETTING.
Usage: with SETTING [VALUE] [-- COMMAND]
Usage: w SETTING [VALUE] [-- COMMAND]
With no COMMAND, repeats the last executed command.
SETTING is any setting you can change with the "set" subcommands.
E.g.:
with language pascal -- print obj
with print elements unlimited -- print obj
As can be seen above, the "with" command is just like "set", but
instead of setting the setting permanently, it sets the setting, runs
a command and then restores the setting.
(gdb) p g_s
$1 = {a = 1, b = 2, c = 3}
(gdb) with language ada -- print g_s
$2 = (a => 1, b => 2, c => 3)
Warning: the current language does not match this frame.
(gdb) show language
The current source language is "auto; currently c".
(gdb) with print elements 100 -- with print object on -- print 1
$3 = 1
You can shorten things a bit though, as long as unambiguous. So this:
(gdb) with print elements 100 -- with print object off -- print 1
is the same as:
(gdb) w p el 100 -- w p o 0 -- p 1
Note that the patch adds a "w" alias for "with", as "w" is not
currently taken:
(gdb) w
Ambiguous command "w": watch, wh, whatis, where, while, while-stepping, winheight, ws.
Let me know if you'd prefer to reserve "w" for one of the other
commands above. IMHO, this command will end up being used frequently
enough that it deserves the "w" shorthand.
A nice feature is that this is fully integrated with TAB-completion:
(gdb) with p[TAB]
pagination print prompt python
(gdb) with print [TAB]
address max-depth static-members
array max-symbolic-offset symbol
array-indexes null-stop symbol-filename
asm-demangle object symbol-loading
demangle pascal_static-members thread-events
elements pretty type
entry-values raw union
frame-arguments repeats vtbl
inferior-events sevenbit-strings
(gdb) with print [TAB]
(gdb) with print elements unlimited -- thread apply all -[TAB]
-ascending -c -q -s
(gdb) with print elements unlimited -- print -[TAB]
-address -max-depth -repeats -vtbl
-array -null-stop -static-members
-array-indexes -object -symbol
-elements -pretty -union
The main advantage of this new command compared to command options,
like the new "print -OPT", is that this command works with any
setting, and, it works nicely when you want to override a setting
while running a user-defined command, like:
(gdb) with print pretty -- usercmd
The disadvantage is that it isn't as compact or easy to type. I think
of command options and this command as complementary. I think that
even with this new command, it makes sense to continue developing the
command options in the direction of exposing most-oft-used settings as
command options.
Inspired by Philippe's "/" command proposal, if no command is
specified, then the last command is re-invoked, under the overridden
setting:
(gdb) p g_s
$1 = {a = 1, b = 2, c = 3}
(gdb) with language ada
$2 = (a => 1, b => 2, c => 3)
Warning: the current language does not match this frame.
Note: "with" requires "--" to separate the setting from the command.
It might be possible to do without that, but, I haven't tried it yet,
and I think that this can go in without it. We can always downgrade
to making "--" optional if we manage to make it work.
On to the patch itself, the implementation of the command is simpler
than one might expect. A few details:
- I factored out a bit from pipe_command into repeat_previous
directly, because otherwise I'd need to copy&paste the same code and
same error message in the with command.
- The parse_cli_var_uinteger / parse_cli_var_zuinteger_unlimited /
do_set_command changes are necessary since we can now pass an empty
string as argument.
- do_show_command was split in two, as a FIXME comment suggests, but
for a different reason: we need to get a string version of a "set"
command's value, and we already had code for that in
do_show_command. That code is now factored out to the new
get_setshow_command_value_string function.
- There's a new "maint with" command added too:
(gdb) help maint with
Like "with", but works with "maintenance set" variables.
Usage: maintenance with SETTING [VALUE] [-- COMMAND]
With no COMMAND, repeats the last executed command.
SETTING is any setting you can change with the "maintenance set"
subcommands.
"with" and "maint with" share 99% of the implementation.
This might be useful on its own, but it's also useful for testing,
since with this, we can use the "maint set/show test-settings"
settings for exercising the "with" machinery with all the command
type variants (all enum var_types). This is done in the new
gdb/base/with.exp testcase.
The documentation bits are originally based on Philippe's docs for the
"/" command, hence the attribution in the ChangeLog.
gdb/ChangeLog:
2019-07-03 Pedro Alves <palves@redhat.com>
* NEWS (New commands): Mention "with" and "maint with".
* cli/cli-cmds.c (with_command_1, with_command_completer_1)
(with_command, with_command_completer): New.
(pipe_command): Adjust to new repeat_previous
interface.
(_initialize_cli_cmds): Install the "with" command and its "w"
alias.
* cli/cli-cmds.h (with_command_1, with_command_completer_1): New
declarations.
* cli/cli-setshow.c (parse_cli_var_uinteger)
(parse_cli_var_zuinteger_unlimited, do_set_command): Handle empty
argument strings for all var_types.
(get_setshow_command_value_string): New, factored out from ...
(do_show_command): ... this.
* cli/cli-setshow.h: Include <string>.
(get_setshow_command_value_string): Declare.
* command.h (repeat_previous): Now returns const char *. Adjust
comment.
* maint.c: Include "cli/cli-cmds.h".
(maintenance_with_cmd, maintenance_with_cmd_completer): New.
(_initialize_maint_cmds): Register the "maintenance with" command.
* top.c (repeat_previous): Move bits from pipe_command here:
Return the saved command line, if any; error out if there's no
command to relaunch.
gdb/doc/ChangeLog:
2019-07-03 Pedro Alves <palves@redhat.com>
Philippe Waroquiers <philippe.waroquiers@skynet.be>
* gdb.texinfo (Command Settings): New node documenting the general
concept of settings, how to change them, and the new "with"
command.
(Maintenance Commands): Document "maint with".
gdb/testsuite/ChangeLog:
2019-07-03 Pedro Alves <palves@redhat.com>
* gdb.base/with.c: New file.
* gdb.base/with.exp: New file.
Diffstat (limited to 'gdb/cli')
-rw-r--r-- | gdb/cli/cli-cmds.c | 134 | ||||
-rw-r--r-- | gdb/cli/cli-cmds.h | 15 | ||||
-rw-r--r-- | gdb/cli/cli-setshow.c | 74 | ||||
-rw-r--r-- | gdb/cli/cli-setshow.h | 5 |
4 files changed, 187 insertions, 41 deletions
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index 5124ab3..d1ecd62 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -211,6 +211,116 @@ show_command (const char *arg, int from_tty) cmd_show_list (showlist, from_tty, ""); } +/* See cli/cli-cmds.h. */ + +void +with_command_1 (const char *set_cmd_prefix, + cmd_list_element *setlist, const char *args, int from_tty) +{ + const char *delim = strstr (args, "--"); + const char *nested_cmd = nullptr; + + if (delim == args) + error (_("Missing setting before '--' delimiter")); + + if (delim == nullptr || *skip_spaces (&delim[2]) == '\0') + nested_cmd = repeat_previous (); + + cmd_list_element *set_cmd = lookup_cmd (&args, setlist, set_cmd_prefix, + /*allow_unknown=*/ 0, + /*ignore_help_classes=*/ 1); + gdb_assert (set_cmd != nullptr); + + if (set_cmd->var == nullptr) + error (_("Cannot use this setting with the \"with\" command")); + + std::string temp_value + = (delim == nullptr ? args : std::string (args, delim - args)); + + if (nested_cmd == nullptr) + nested_cmd = skip_spaces (delim + 2); + + std::string org_value = get_setshow_command_value_string (set_cmd); + + /* Tweak the setting to the new temporary value. */ + do_set_command (temp_value.c_str (), from_tty, set_cmd); + + try + { + scoped_restore save_async = make_scoped_restore (¤t_ui->async, 0); + + /* Execute the nested command. */ + execute_command (nested_cmd, from_tty); + } + catch (const gdb_exception &ex) + { + /* Restore the setting and rethrow. If restoring the setting + throws, swallow the new exception and warn. There's nothing + else we can reasonably do. */ + try + { + do_set_command (org_value.c_str (), from_tty, set_cmd); + } + catch (const gdb_exception &ex2) + { + warning (_("Couldn't restore setting: %s"), ex2.what ()); + } + + throw; + } + + /* Restore the setting. */ + do_set_command (org_value.c_str (), from_tty, set_cmd); +} + +/* See cli/cli-cmds.h. */ + +void +with_command_completer_1 (const char *set_cmd_prefix, + completion_tracker &tracker, + const char *text) +{ + tracker.set_use_custom_word_point (true); + + const char *delim = strstr (text, "--"); + + /* If we're still not past the "--" delimiter, complete the "with" + command as if it was a "set" command. */ + if (delim == text + || delim == nullptr + || !isspace (delim[-1]) + || !(isspace (delim[2]) || delim[2] == '\0')) + { + std::string new_text = std::string (set_cmd_prefix) + text; + tracker.advance_custom_word_point_by (-(int) strlen (set_cmd_prefix)); + complete_nested_command_line (tracker, new_text.c_str ()); + return; + } + + /* We're past the "--" delimiter. Complete on the sub command. */ + const char *nested_cmd = skip_spaces (delim + 2); + tracker.advance_custom_word_point_by (nested_cmd - text); + complete_nested_command_line (tracker, nested_cmd); +} + +/* The "with" command. */ + +static void +with_command (const char *args, int from_tty) +{ + with_command_1 ("set ", setlist, args, from_tty); +} + +/* "with" command completer. */ + +static void +with_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char * /*word*/) +{ + with_command_completer_1 ("set ", tracker, text); +} + /* Provide documentation on command or list given by COMMAND. FROM_TTY is ignored. */ @@ -878,12 +988,7 @@ pipe_command (const char *arg, int from_tty) arg += delim.length (); /* Skip the delimiter. */ if (gdb_cmd.empty ()) - { - repeat_previous (); - gdb_cmd = skip_spaces (get_saved_command_line ()); - if (gdb_cmd.empty ()) - error (_("No previous command to relaunch")); - } + gdb_cmd = repeat_previous (); const char *shell_command = skip_spaces (arg); if (*shell_command == '\0') @@ -1850,6 +1955,23 @@ Generic command for showing things about the debugger."), /* Another way to get at the same thing. */ add_info ("set", show_command, _("Show all GDB settings.")); + c = add_com ("with", class_vars, with_command, _("\ +Temporarily set SETTING to VALUE, run COMMAND, and restore SETTING.\n\ +Usage: with SETTING [VALUE] [-- COMMAND]\n\ +Usage: w SETTING [VALUE] [-- COMMAND]\n\ +With no COMMAND, repeats the last executed command.\n\ +\n\ +SETTING is any setting you can change with the \"set\" subcommands.\n\ +E.g.:\n\ + with language pascal -- print obj\n\ + with print elements unlimited -- print obj\n\ +\n\ +You can change multiple settings using nested with, and use\n\ +abbreviations for commands and/or values. E.g.:\n\ + w la p -- w p el u -- p obj")); + set_cmd_completer_handle_brkchars (c, with_command_completer); + add_com_alias ("w", "with", class_vars, 1); + add_cmd ("commands", no_set_class, show_commands, _("\ Show the history of commands you typed.\n\ You can supply a command number to start with, or a `+' to start after\n\ diff --git a/gdb/cli/cli-cmds.h b/gdb/cli/cli-cmds.h index 63b8c40..94e210a 100644 --- a/gdb/cli/cli-cmds.h +++ b/gdb/cli/cli-cmds.h @@ -142,4 +142,19 @@ extern gdb::optional<open_script> extern int source_verbose; extern int trace_commands; +/* Common code for the "with" and "maintenance with" commands. + SET_CMD_PREFIX is the spelling of the corresponding "set" command + prefix: i.e., "set " or "maintenance set ". SETLIST is the command + element for the same "set" command prefix. */ +extern void with_command_1 (const char *set_cmd_prefix, + cmd_list_element *setlist, + const char *args, int from_tty); + +/* Common code for the completers of the "with" and "maintenance with" + commands. SET_CMD_PREFIX is the spelling of the corresponding + "set" command prefix: i.e., "set " or "maintenance set ". */ +extern void with_command_completer_1 (const char *set_cmd_prefix, + completion_tracker &tracker, + const char *text); + #endif /* CLI_CLI_CMDS_H */ diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c index d588d04..6fb3244 100644 --- a/gdb/cli/cli-setshow.c +++ b/gdb/cli/cli-setshow.c @@ -190,7 +190,7 @@ parse_cli_var_uinteger (var_types var_type, const char **arg, { LONGEST val; - if (*arg == nullptr) + if (*arg == nullptr || **arg == '\0') { if (var_type == var_uinteger) error_no_arg (_("integer to set it to, or \"unlimited\".")); @@ -225,7 +225,7 @@ parse_cli_var_zuinteger_unlimited (const char **arg, bool expression) { LONGEST val; - if (*arg == nullptr) + if (*arg == nullptr || **arg == '\0') error_no_arg (_("integer to set it to, or \"unlimited\".")); if (is_unlimited_literal (arg, expression)) @@ -308,6 +308,9 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) gdb_assert (c->type == set_cmd); + if (arg == NULL) + arg = ""; + switch (c->var_type) { case var_string: @@ -317,8 +320,6 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) char *q; int ch; - if (arg == NULL) - arg = ""; newobj = (char *) xmalloc (strlen (arg) + 2); p = arg; q = newobj; @@ -364,9 +365,6 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) } break; case var_string_noescape: - if (arg == NULL) - arg = ""; - if (*(char **) c->var == NULL || strcmp (*(char **) c->var, arg) != 0) { xfree (*(char **) c->var); @@ -376,14 +374,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) } break; case var_filename: - if (arg == NULL) + if (*arg == '\0') error_no_arg (_("filename to set it to.")); /* FALLTHROUGH */ case var_optional_filename: { char *val = NULL; - if (arg != NULL) + if (*arg != '\0') { /* Clear trailing whitespace of filename. */ const char *ptr = arg + strlen (arg) - 1; @@ -455,7 +453,7 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) { LONGEST val; - if (arg == NULL) + if (*arg == '\0') { if (c->var_type == var_integer) error_no_arg (_("integer to set it to, or \"unlimited\".")); @@ -625,24 +623,13 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) } } -/* Do a "show" command. ARG is NULL if no argument, or the - text of the argument, and FROM_TTY is nonzero if this command is - being entered directly by the user (i.e. these are just like any - other command). C is the command list element for the command. */ +/* See cli/cli-setshow.h. */ -void -do_show_command (const char *arg, int from_tty, struct cmd_list_element *c) +std::string +get_setshow_command_value_string (cmd_list_element *c) { - struct ui_out *uiout = current_uiout; - - gdb_assert (c->type == show_cmd); - string_file stb; - /* Possibly call the pre hook. */ - if (c->pre_show_hook) - (c->pre_show_hook) (c); - switch (c->var_type) { case var_string: @@ -672,9 +659,7 @@ do_show_command (const char *arg, int from_tty, struct cmd_list_element *c) stb.puts ("auto"); break; default: - internal_error (__FILE__, __LINE__, - _("do_show_command: " - "invalid var_auto_boolean")); + gdb_assert_not_reached ("invalid var_auto_boolean"); break; } break; @@ -703,23 +688,42 @@ do_show_command (const char *arg, int from_tty, struct cmd_list_element *c) } break; default: - error (_("gdb internal error: bad var_type in do_show_command")); + gdb_assert_not_reached ("bad var_type"); } + return std::move (stb.string ()); +} + + +/* Do a "show" command. ARG is NULL if no argument, or the + text of the argument, and FROM_TTY is nonzero if this command is + being entered directly by the user (i.e. these are just like any + other command). C is the command list element for the command. */ + +void +do_show_command (const char *arg, int from_tty, struct cmd_list_element *c) +{ + struct ui_out *uiout = current_uiout; + + gdb_assert (c->type == show_cmd); + + /* Possibly call the pre hook. */ + if (c->pre_show_hook) + (c->pre_show_hook) (c); + + std::string val = get_setshow_command_value_string (c); - /* FIXME: cagney/2005-02-10: Need to split this in half: code to - convert the value into a string (esentially the above); and - code to print the value out. For the latter there should be - MI and CLI specific versions. */ + /* FIXME: cagney/2005-02-10: There should be MI and CLI specific + versions of code to print the value out. */ if (uiout->is_mi_like_p ()) - uiout->field_stream ("value", stb); + uiout->field_string ("value", val.c_str ()); else { if (c->show_value_func != NULL) - c->show_value_func (gdb_stdout, from_tty, c, stb.c_str ()); + c->show_value_func (gdb_stdout, from_tty, c, val.c_str ()); else - deprecated_show_value_hack (gdb_stdout, from_tty, c, stb.c_str ()); + deprecated_show_value_hack (gdb_stdout, from_tty, c, val.c_str ()); } c->func (c, NULL, from_tty); diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h index c00a098..8bfe7e8 100644 --- a/gdb/cli/cli-setshow.h +++ b/gdb/cli/cli-setshow.h @@ -17,6 +17,8 @@ #ifndef CLI_CLI_SETSHOW_H #define CLI_CLI_SETSHOW_H +#include <string> + struct cmd_list_element; /* Parse ARG, an option to a boolean variable. @@ -55,6 +57,9 @@ extern void do_set_command (const char *arg, int from_tty, extern void do_show_command (const char *arg, int from_tty, struct cmd_list_element *c); +/* Get a string version of C's current value. */ +extern std::string get_setshow_command_value_string (cmd_list_element *c); + extern void cmd_show_list (struct cmd_list_element *list, int from_tty, const char *prefix); |