Compare commits

...

7 Commits

Author SHA1 Message Date
Pedro Alves
cc0a76bef5 pipe completer & var_string options 2019-06-25 18:35:19 +01:00
Pedro Alves
2d3081ce81 Make complete_options save arguments too 2019-06-25 17:48:20 +01:00
Pedro Alves
4b4de9f875 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:
yyyy-mm-dd  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:
yyyy-mm-dd  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:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/with.c: New file.
	* gdb.base/with.exp: New file.
2019-06-22 21:24:50 +01:00
Pedro Alves
47f969f4d9 "maint test-settings set/show" -> "maint set/show test-settings"
This commit renames "maint test-settings set/show" to "maint set/show
test-settings".

This helps the following patch, which introduce a "maint with" command
what works with all "maint set" settings.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* NEWS (New commands): Mention "maint set/show test-settings"
	instead of "maint test-settings".
	* maint-test-settings.c (maintenance_test_settings_list): Delete.
	(maintenance_test_settings_set_list): Rename to ...
	(maintenance_set_test_settings_list): ... this.
	(maintenance_test_settings_show_list): Rename to  ...
	(maintenance_show_test_settings_list): ... this.
	(maintenance_test_settings_cmd): Delete.
	(maintenance_test_settings_set_cmd): ...
	(maintenance_set_test_settings_cmd): ... this.
	(maintenance_test_settings_show_cmd): ...
	(maintenance_show_test_settings_cmd): ... this.
	(maintenance_test_settings_show_value_cmd):
	(maintenance_show_test_settings_value_cmd): ... this.
	(_initialize_maint_test_settings): No longer install the "maint
	test-settings" prefix command.  Rename "maint test-settings set"
	to "maint set test-settings", and "maint test-settings show" to
	"maint show test-settings".  Adjust all subcommands.

gdb/doc/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.texinfo (Maintenance Commands): Document "maint set/show
	test-settings" instead of "maint test-settings set/show".

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/settings.exp: Replace all references to "maint
	test-settings set" with references to "maint set test-settings",
	and all references to "maint test-settings show" with references
	to "maint show test-settings".
2019-06-22 21:24:50 +01:00
Pedro Alves
03c6774d01 Fix a few comments in maint-test-settings.c
Fix the file's intro comment, and s/test-options/test-settings/.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* maint-test-settings.c: Fix file's intro comment.  Replace all
	references to "test-options" with references to "test-settings",
	in comments.
2019-06-22 21:24:50 +01:00
Pedro Alves
67f57a9be2 Fix defaults of some "maint test-settings" subcommands
New tests added later for the incoming "with" command exposed a couple
invalid-default-value bugs in the "maint test-settings" commands:

- var_filename commands don't allow setting the filename to the empty
  string (unlike var_optional_filename commands), yet, "maint
  test-settings filename"'s control variable was not initialized, so
  on startup, "maint test-settings show filename" shows an empty
  string.

- "maint test-settings enum"'s control variable was not initialized,
  so on startup, "maint test-settings show enum" shows an empty value
  instead of a valid enum value.

Both issues are fixed by initializing the control variables.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* maint-test-settings.c (maintenance_test_settings_xxx)
	(maintenance_test_settings_yyy, maintenance_test_settings_zzz):
	New.
	(maintenance_test_settings_enums): Use them.
	(maintenance_test_settings_enum): Default to
	maintenance_test_settings_xxx.
	(_initialize_maint_test_settings): Initialize
	MAINTENANCE_TEST_SETTINGS_FILENAME.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/settings.exp (test-string): Adjust expected out when
	testing "maint test-settings show filename"
2019-06-22 21:24:50 +01:00
Pedro Alves
9507d5238b Make "info threads" use the gdb::option framework
I had this in my local "print -OPT" branch, but had decided not to
include in the original series, because it's not as useful as the
others.  I'm sending it now, since I had it written anyway.

This makes "info threads" use the gdb::option framework to process
options.  There's only one option today (-gid), and it isn't used much
frequently unless you're looking at matching MI output.  Still, this
was in the neighborhood of "thread apply" so I had converted it.

The main advantage is that TAB completion now shows you the available
options, and gives you a hint to what the command accepts as operand
argument, including showing a metasyntactic variable:

  (gdb) info threads [TAB]
  -gid  ID

  (gdb) help info threads
  Display currently known threads.
  Usage: info threads [OPTION]... [ID]...

  Options:
    -gid
      Show global thread IDs.

  If ID is given, it is a space-separated list of IDs of threads to display.
  Otherwise, all threads are displayed.
  (gdb)

gdb/ChangeLog:
2019-06-13  Pedro Alves  <palves@redhat.com>

	* NEWS (Completion improvements): Mention "info threads".
	* thread.c (struct info_threads_opts, info_threads_option_defs)
	(make_info_threads_options_def_group): New.
	(info_threads_command): Use gdb::option::process_options.
	(info_threads_command_completer): New.
	(_initialize_thread): Use gdb::option::build_help to build the
	help text for "info threads".

gdb/testsuite/ChangeLog:
2019-06-13  Pedro Alves  <palves@redhat.com>

	* gdb.base/options.exp (test-info-threads): New procedure.
	(top level): Call it.
2019-06-18 01:20:45 +01:00
17 changed files with 1106 additions and 207 deletions

View File

@@ -46,6 +46,21 @@ pipe -d DELIM COMMAND DELIM SHELL_COMMAND
With no COMMAND, repeat the last executed command
and send its output to SHELL_COMMAND.
with SETTING [VALUE] [-- COMMAND]
w SETTING [VALUE] [-- COMMAND]
Temporarily set SETTING, run COMMAND, and restore SETTING.
Usage: with SETTING -- COMMAND
With no COMMAND, repeats the last executed command.
SETTING is any GDB setting you can change with the "set"
subcommands. For example, 'with language c -- print someobj'
temporarily switches to the C language in order to print someobj.
Settings can be combined: 'w lang c -- w print elements unlimited --
usercmd' switches to the C language and runs usercmd with no limit
of array elements to print.
maint with SETTING [VALUE] [-- COMMAND]
Like "with", but works with "maintenance set" settings.
set may-call-functions [on|off]
show may-call-functions
This controls whether GDB will attempt to call functions in
@@ -84,7 +99,8 @@ set style highlight background COLOR
set style highlight intensity VALUE
Control the styling of highlightings.
maint test-settings KIND
maint set test-settings KIND
maint show test-settings KIND
A set of commands used by the testsuite for exercising the settings
infrastructure.
@@ -194,6 +210,8 @@ maint show test-options-completion-result
"taas" commands, and their "-ascending" option can now be
abbreviated.
** GDB can now complete the options of the "info threads" command.
** GDB can now complete the options of the "compile file" and
"compile code" commands. The "compile file" command now
completes on filenames.

