gdb: add remove-symbol-file command completion

The 'remove-symbol-file' command doesn't currently offer command
completion.  This commit addresses this.

The 'remove-symbol-file' uses gdb_argv to split its command arguments,
this means that the filename the command expects can be quoted.

However, the 'remove-symbol-file' command is a little weird in that it
also has a '-a' option, if this option is passed then the command
expects not a filename, but an address.

Currently the remove_symbol_file_command function splits the command
args using gdb_argv, checks for a '-a' flag by looking at the first
argument value, and then expects the filename or address to occupy a
single entry in the gdb_argv array.

The first thing I do is handle the '-a' flag using GDB's option
system.  I model this option as a flag_option_def (a boolean option).

I've dropped the use of gdb_argv and instead use the new(ish) function
extract_single_filename_arg, which was added a couple of commits back,
to parse the filename argument (when '-a' is not given).

If '-a' is given the the remove-symbol-file command expects an address
rather than a filename.  As we previously split the arguments using
gdb_argv this meant the address needed to appear as a single
argument.  So a user could write:

  (gdb) remove-symbol-file 0x1234

Or they could write:

  (gdb) remove-symbol-file some_function

Both of these would work fine.  But a user could not write:

  (gdb) remove-symbol-file some_function + 0x1000

As only the 'some_function' part would be processed.  Now the user
could do this:

  (gdb) remove-symbol-file "some_function + 0x1000"

By enclosing the address expression in quotes this would be handled as
a single argument.  However, this is a little weird, that's not how
commands like 'print' or 'x' work.  Also this functionality was
neither documented, or tested.

And so, in this commit, by removing the use of gdb_argv I bring the
'remove-symbol-file' command inline with GDB's other commands that
take an expression, the quotes are no longer needed.

Usually in a completer we call 'complete_options', but don't actually
capture the option values.  But for remove-symbol-file I do.  This
allows me to spot when the '-a' option has been given, I can then
complete the rest of the command line as either a filename or an
expression.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
This commit is contained in:
Andrew Burgess 2024-06-22 11:39:38 +01:00
parent 1be3b2e82f
commit d552429eda
5 changed files with 239 additions and 149 deletions

View File

@ -53,6 +53,16 @@ maintenance info blocks [ADDRESS]
are listed starting at the inner global block out to the most inner
block.
* Changed commands
remove-symbol-file
This command now supports file-name completion.
remove-symbol-file -a ADDRESS
The ADDRESS expression can now be a full expression consisting of
multiple terms, e.g. 'function + 0x1000' (without quotes),
previously only a single term could be given.
* New remote packets
vFile:stat

View File

@ -21893,6 +21893,7 @@ Remove symbol table from file "/home/user/gdb/mylib.so"? (y or n) y
(@value{GDBP})
@end smallexample
The @var{address} can be any expression which evaluates to an address.
@code{remove-symbol-file} does not repeat if you press @key{RET} after using it.

View File