View File

@@ -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 (&current_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. */
@@ -850,40 +960,71 @@ edit_command (const char *arg, int from_tty)
xfree (p);
}
/* The options for the "pipe" command. */
struct pipe_cmd_opts
{
/* For "-d". */
char *delimiter = nullptr;
~pipe_cmd_opts ()
{
xfree (delimiter);
}
};
static const gdb::option::option_def pipe_cmd_option_defs[] = {
gdb::option::string_option_def<pipe_cmd_opts> {
"d",
[] (pipe_cmd_opts *opts) { return &opts->delimiter; },
nullptr,
N_("Indicates to use the specified delimiter string to separate\n\
COMMAND from SHELL_COMMAND, in alternative to |. This is useful in\n\
case COMMAND contains a | character."),
},
};
/* Create an option_def_group for the "pipe" command's options, with
OPTS as context. */
static inline gdb::option::option_def_group
make_pipe_cmd_options_def_group (pipe_cmd_opts *opts)
{
return {{pipe_cmd_option_defs}, opts};
}
/* Implementation of the "pipe" command. */
static void
pipe_command (const char *arg, int from_tty)
{
std::string delim ("|");
pipe_cmd_opts opts;
if (arg != nullptr && check_for_argument (&arg, "-d", 2))
{
delim = extract_arg (&arg);
if (delim.empty ())
error (_("Missing delimiter DELIM after -d"));
}
auto grp = make_pipe_cmd_options_def_group (&opts);
gdb::option::process_options
(&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
const char *delim = "|";
if (opts.delimiter != nullptr)
delim = opts.delimiter;
const char *command = arg;
if (command == nullptr)
error (_("Missing COMMAND"));
arg = strstr (arg, delim.c_str ());
arg = strstr (arg, delim);
if (arg == nullptr)
error (_("Missing delimiter before SHELL_COMMAND"));
std::string gdb_cmd (command, arg - command);
arg += delim.length (); /* Skip the delimiter. */
arg += strlen (delim); /* 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')
@@ -914,6 +1055,43 @@ pipe_command (const char *arg, int from_tty)
exit_status_set_internal_vars (exit_status);
}
/* Completer for the pipe command. */
static void
pipe_command_completer (struct cmd_list_element *ignore,
completion_tracker &tracker,
const char *text, const char *word_ignored)
{
pipe_cmd_opts opts;
const char *org_text = text;
auto grp = make_pipe_cmd_options_def_group (&opts);
if (gdb::option::complete_options
(tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp))
return;
const char *delimiter = "|";
if (opts.delimiter != nullptr)
delimiter = opts.delimiter;
/* Check if we're past option values already. */
if (text > org_text && !isspace (text[-1]))
return;
const char *delim = strstr (text, delimiter);
/* If we're still not past the delimiter, complete the gdb
command. */
if (delim == nullptr || delim == text)
{
complete_nested_command_line (tracker, text);
return;
}
/* We're past the delimiter. What follows is a shell command, which
we don't know how to complete. */
}
static void
list_command (const char *arg, int from_tty)
{
@@ -1849,6 +2027,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\
@@ -1907,7 +2102,10 @@ Uses EDITOR environment variable contents as editor (or ex as default)."));
c->completer = location_completer;
c = add_com ("pipe", class_support, pipe_command, _("\
const auto pipe_cmd_opts = make_pipe_cmd_options_def_group (nullptr);
static std::string pipe_cmd_help
= gdb::option::build_help (_("\
Send the output of a gdb command to a shell command.\n\
Usage: | [COMMAND] | SHELL_COMMAND\n\
Usage: | -d DELIM COMMAND DELIM SHELL_COMMAND\n\
@@ -1916,12 +2114,15 @@ Usage: pipe -d DELIM COMMAND DELIM SHELL_COMMAND\n\
\n\
Executes COMMAND and sends its output to SHELL_COMMAND.\n\
\n\
The -d option indicates to use the string DELIM to separate COMMAND\n\
from SHELL_COMMAND, in alternative to |. This is useful in\n\
case COMMAND contains a | character.\n\
\n\
Options:\n\
%OPTIONS%\
With no COMMAND, repeat the last executed command\n\
and send its output to SHELL_COMMAND."));
and send its output to SHELL_COMMAND."),
pipe_cmd_opts);
c = add_com ("pipe", class_support, pipe_command,
pipe_cmd_help.c_str ());
set_cmd_completer_handle_brkchars (c, pipe_command_completer);
add_com_alias ("|", "pipe", class_support, 0);
add_com ("list", class_files, list_command, _("\

View File

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

View File

@@ -43,6 +43,9 @@ union option_value
/* For var_enum options. */
const char *enumeration;
/* For var_string options. This is malloc-allocated. */
char *string;
};
/* Holds an options definition and its value. */
@@ -56,8 +59,57 @@ struct option_def_and_value
/* The option's value, if any. */
gdb::optional<option_value> value;
option_def_and_value (const option_def &option_, void *ctx_,
gdb::optional<option_value> &&value_)
: option (option_),
ctx (ctx_),
value (std::move (value_))
{
clear_value (option_, value_);
}
option_def_and_value (const option_def &option_, void *ctx_)
: option (option_),
ctx (ctx_)
{
}
option_def_and_value (option_def_and_value &&rval)
: option (rval.option),
ctx (rval.ctx),
value (std::move (rval.value))
{
clear_value (rval.option, rval.value);
}
~option_def_and_value ()
{
if (value.has_value ())
{
if (option.type == var_string)
xfree (value->string);
}
}
private:
/* Clear the option_value, without releasing it. This is used after
the value has been moved to some other option_def_and_value
instance. */
static void clear_value (const option_def &option,
gdb::optional<option_value> &value)
{
if (value.has_value ())
{
if (option.type == var_string)
value->string = nullptr;
}
}
};
static void save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov);
/* Info passed around when handling completion. */
struct parse_option_completion_info
{
@@ -370,6 +422,25 @@ parse_option (gdb::array_view<const option_def_group> options_group,
val.enumeration = parse_cli_var_enum (args, match->enums);
return option_def_and_value {*match, match_ctx, val};
}
case var_string:
{
if (check_for_argument (args, "--"))
{
/* Treat e.g., "pipe -d --" as if there was no argument
after "-d". */
error (_("-%s requires an argument"), match->name);
}
const char *arg_start = *args;
*args = skip_to_space (*args);
if (*args == arg_start)
error (_("-%s requires an argument"), match->name);
option_value val;
val.string = savestring (arg_start, *args - arg_start);
return option_def_and_value {*match, match_ctx, val};
}
default:
/* Not yet. */
@@ -456,6 +527,11 @@ complete_options (completion_tracker &tracker,
(*args - text);
return true;
}
/* If the caller passed in a context, then it is
interested in the option argument values. */
if (ov && ov->ctx != nullptr)
save_option_value_in_ctx (ov);
}
else
{
@@ -499,6 +575,41 @@ complete_options (completion_tracker &tracker,
return false;
}
/* Save the parsed value in the option's context. */
static void
save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov)
{
switch (ov->option.type)
{
case var_boolean:
{
bool value = ov->value.has_value () ? ov->value->boolean : true;
*ov->option.var_address.boolean (ov->option, ov->ctx) = value;
}
break;
case var_uinteger:
*ov->option.var_address.uinteger (ov->option, ov->ctx)
= ov->value->uinteger;
break;
case var_zuinteger_unlimited:
*ov->option.var_address.integer (ov->option, ov->ctx)
= ov->value->integer;
break;
case var_enum:
*ov->option.var_address.enumeration (ov->option, ov->ctx)
= ov->value->enumeration;
break;
case var_string:
*ov->option.var_address.string (ov->option, ov->ctx)
= ov->value->string;
ov->value->string = nullptr;
break;
default:
gdb_assert_not_reached ("unhandled option type");
}
}
/* See cli-option.h. */
bool
@@ -534,29 +645,7 @@ process_options (const char **args,
processed_any = true;
switch (ov->option.type)
{
case var_boolean:
{
bool value = ov->value.has_value () ? ov->value->boolean : true;
*ov->option.var_address.boolean (ov->option, ov->ctx) = value;
}
break;
case var_uinteger:
*ov->option.var_address.uinteger (ov->option, ov->ctx)
= ov->value->uinteger;
break;
case var_zuinteger_unlimited:
*ov->option.var_address.integer (ov->option, ov->ctx)
= ov->value->integer;
break;
case var_enum:
*ov->option.var_address.enumeration (ov->option, ov->ctx)
= ov->value->enumeration;
break;
default:
gdb_assert_not_reached ("unhandled option type");
}
save_option_value_in_ctx (ov);
}
}
@@ -588,6 +677,8 @@ get_val_type_str (const option_def &opt, std::string &buffer)
}
return buffer.c_str ();
}
case var_string:
return "STRING";
default:
return nullptr;
}
@@ -715,6 +806,15 @@ add_setshow_cmds_for_options (command_class cmd_class,
nullptr, option.show_cmd_cb,
set_list, show_list);
}
else if (option.type == var_string)
{
add_setshow_string_cmd (option.name, cmd_class,
option.var_address.string (option, data),
option.set_doc, option.show_doc,
option.help_doc,
nullptr, option.show_cmd_cb,
set_list, show_list);
}
else
gdb_assert_not_reached (_("option type not handled"));
}