@ -2330,39 +2330,90 @@ add_symbol_file_command (const char *args, int from_tty)
}
/* Option support for 'remove-symbol-file' command. */
struct remove_symbol_file_options
{
/* True when the '-a' flag was passed. */
bool address_flag = false;
};
using remove_symbol_file_options_opt_def
= gdb::option::flag_option_def<remove_symbol_file_options>;
static const gdb::option::option_def remove_symbol_file_opt_defs[] = {
remove_symbol_file_options_opt_def {
"a",
[] (remove_symbol_file_options *opt) { return &opt->address_flag; },
N_("Select a symbol file containing ADDRESS.")
},
};
static inline gdb::option::option_def_group
make_remove_symbol_file_def_group (remove_symbol_file_options *opts)
{
return {{remove_symbol_file_opt_defs}, opts};
}
/* Completion function for 'remove-symbol-file' command. */
static void
remove_symbol_file_command_completer (struct cmd_list_element *ignore,
completion_tracker &tracker,
const char *text, const char * /* word */)
{
/* Unlike many command completion functions we do gather the option
values here. How we complete the rest of the command depends on
whether the '-a' flag has been given or not. */
remove_symbol_file_options opts;
auto grp = make_remove_symbol_file_def_group (&opts);
if (gdb::option::complete_options
(tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp))
return;
/* Complete the rest of the command line as either a filename or an
expression (which will evaluate to an address) if the '-a' flag was
given. */
if (!opts.address_flag)
{
const char *word
= advance_to_filename_maybe_quoted_complete_word_point (tracker, text);
filename_maybe_quoted_completer (ignore, tracker, text, word);
}
else
{
const char *word
= advance_to_expression_complete_word_point (tracker, text);
symbol_completer (ignore, tracker, text, word);
}
}
/* This function removes a symbol file that was added via add-symbol-file. */
static void
remove_symbol_file_command (const char *args, int from_tty)
{
struct objfile *objf = NULL;
struct program_space *pspace = current_program_space;
dont_repeat ();
if (args == NULL)
error (_("remove-symbol-file: no symbol file provided"));
remove_symbol_file_options opts;
auto grp = make_remove_symbol_file_def_group (&opts);
gdb::option::process_options
(&args, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_ERROR, grp);
gdb_argv argv (args);
struct objfile *objf = nullptr;
if (strcmp (argv[0], "-a") == 0)
if (opts.address_flag)
{
/* Interpret the next argument as an address. */
CORE_ADDR addr;
if (args == nullptr || *args == '\0')
error (_("remove-symbol-file: no address provided"));
if (argv[1] == NULL)
error (_("Missing address argument"));
if (argv[2] != NULL)
error (_("Junk after %s"), argv[1]);
addr = parse_and_eval_address (argv[1]);
CORE_ADDR addr = parse_and_eval_address (args);
for (objfile *objfile : current_program_space->objfiles ())
{
if ((objfile->flags & OBJF_USERLOADED) != 0
&& (objfile->flags & OBJF_SHARED) != 0
&& objfile->pspace () == pspace
&& objfile->pspace () == current_program_space
&& is_addr_in_objfile (addr, objfile))
{
objf = objfile;
@ -2370,21 +2421,18 @@ remove_symbol_file_command (const char *args, int from_tty)
}
}
}
else if (argv[0] != NULL)
else
{
/* Interpret the current argument as a file name. */
if (argv[1] != NULL)
error (_("Junk after %s"), argv[0]);
gdb::unique_xmalloc_ptr<char> filename (tilde_expand (argv[0]));
std::string filename = extract_single_filename_arg (args);
if (filename.empty ())
error (_("remove-symbol-file: no symbol file provided"));
for (objfile *objfile : current_program_space->objfiles ())
{
if ((objfile->flags & OBJF_USERLOADED) != 0
&& (objfile->flags & OBJF_SHARED) != 0
&& objfile->pspace () == pspace
&& filename_cmp (filename.get (), objfile_name (objfile)) == 0)
&& objfile->pspace () == current_program_space
&& filename_cmp (filename.c_str (), objfile_name (objfile)) == 0)
{
objf = objfile;
break;
@ -3830,14 +3878,22 @@ READNOW_READNEVER_HELP),
&cmdlist);
set_cmd_completer (c, filename_maybe_quoted_completer);
c = add_cmd ("remove-symbol-file", class_files,
remove_symbol_file_command, _("\
const auto remove_symbol_file_opts
= make_remove_symbol_file_def_group (nullptr);
static std::string remove_symbol_file_cmd_help
= gdb::option::build_help (_("\
Remove a symbol file added via the add-symbol-file command.\n\
Usage: remove-symbol-file FILENAME\n\
remove-symbol-file -a ADDRESS\n\
The file to remove can be identified by its filename or by an address\n\
that lies within the boundaries of this symbol file in memory."),
that lies within the boundaries of this symbol file in memory.\n\
Options:\n\
%OPTIONS%"), remove_symbol_file_opts);
c = add_cmd ("remove-symbol-file", class_files,
remove_symbol_file_command,
remove_symbol_file_cmd_help.c_str (),
&cmdlist);
set_cmd_completer_handle_brkchars (c, remove_symbol_file_command_completer);
c = add_cmd ("load", class_files, load_command, _("\
Dynamically load FILE into the running program.\n\

View File

@ -122,7 +122,8 @@ proc test_gdb_complete_filename_multiple {
proc run_quoting_and_escaping_tests { root } {
# Test all the commands which allow quoting of filenames, and
# which require whitespace to be escaped in unquoted filenames.
foreach_with_prefix cmd { file exec-file symbol-file add-symbol-file } {
foreach_with_prefix cmd { file exec-file symbol-file add-symbol-file \
remove-symbol-file } {
gdb_start
# Completing 'thread apply all ...' commands uses a custom word

View File

@ -67,134 +67,156 @@ if {[prepare_for_testing "failed to prepare" $binfile "$srcfile $srcfile2" $exe
gdb_load_shlib ${lib_so}
if {![runto_main]} {
return
}
proc do_test { remove_expr } {
global lib_basename lib_syms srcfile srcfile3
# 1) Run to gdb_add_symbol_file in $srcfile for adding the library's
# symbols.
gdb_breakpoint gdb_add_symbol_file
gdb_continue_to_breakpoint gdb_add_symbol_file
clean_restart $::binfile
# 2) Set a pending breakpoint at bar in $srcfile3.
set result [gdb_breakpoint bar allow-pending]
if {!$result} {
return
}
if {![runto_main]} {
return
}
# 3) Add the library's symbols using 'add-symbol-file'.
set result [gdb_test "add-symbol-file ${lib_syms} addr" \
"Reading symbols from .*${lib_syms}\\.\\.\\." \
"add-symbol-file ${lib_basename}.so addr" \
"add symbol table from file \".*${lib_basename}\\.so\"\
at.*\\(y or n\\) " \
"y"]
if {$result != 0} {
return
}
# 4) 'info files' must display $srcfile3.
gdb_test "info files" \
"^(?=(.*${lib_basename})).*" \
"info files must display ${lib_basename}"
# 5) Continue to bar in $srcfile3 to ensure that the breakpoint
# was bound correctly after adding $shilb_name.
set lnum_bar [gdb_get_line_number "break at bar" $srcfile3]
gdb_continue_to_breakpoint bar ".*${lib_basename}\\.c:$lnum_bar.*"
# 6) Set a breakpoint at foo in $srcfile3.
set result [gdb_breakpoint foo]
if {!$result} {
return
}
# 7) Continue to foo in $srcfile3 to ensure that the breakpoint
# was bound correctly.
set lnum_foo [gdb_get_line_number "break at foo" $srcfile3]
gdb_continue_to_breakpoint foo ".*${lib_basename}\\.c:$lnum_foo.*"
# 8) Set a breakpoint at gdb_remove_symbol_file in $srcfile for
# removing the library's symbols.
set result [gdb_breakpoint gdb_remove_symbol_file]
if {!$result} {
return
}
# 9) Continue to gdb_remove_symbol_file in $srcfile.
gdb_continue_to_breakpoint gdb_remove_symbol_file
# 10) Remove the library's symbols using 'remove-symbol-file'.
set result [gdb_test "remove-symbol-file -a addr" \
""\
"remove-symbol-file -a addr" \
"Remove symbol table from file \".*${lib_basename}\\.so\"\\?\
.*\\(y or n\\) " \
"y"]
if {$result != 0} {
return
}
# 11) 'info files' must not display ${lib_basename}, anymore.
gdb_test "info files" \
"^(?!(.*${lib_basename})).*" \
"info files must not display ${lib_basename}"
# 12) Check that the breakpoints at foo and bar are pending after
# removing the library's symbols.
gdb_test "info breakpoints 3" \
".*PENDING.*" \
"breakpoint at foo is pending"
gdb_test "info breakpoints 4" \
".*PENDING.*" \
"breakpoint at bar is pending"
# 13) Check that the execution can continue without error.
set lnum_reload [gdb_get_line_number "reload lib here"]
gdb_breakpoint $lnum_reload
gdb_continue_to_breakpoint reload ".*${srcfile}:$lnum_reload.*"
# 14) Regression test for a stale breakpoints bug. Check whether
# unloading symbols manually without the program actually unloading
# the library, when breakpoints are inserted doesn't leave stale
# breakpoints behind.
with_test_prefix "stale bkpts" {
# Force breakpoints always inserted.
gdb_test_no_output "set breakpoint always-inserted on"
# Get past the library reload.
# 1) Run to gdb_add_symbol_file in $srcfile for adding the library's
# symbols.
gdb_breakpoint gdb_add_symbol_file
gdb_continue_to_breakpoint gdb_add_symbol_file
# Load the library's symbols.
gdb_test "add-symbol-file ${lib_syms} addr" \
"Reading symbols from .*${lib_syms}\\.\\.\\." \
"add-symbol-file ${lib_basename}.so addr" \
"add symbol table from file \".*${lib_syms}\"\
at.*\\(y or n\\) " \
"y"
# 2) Set a pending breakpoint at bar in $srcfile3.
set result [gdb_breakpoint bar allow-pending]
if {!$result} {
return
}
# Set a breakpoint at baz, in the library.
gdb_breakpoint baz
# 3) Add the library's symbols using 'add-symbol-file'.
set result [gdb_test "add-symbol-file ${lib_syms} addr" \
"Reading symbols from .*${lib_syms}\\.\\.\\." \
"add-symbol-file ${lib_basename}.so addr" \
"add symbol table from file \".*${lib_basename}\\.so\"\
at.*\\(y or n\\) " \
"y"]
if {$result != 0} {
return
}
gdb_test "info breakpoints 7" ".*y.*0x.*in baz.*" \
"breakpoint at baz is resolved"
# 4) 'info files' must display $srcfile3.
gdb_test "info files" \
"^(?=(.*${lib_basename})).*" \
"info files must display ${lib_basename}"
# Unload symbols manually without the program actually unloading
# the library.
gdb_test "remove-symbol-file -a addr" \
"" \
"remove-symbol-file -a addr" \
"Remove symbol table from file \".*${lib_basename}\\.so\"\\?\
# 5) Continue to bar in $srcfile3 to ensure that the breakpoint
# was bound correctly after adding $shilb_name.
set lnum_bar [gdb_get_line_number "break at bar" $srcfile3]
gdb_continue_to_breakpoint bar ".*${lib_basename}\\.c:$lnum_bar.*"
# 6) Set a breakpoint at foo in $srcfile3.
set result [gdb_breakpoint foo]
if {!$result} {
return
}
# 7) Continue to foo in $srcfile3 to ensure that the breakpoint
# was bound correctly.
set lnum_foo [gdb_get_line_number "break at foo" $srcfile3]
gdb_continue_to_breakpoint foo ".*${lib_basename}\\.c:$lnum_foo.*"
# 8) Set a breakpoint at gdb_remove_symbol_file in $srcfile for
# removing the library's symbols.
set result [gdb_breakpoint gdb_remove_symbol_file]
if {!$result} {
return
}
# 9) Continue to gdb_remove_symbol_file in $srcfile.
gdb_continue_to_breakpoint gdb_remove_symbol_file
# 10) Remove the library's symbols using 'remove-symbol-file'.
set result [gdb_test "remove-symbol-file ${remove_expr}" \
""\
"remove-symbol-file" \
"Remove symbol table from file \".*${lib_basename}\\.so\"\\?\
.*\\(y or n\\) " \
"y"
"y"]
if {$result != 0} {
return
}
gdb_test "info breakpoints 7" ".*PENDING.*" \
"breakpoint at baz is pending"
# 11) 'info files' must not display ${lib_basename}, anymore.
gdb_test "info files" \
"^(?!(.*${lib_basename})).*" \
"info files must not display ${lib_basename}"
# Check that execution can continue without error. If GDB leaves
# breakpoints behind, we'll get back a spurious SIGTRAP.
set lnum_end [gdb_get_line_number "end here"]
gdb_breakpoint $lnum_end
gdb_continue_to_breakpoint "end here" ".*end here.*"
# 12) Check that the breakpoints at foo and bar are pending after
# removing the library's symbols.
gdb_test "info breakpoints 3" \
".*PENDING.*" \
"breakpoint at foo is pending"
gdb_test "info breakpoints 4" \
".*PENDING.*" \
"breakpoint at bar is pending"
# 13) Check that the execution can continue without error.
set lnum_reload [gdb_get_line_number "reload lib here"]
gdb_breakpoint $lnum_reload
gdb_continue_to_breakpoint reload ".*${srcfile}:$lnum_reload.*"
# 14) Regression test for a stale breakpoints bug. Check whether
# unloading symbols manually without the program actually unloading
# the library, when breakpoints are inserted doesn't leave stale
# breakpoints behind.
with_test_prefix "stale bkpts" {
# Force breakpoints always inserted.
gdb_test_no_output "set breakpoint always-inserted on"
# Get past the library reload.
gdb_continue_to_breakpoint gdb_add_symbol_file
# Load the library's symbols.
gdb_test "add-symbol-file ${lib_syms} addr" \
"Reading symbols from .*${lib_syms}\\.\\.\\." \
"add-symbol-file ${lib_basename}.so addr" \
"add symbol table from file \".*${lib_syms}\"\
at.*\\(y or n\\) " \
"y"
# Set a breakpoint at baz, in the library.
gdb_breakpoint baz
gdb_test "info breakpoints 7" ".*y.*0x.*in baz.*" \
"breakpoint at baz is resolved"
# Unload symbols manually without the program actually unloading
# the library.
gdb_test "remove-symbol-file ${remove_expr}" \
"" \
"remove-symbol-file" \
"Remove symbol table from file \".*${lib_basename}\\.so\"\\?\
.*\\(y or n\\) " \
"y"
gdb_test "info breakpoints 7" ".*PENDING.*" \
"breakpoint at baz is pending"
# Check that execution can continue without error. If GDB leaves
# breakpoints behind, we'll get back a spurious SIGTRAP.
set lnum_end [gdb_get_line_number "end here"]
gdb_breakpoint $lnum_end
gdb_continue_to_breakpoint "end here" ".*end here.*"
}
}
foreach remove_expr [list addr bar "bar + 0x10" "${lib_syms}" ] {
# Don't use full filenames in the test prefix. Also, add '-a' to
# all the REMOVE_EXPR values which are addresses rather than
# filenames.
set prefix $remove_expr
if { $prefix == $lib_syms } {
set prefix [file tail $prefix]
} else {
set remove_expr "-a $remove_expr"
}
with_test_prefix "remove_expr=$prefix" {
do_test $remove_expr
}
}