View File

@@ -86,6 +86,7 @@ public:
unsigned int *(*uinteger) (const option_def &, void *ctx);
int *(*integer) (const option_def &, void *ctx);
const char **(*enumeration) (const option_def &, void *ctx);
char **(*string) (const option_def &, void *ctx);
}
var_address;
@@ -261,6 +262,26 @@ struct enum_option_def : option_def
}
};
/* An var_string command line option. */
template<typename Context>
struct string_option_def : option_def
{
string_option_def (const char *long_option_,
char **(*get_var_address_cb_) (Context *),
show_value_ftype *show_cmd_cb_,
const char *set_doc_,
const char *show_doc_ = nullptr,
const char *help_doc_ = nullptr)
: option_def (long_option_, var_string,
(erased_get_var_address_ftype *) get_var_address_cb_,
show_cmd_cb_,
set_doc_, show_doc_, help_doc_)
{
var_address.enumeration = detail::get_var_address<const char *, Context>;
}
};
/* A group of options that all share the same context pointer to pass
to the options' get-current-value callbacks. */
struct option_def_group

View File

@@ -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 ());
}
/* 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. */
/* 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: 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);

View File

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

View File

@@ -461,14 +461,17 @@ extern void error_no_arg (const char *) ATTRIBUTE_NORETURN;
extern void dont_repeat ();
/* Commands call repeat_previous if they want to repeat the previous command.
Such commands that repeat the previous command must indicate
to not repeat themselves, to avoid recursive repeat.
repeat_previous will mark the current command as not repeating,
and will ensure get_saved_command_line returns the previous command,
so that the currently executing command can repeat it. */
/* Commands call repeat_previous if they want to repeat the previous
command. Such commands that repeat the previous command must
indicate to not repeat themselves, to avoid recursive repeat.
repeat_previous marks the current command as not repeating, and
ensures get_saved_command_line returns the previous command, so
that the currently executing command can repeat it. If there's no
previous command, throws an error. Otherwise, returns the result
of get_saved_command_line, which now points at the command to
repeat. */
extern void repeat_previous ();
extern const char *repeat_previous ();
/* Prevent dont_repeat from working, and return a cleanup that
restores the previous state. */

View File

@@ -1560,6 +1560,7 @@ show you the alternatives available, if there is more than one possibility).
@menu
* Command Syntax:: How to give commands to @value{GDBN}
* Command Settings:: How to change default behavior of commands
* Completion:: Command completion
* Command Options:: Command options
* Help:: How to ask @value{GDBN} for help
@@ -1616,6 +1617,98 @@ commands. This command accepts the current line, like @key{RET}, and
then fetches the next line relative to the current line from the history
for editing.
@node Command Settings
@section Command Settings
@cindex default behavior of commands, changing
@cindex default settings, changing
Many commands change their behavior according to command-specific
variables or settings. These settings can be changed with the
@code{set} subcommands. For example, the @code{print} command
(@pxref{Data, ,Examining Data}) prints arrays differently depending on
settings changeable with the commands @code{set print elements
NUMBER-OF-ELEMENTS} and @code{set print array-indexes}, among others.
You can change these settings to your preference in the gdbinit files
loaded at @value{GDBN} startup. @xref{Startup}.
The settings can also be changed interactively during the debugging
session. For example, to change the limit of array elements to print,
you can do the following:
@smallexample
(@value{GDBN}) set print elements 10
(@value{GDBN}) print some_array
$1 = @{0, 10, 20, 30, 40, 50, 60, 70, 80, 90...@}
@end smallexample
The above @code{set print elements 10} command changes the number of
elements to print from the default of 200 to 10. If you only intend
this limit of 10 to be used for printing @code{some_array}, then you
must restore the limit back to 200, with @code{set print elements
200}.
Some commands allow overriding settings with command options. For
example, the @code{print} command supports a number of options that
allow overriding relevant global print settings as set by @code{set
print} subcommands. @xref{print options}. The example above could be
rewritten as:
@smallexample
(@value{GDBN}) print -elements 10 -- some_array
$1 = @{0, 10, 20, 30, 40, 50, 60, 70, 80, 90...@}
@end smallexample
Alternatively, you can use the @code{with} command to change a setting
temporarily, for the duration of a command invocation.
@table @code
@kindex with command
@kindex w @r{(@code{with})}
@cindex settings
@cindex temporarily change settings
@item with @var{setting} [@var{value}] [-- @var{command}]
@itemx w @var{setting} [@var{value}] [-- @var{command}]
Temporarily set @var{setting} to @var{value} for the duration of
@var{command}.
@var{setting} is any setting you can change with the @code{set}
subcommands. @var{value} is the value to assign to @code{setting}
while running @code{command}.
If no @var{command} is provided, the last command executed is
repeated.
If a @var{command} is provided, it must be preceded by a double dash
(@code{--}) separator. This is required because some settings accept
free-form arguments, such as expressions or filenames.
For example, the command
@smallexample
(@value{GDBN}) with print array on -- print some_array
@end smallexample
@noindent
is equivalent to the following 3 commands:
@smallexample
(@value{GDBN}) set print array on
(@value{GDBN}) print some_array
(@value{GDBN}) set print array off
@end smallexample
The @code{with} command is particularly useful when you want to
override a setting while running user-defined commands, or commands
defined in Python or Guile. @xref{Extending GDB,, Extending GDB}.
@smallexample
(@value{GDBN}) with print pretty on -- my_complex_command
@end smallexample
To change several settings for the same command, you can nest
@code{with} commands. For example, @code{with language ada -- with
print elements 10} temporarily changes the language to Ada and sets a
limit of 10 elements to print for arrays and strings.
@end table
@node Completion
@section Command Completion
@@ -37626,12 +37719,20 @@ Shows the result of completing the @code{maint test-options}
subcommands. This is used by the testsuite to validate completion
support in the command options framework.
@kindex maint test-settings
@item maint test-settings set @var{kind}
@itemx maint test-settings show @var{kind}
@kindex maint set test-settings
@kindex maint show test-settings
@item maint set test-settings @var{kind}
@itemx maint show test-settings @var{kind}
These are representative commands for each @var{kind} of setting type
@value{GDBN} supports. They are used by the testsuite for exercising
the settings infrastructure.
@kindex maint with
@item maint with @var{setting} [@var{value}] [-- @var{command}]
Like the @code{with} command, but works with @code{maintenance set}
variables. This is used by the testsuite to exercise the @code{with}
command's infrastructure.
@end table
The following command is useful for non-interactive invocations of

View File

@@ -1,4 +1,4 @@
/* Support for GDB maintenance commands.
/* Maintenance commands for testing the settings framework.
Copyright (C) 2019 Free Software Foundation, Inc.
@@ -21,46 +21,33 @@
#include "defs.h"
#include "gdbcmd.h"
/* Command list for "maint test-settings". */
static cmd_list_element *maintenance_test_settings_list;
/* Command list for "maint set test-settings". */
static cmd_list_element *maintenance_set_test_settings_list;
/* Command list for "maint test-settings set/show". */
static cmd_list_element *maintenance_test_settings_set_list;
static cmd_list_element *maintenance_test_settings_show_list;
/* Command list for "maint show test-settings". */
static cmd_list_element *maintenance_show_test_settings_list;
/* The "maintenance test-options" prefix command. */
/* The "maintenance set test-settings" prefix command. */
static void
maintenance_test_settings_cmd (const char *arg, int from_tty)
maintenance_set_test_settings_cmd (const char *args, int from_tty)
{
printf_unfiltered
(_("\"maintenance test-settings\" must be followed "
"by the name of a subcommand.\n"));
help_list (maintenance_test_settings_list, "maintenance test-settings ",
all_commands, gdb_stdout);
}
/* The "maintenance test-options set" prefix command. */
static void
maintenance_test_settings_set_cmd (const char *args, int from_tty)
{
printf_unfiltered (_("\"maintenance test-settings set\" must be followed "
printf_unfiltered (_("\"maintenance set test-settings\" must be followed "
"by the name of a set command.\n"));
help_list (maintenance_test_settings_set_list,
"maintenance test-settings set ",
help_list (maintenance_set_test_settings_list,
"maintenance set test-settings ",
all_commands, gdb_stdout);
}
/* The "maintenance test-options show" prefix command. */
/* The "maintenance show test-settings" prefix command. */
static void
maintenance_test_settings_show_cmd (const char *args, int from_tty)
maintenance_show_test_settings_cmd (const char *args, int from_tty)
{
cmd_show_list (maintenance_test_settings_show_list, from_tty, "");
cmd_show_list (maintenance_show_test_settings_list, from_tty, "");
}
/* Control variables for all the "maintenance test-options set/show
/* Control variables for all the "maintenance set/show test-settings
xxx" commands. */
static int maintenance_test_settings_boolean;
@@ -85,18 +72,26 @@ static char *maintenance_test_settings_optional_filename;
static char *maintenance_test_settings_filename;
static const char *maintenance_test_settings_enum;
/* Enum values for the "maintenance test-settings set/show boolean"
/* Enum values for the "maintenance set/show test-settings boolean"
commands. */
static const char maintenance_test_settings_xxx[] = "xxx";
static const char maintenance_test_settings_yyy[] = "yyy";
static const char maintenance_test_settings_zzz[] = "zzz";
static const char *const maintenance_test_settings_enums[] = {
"xxx", "yyy", "zzz", nullptr
maintenance_test_settings_xxx,
maintenance_test_settings_yyy,
maintenance_test_settings_zzz,
nullptr
};
/* The "maintenance test-options show xxx" commands. */
static const char *maintenance_test_settings_enum
= maintenance_test_settings_xxx;
/* The "maintenance show test-settings xxx" commands. */
static void
maintenance_test_settings_show_value_cmd
maintenance_show_test_settings_value_cmd
(struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
@@ -107,29 +102,23 @@ maintenance_test_settings_show_value_cmd
void
_initialize_maint_test_settings (void)
{
add_prefix_cmd ("test-settings", no_class,
maintenance_test_settings_cmd,
_("\
Generic command for testing the settings infrastructure."),
&maintenance_test_settings_list,
"maintenance test-settings ", 0,
&maintenancelist);
maintenance_test_settings_filename = xstrdup ("/foo/bar");
add_prefix_cmd ("set", class_maintenance,
maintenance_test_settings_set_cmd, _("\
add_prefix_cmd ("test-settings", class_maintenance,
maintenance_set_test_settings_cmd, _("\
Set GDB internal variables used for set/show command infrastructure testing."),
&maintenance_test_settings_set_list,
"maintenance test-settings set ",
&maintenance_set_test_settings_list,
"maintenance set test-settings ",
0/*allow-unknown*/,
&maintenance_test_settings_list);
&maintenance_set_cmdlist);
add_prefix_cmd ("show", class_maintenance,
maintenance_test_settings_show_cmd, _("\
add_prefix_cmd ("test-settings", class_maintenance,
maintenance_show_test_settings_cmd, _("\
Show GDB internal variables used for set/show command infrastructure testing."),
&maintenance_test_settings_show_list,
"maintenance test-settings show ",
&maintenance_show_test_settings_list,
"maintenance show test-settings ",
0/*allow-unknown*/,
&maintenance_test_settings_list);
&maintenance_show_cmdlist);
add_setshow_boolean_cmd ("boolean", class_maintenance,
&maintenance_test_settings_boolean, _("\
@@ -137,9 +126,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_auto_boolean_cmd ("auto-boolean", class_maintenance,
&maintenance_test_settings_auto_boolean, _("\
@@ -147,19 +136,19 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_uinteger_cmd ("uinteger", class_maintenance,
&maintenance_test_settings_uinteger, _("\
command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_integer_cmd ("integer", class_maintenance,
&maintenance_test_settings_integer, _("\
@@ -167,9 +156,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_string_cmd ("string", class_maintenance,
&maintenance_test_settings_string, _("\
@@ -177,9 +166,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_string_noescape_cmd
("string-noescape", class_maintenance,
@@ -188,9 +177,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_optional_filename_cmd
("optional-filename", class_maintenance,
@@ -199,9 +188,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_filename_cmd ("filename", class_maintenance,
&maintenance_test_settings_filename, _("\
@@ -209,9 +198,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_zinteger_cmd ("zinteger", class_maintenance,
&maintenance_test_settings_zinteger, _("\
@@ -219,9 +208,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_zuinteger_cmd ("zuinteger", class_maintenance,
&maintenance_test_settings_zuinteger, _("\
@@ -229,9 +218,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_zuinteger_unlimited_cmd
("zuinteger-unlimited", class_maintenance,
@@ -240,9 +229,9 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
add_setshow_enum_cmd ("enum", class_maintenance,
maintenance_test_settings_enums,
@@ -251,7 +240,7 @@ command used for internal testing"), _("\
command used for internal testing"),
nullptr, /* help_doc */
nullptr, /* set_cmd */
maintenance_test_settings_show_value_cmd,
&maintenance_test_settings_set_list,
&maintenance_test_settings_show_list);
maintenance_show_test_settings_value_cmd,
&maintenance_set_test_settings_list,
&maintenance_show_test_settings_list);
}

View File

@@ -43,6 +43,7 @@
#include "cli/cli-decode.h"
#include "cli/cli-utils.h"
#include "cli/cli-setshow.h"
#include "cli/cli-cmds.h"
static void maintenance_do_deprecate (const char *, int);
@@ -634,6 +635,24 @@ maintenance_show_cmd (const char *args, int from_tty)
cmd_show_list (maintenance_show_cmdlist, from_tty, "");
}
/* "maintenance with" command. */
static void
maintenance_with_cmd (const char *args, int from_tty)
{
with_command_1 ("maintenance set ", maintenance_set_cmdlist, args, from_tty);
}
/* "maintenance with" command completer. */
static void
maintenance_with_cmd_completer (struct cmd_list_element *ignore,
completion_tracker &tracker,
const char *text, const char * /*word*/)
{
with_command_completer_1 ("maintenance set ", tracker, text);
}
/* Profiling support. */
static int maintenance_profile_p;
@@ -1023,6 +1042,15 @@ Configure variables internal to GDB that aid in GDB's maintenance"),
0/*allow-unknown*/,
&maintenancelist);
cmd = add_cmd ("with", class_maintenance, maintenance_with_cmd, _("\
Like \"with\", but works with \"maintenance set\" variables.\n\
Usage: maintenance with SETTING [VALUE] [-- COMMAND]\n\
With no COMMAND, repeats the last executed command.\n\
SETTING is any setting you can change with the \"maintenance set\"\n\
subcommands."),
&maintenancelist);
set_cmd_completer_handle_brkchars (cmd, maintenance_with_cmd_completer);
#ifndef _WIN32
add_cmd ("dump-me", class_maintenance, maintenance_dump_me, _("\
Get fatal error; make debugger dump its core.\n\

View File

@@ -454,6 +454,21 @@ proc_with_prefix test-thread-apply {} {
}
}
# Basic option-machinery + "info threads" command integration tests.
proc_with_prefix test-info-threads {} {
test_gdb_complete_multiple "info threads " "" "" {
"-gid"
"ID"
}
test_gdb_complete_unique \
"info threads -" \
"info threads -gid"
# "ID" isn't really something the user can type.
test_gdb_complete_none "info threads I"
}
# Miscellaneous tests.
proc_with_prefix test-misc {variant} {
global all_options
@@ -921,3 +936,6 @@ test-frame-apply
# Basic "thread apply" integration tests.
test-thread-apply
# Basic "info threads" integration tests.
test-info-threads

View File

@@ -45,8 +45,8 @@ proc show_setting {show_cmd expected_re} {
# var_Xinteger tests. VARIANT determines which command/variant to
# exercise.
proc test-integer {variant} {
set set_cmd "maint test-settings set $variant"
set show_cmd "maint test-settings show $variant"
set set_cmd "maint set test-settings $variant"
set show_cmd "maint show test-settings $variant"
# A bogus value.
gdb_test "$set_cmd bogus" \
@@ -122,7 +122,7 @@ proc test-integer {variant} {
}
if {$variant == "zuinteger"} {
test_gdb_complete_multiple "maint test-settings set " "zuinteger" "" {
test_gdb_complete_multiple "maint set test-settings " "zuinteger" "" {
"zuinteger"
"zuinteger-unlimited"
}
@@ -166,7 +166,7 @@ proc test-integer {variant} {
# Check show command completion.
if {$variant == "zuinteger"} {
test_gdb_complete_multiple "maintenance test-settings show " "zuinteger" "" {
test_gdb_complete_multiple "maintenance show test-settings " "zuinteger" "" {
"zuinteger"
"zuinteger-unlimited"
}
@@ -182,8 +182,8 @@ proc test-integer {variant} {
proc_with_prefix test-boolean {} {
# Use these variables to make sure we don't call the wrong command
# by mistake.
set set_cmd "maint test-settings set boolean"
set show_cmd "maint test-settings show boolean"
set set_cmd "maint set test-settings boolean"
set show_cmd "maint show test-settings boolean"
# A bogus value.
gdb_test "$set_cmd bogus" \
@@ -277,8 +277,8 @@ proc_with_prefix test-boolean {} {
proc_with_prefix test-auto-boolean {} {
# Use these variables to make sure we don't call the wrong command
# by mistake.
set set_cmd "maint test-settings set auto-boolean"
set show_cmd "maint test-settings show auto-boolean"
set set_cmd "maint set test-settings auto-boolean"
set show_cmd "maint show test-settings auto-boolean"
# A bogus value.
gdb_test "$set_cmd bogus" \
@@ -381,8 +381,8 @@ proc_with_prefix test-auto-boolean {} {
proc_with_prefix test-enum {} {
# Use these variables to make sure we don't call the wrong command
# by mistake.
set set_cmd "maint test-settings set enum"
set show_cmd "maint test-settings show enum"
set set_cmd "maint set test-settings enum"
set show_cmd "maint show test-settings enum"
# Missing value.
gdb_test "$set_cmd" \
@@ -444,12 +444,15 @@ proc test-string {variant} {
# Use these variables to make sure we don't call the wrong command
# by mistake.
set set_cmd "maint test-settings set $variant"
set show_cmd "maint test-settings show $variant"
set set_cmd "maint set test-settings $variant"
set show_cmd "maint show test-settings $variant"
# Empty string. Also checks that gdb doesn't crash if we haven't
# set the string yet.
gdb_test "$show_cmd" "^$show_cmd\r\n" "$show_cmd: empty first time"
# Checks that gdb doesn't crash if we haven't set the string yet.
if {$variant != "filename"} {
gdb_test "$show_cmd" "^$show_cmd\r\n" "$show_cmd: show default"
} else {
gdb_test "$show_cmd" "/foo/bar" "$show_cmd: show default"
}
# A string value.
gdb_test_no_output "$set_cmd hello world"
@@ -516,7 +519,7 @@ proc test-string {variant} {
# Check show command completion.
if {$variant == "string"} {
test_gdb_complete_multiple "maint test-settings show " "string" "" {
test_gdb_complete_multiple "maint show test-settings " "string" "" {
"string"
"string-noescape"
}

View File

@@ -0,0 +1,41 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2019 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/>. */
int xxx1 = 123;
struct S
{
int a;
int b;
int c;
};
struct S g_s = {1, 2, 3};
static void
inc ()
{
g_s.a++;;
}
int
main ()
{
inc ();
return 0;
}

View File

@@ -0,0 +1,289 @@
# This testcase is part of GDB, the GNU debugger.
# Copyright 2019 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/>.
# Test the "with" command.
load_lib completion-support.exp
standard_testfile .c
if {[build_executable "failed to prepare" $testfile $srcfile debug]} {
return -1
}
clean_restart $binfile
# Test "maint with". VALUES is a list of values. A nested "with" is
# performed with each combination of pair of values from this list.
# This exercises setting a value, and restoring it too. This is
# particularly important for the "special" values like "unlimited",
# which for example for var_uinteger maps to 0 at the user-visible
# level, but maps to -1 internally.
proc test_with {setting values} {
foreach val1 $values {
foreach val2 $values {
gdb_test \
"maint with test-settings $setting $val1 -- maint with test-settings $setting $val2 -- p 1" \
" = 1"
}
}
}
# Test "maint with" in the error case. SETTING is the "maint set
# test-setting" setting to exercise. TMP_VAL is the value to set the
# setting to. EXPECTED_RE is the expected GDB output, which should be
# an error of some kind. Also checks that the setting's original
# value is preserved across the error.
proc test_with_error {setting tmp_val expected_re} {
global gdb_prompt
with_test_prefix "$setting, $tmp_val" {
set test "save org value"
set org_val ""
gdb_test_multiple "maint show test-settings $setting" $test {
-re "(.*)\r\n$gdb_prompt $" {
set org_val $expect_out(1,string)
pass $test
}
}
gdb_test \
"maint with test-settings $setting $tmp_val -- p 1" \
$expected_re
gdb_test "maint show test-settings $setting" "^$org_val" \
"value hasn't changed across error"
}
}
# Test "with" framework basics, using the internal "maint with
# test-settings" subcommands.
with_test_prefix "maint" {
test_with "auto-boolean" {"on" "off" "auto"}
test_with "boolean" {"" "on" "off" "0" "1" "enable" "disable"}
test_with "integer" {"0" "1" "-1" "unlimited"}
test_with "uinteger" {"0" "1" "unlimited"}
test_with "zinteger" {"0" "1" "-1"}
test_with "zuinteger" {"0" "1"}
test_with "zuinteger-unlimited" {"-1" "unlimited" "0" "1"}
test_with "string" {"" "foo" "\"hello world\""}
test_with "string-noescape" {"" "foo" "\"hello world\""}
test_with "filename" {"/foo" "bar/x/y"}
test_with "optional-filename" {"" "/foo" "bar/x/y"}
test_with "enum" {"xxx" "yyy"}
# Check the most important error conditions. E.g., empty,
# negative or "unlimited" values for settings that don't accept
# those. Exhaustive error coverage of the set/with value parsing
# is left to "set" testing, in gdb.base/settings.exp.
test_with_error "auto-boolean" "" \
"\"on\", \"off\" or \"auto\" expected\\."
test_with_error "auto-boolean" "xxx" \
"\"on\", \"off\" or \"auto\" expected\\."
test_with_error "boolean" "2" "\"on\" or \"off\" expected\\."
test_with_error "uinteger" "-1" "integer -1 out of range"
test_with_error "uinteger" "" \
"Argument required \\(integer to set it to, or \"unlimited\"\\.\\)\\."
test_with_error "zuinteger" "-1" "integer -1 out of range"
test_with_error "zuinteger" "" \
"Argument required \\(integer to set it to\\.\\)\\."
test_with_error "zuinteger-unlimited" "-2" \
"only -1 is allowed to set as unlimited"
test_with_error "zuinteger-unlimited" "" \
"Argument required \\(integer to set it to, or \"unlimited\"\\.\\)\\."
test_with_error "filename" "" \
"Argument required \\(filename to set it to\\.\\)\\."
test_with_error "enum" "" \
"Requires an argument\\. Valid arguments are xxx, yyy, zzz\\."
}
# Basic/core tests using user-visible commands.
with_test_prefix "basics" {
gdb_test "print g_s" " = {a = 1, b = 2, c = 3}"
gdb_test "with print pretty -- print g_s" \
[multi_line \
" = {" \
" a = 1," \
" b = 2," \
" c = 3" \
"}"]
# A boolean setting.
gdb_test "with non-stop on -- show non-stop" \
"Controlling the inferior in non-stop mode is on\\."
gdb_test "show non-stop" \
"Controlling the inferior in non-stop mode is off\\."
# Language.
gdb_test "with language pascal -- show language" \
"The current source language is \"pascal\"\\."
gdb_test "show language" \
"The current source language is \"auto; currently c\"\\."
gdb_test "with language ada -- print g_s" \
" = \\(a => 1, b => 2, c => 3\\)"
# Nested "with"s.
gdb_test "with language ada -- with language c -- print g_s" \
" = {a = 1, b = 2, c = 3}"
# "w" alias.
gdb_test "w language pascal -- show language" \
"The current source language is \"pascal\"\\." \
"w alias works"
# An early prototype of the "with" command got this wrong.
gdb_test \
"w print repeats unlimited -- w print repeats 1 -- p \"1223334444\"" \
" = \"1\", '2' <repeats 2 times>, '3' <repeats 3 times>, '4' <repeats 4 times>"
}
# Check a user-defined command.
with_test_prefix "user-defined" {
# A user defined command.
set test "define usercmd"
gdb_test_multiple "define usercmd" $test {
-re "End with" {
gdb_test \
[multi_line_input \
{print g_s} \
{end}] \
"" \
$test
}
}
gdb_test "with language ada -- usercmd" \
" = \\(a => 1, b => 2, c => 3\\)"
}
# Check repeating.
with_test_prefix "repeat" {
clean_restart $binfile
# "with" with no command reinvokes the previous command.
gdb_test "with language ada" \
"No previous command to relaunch" \
"reinvoke with no previous command to relaunch"
gdb_test "print g_s" " = {a = 1, b = 2, c = 3}"
gdb_test "with language ada" \
" = \\(a => 1, b => 2, c => 3\\)" \
"reinvoke with language"
# Same, but with "--".
gdb_test "with language fortran --" \
" = \\( a = 1, b = 2, c = 3 \\)" \
"reinvoke with language and --"
# Repeating repeats the original "print g_s", not the last "with"
# command.
set test "repeat command line"
send_gdb "\n"
gdb_test_multiple "" $test {
-re " = {a = 1, b = 2, c = 3}\r\n$gdb_prompt $" {
pass $test
}
}
}
# Basic run control.
with_test_prefix "run control" {
clean_restart $binfile
if ![runto_main] {
fail "cannot run to main"
return
}
# Check "with" with a synchronous execution command.
gdb_test "with disassemble-next-line on -- next" \
"return 0;.*=>.*"
}
# Check errors.
with_test_prefix "errors" {
# Try both an unknown root setting and an unknown prefixed
# setting. The errors come from different locations in the
# sources.
gdb_test "with xxxx yyyy" \
"Undefined set command: \"xxxx\". Try \"help set\"\\."
gdb_test "with print xxxx yyyy" \
"Undefined set print command: \"xxxx yyyy\". Try \"help set print\"\\."
# Try one error case for "maint with", to make sure the right
# "maintenance with" prefix is shown.
gdb_test "maint with xxxx yyyy" \
"Undefined maintenance set command: \"xxxx\". Try \"help maintenance set\"\\."
# Try ambiguous settings.
gdb_test "with w" \
"Ambiguous set command \"w\": watchdog, width, write\\."
gdb_test "with print m" \
"Ambiguous set print command \"m\": max-depth, max-symbolic-offset\\."
gdb_test "with variable xxx=1" \
"Cannot use this setting with the \"with\" command"
gdb_test "with print elements -- p 1" \
"Argument required \\(integer to set it to, or \"unlimited\"\\.\\)\\."
gdb_test "with -- p 1" \
"Missing setting before '--' delimiter"
# Check that the setting is restored even if the command throws.
gdb_test "with print elements 1 -- unknowncommand" \
"Undefined command: \"unknowncommand\"\\. Try \"help\"\\."
gdb_test "show print elements" \
"Limit on string chars or array elements to print is 200\\."
}
# Check completion.
with_test_prefix "completion" {
test_gdb_complete_unique \
"with pri" \
"with print"
test_gdb_complete_unique \
"with print ele" \
"with print elements"
test_gdb_complete_unique \
"with print elements u" \
"with print elements unlimited"
test_gdb_complete_none \
"with print elements unlimited "
test_gdb_completion_offers_commands "with print elements unlimited -- "
# Check that the completer nests into the nested command line's
# completer.
test_gdb_complete_unique \
"with print elements unlimited -- with print ele" \
"with print elements unlimited -- with print elements"
# Check completion of "maint with". "maint with" and "with"'s
# completers share 99% of the code. All we need to care about
# here is that the completion word point is computed correctly, so
# any simple completion is sufficient.
test_gdb_complete_unique \
"maint with test-set" \
"maint with test-settings"
}

View File

@@ -1199,6 +1199,33 @@ print_thread_info (struct ui_out *uiout, const char *requested_threads,
print_thread_info_1 (uiout, requested_threads, 1, pid, 0);
}
/* The options for the "info threads" command. */
struct info_threads_opts
{
/* For "-gid". */
int show_global_ids = 0;
};
static const gdb::option::option_def info_threads_option_defs[] = {
gdb::option::flag_option_def<info_threads_opts> {
"gid",
[] (info_threads_opts *opts) { return &opts->show_global_ids; },
N_("Show global thread IDs."),
},
};
/* Create an option_def_group for the "info threads" options, with
IT_OPTS as context. */
static inline gdb::option::option_def_group
make_info_threads_options_def_group (info_threads_opts *it_opts)
{
return {{info_threads_option_defs}, it_opts};
}
/* Implementation of the "info threads" command.
Note: this has the drawback that it _really_ switches
@@ -1208,16 +1235,36 @@ print_thread_info (struct ui_out *uiout, const char *requested_threads,
static void
info_threads_command (const char *arg, int from_tty)
{
int show_global_ids = 0;
info_threads_opts it_opts;
if (arg != NULL
&& check_for_argument (&arg, "-gid", sizeof ("-gid") - 1))
auto grp = make_info_threads_options_def_group (&it_opts);
gdb::option::process_options
(&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp);
print_thread_info_1 (current_uiout, arg, 0, -1, it_opts.show_global_ids);
}
/* Completer for the "info threads" command. */
static void
info_threads_command_completer (struct cmd_list_element *ignore,
completion_tracker &tracker,
const char *text, const char *word_ignored)
{
const auto grp = make_info_threads_options_def_group (nullptr);
if (gdb::option::complete_options
(tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp))
return;
/* Convenience to let the user know what the option can accept. */
if (*text == '\0')
{
arg = skip_spaces (arg);
show_global_ids = 1;
gdb::option::complete_on_all_options (tracker, grp);
/* Keep this "ID" in sync with what "help info threads"
says. */
tracker.add_completion (make_unique_xstrdup ("ID"));
}
print_thread_info_1 (current_uiout, arg, 0, -1, show_global_ids);
}
/* See gdbthread.h. */
@@ -2068,12 +2115,23 @@ _initialize_thread (void)
static struct cmd_list_element *thread_apply_list = NULL;
cmd_list_element *c;
add_info ("threads", info_threads_command,
_("Display currently known threads.\n\
Usage: info threads [-gid] [ID]...\n\
-gid: Show global thread IDs.\n\
const auto info_threads_opts = make_info_threads_options_def_group (nullptr);
/* Note: keep this "ID" in sync with what "info threads [TAB]"
suggests. */
static std::string info_threads_help
= gdb::option::build_help (_("\
Display currently known threads.\n\
Usage: info threads [OPTION]... [ID]...\n\
\n\
Options:\n\
%OPTIONS%\
If ID is given, it is a space-separated list of IDs of threads to display.\n\
Otherwise, all threads are displayed."));
Otherwise, all threads are displayed."),
info_threads_opts);
c = add_info ("threads", info_threads_command, info_threads_help.c_str ());
set_cmd_completer_handle_brkchars (c, info_threads_command_completer);
add_prefix_cmd ("thread", class_run, thread_command, _("\
Use this command to switch between threads.\n\

View File

@@ -735,7 +735,7 @@ dont_repeat (void)
/* See command.h */
void
const char *
repeat_previous ()
{
/* Do not repeat this command, as this command is a repeating command. */
@@ -745,6 +745,11 @@ repeat_previous ()
so swap it with previous_saved_command_line. */
std::swap (previous_saved_command_line, saved_command_line);
std::swap (previous_repeat_arguments, repeat_arguments);
const char *prev = skip_spaces (get_saved_command_line ());
if (*prev == '\0')
error (_("No previous command to relaunch"));
return prev;
}
/* See command.h. */