gdb: new maintenance command to help debug remote argument issues

Add a new maintenance command 'maint test-remote-args', this command
takes an argument string and splits it using gdb::remote_args::split
and then joins the result using gdb::remote_args::join and prints all
of the results.  This is useful for diagnosing problems with remote
argument passing.

This new command is identical to what the remote argument self-tests
do, but while I was working on improving remote argument passing it
was far easier to have a command that I could just throw example
strings at, rather than having to add new selftests and recompile
GDB.

I ended up adding a couple of additional helper functions to the
gdb::argv_vec class.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Tested-By: Guinevere Larsen <guinevere@redhat.com>
Approved-By: Kevin Buettner <kevinb@redhat.com>
This commit is contained in:
Andrew Burgess
2024-01-07 13:46:29 +00:00
parent d39cae626f
commit 84dd63f327
5 changed files with 141 additions and 0 deletions

View File

@@ -18,6 +18,12 @@
GNU/Linux/MicroBlaze (gdbserver) microblazeel-*linux*
* New commands
maintenance test-remote-args ARGS
Test splitting and joining of inferior arguments ARGS as they would
be split and joined when being passed to a remote target.
* Changed remote packets
single-inf-arg in qSupported

View File

@@ -42842,6 +42842,26 @@ 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 test-remote-args
@item maint test-remote-args @var{args}
For targets that don't support passing inferior arguments as a single
string (@pxref{single-inf-arg}), @value{GDBN} will attempt to split
the inferior arguments before passing them to the remote target, and
the remote target might choose to join the inferior arguments upon
receipt. Historically gdbserver did join inferior arguments, but now
it will request inferior arguments be passed as a single string if
@value{GDBN} supports this feature.
This maintenance command splits @var{args} as @value{GDBN} would
normally split such an argument string before passing the arguments to
a remote target, the split arguments are then printed.
The split arguments are then joined together as gdbserver would join
them, and the result is printed.
This command is intended to help diagnose issues passing inferior
arguments to remote targets.
@kindex maint set backtrace-on-fatal-signal
@kindex maint show backtrace-on-fatal-signal
@item maint set backtrace-on-fatal-signal [on|off]

View File

@@ -81,6 +81,7 @@
#include "gdbsupport/selftest.h"
#include "cli/cli-style.h"
#include "gdbsupport/remote-args.h"
#include "gdbsupport/gdb_argv_vec.h"
/* The remote target. */
@@ -12278,6 +12279,51 @@ cli_packet_command (const char *args, int from_tty)
send_remote_packet (view, &cb);
}
/* Implement 'maint test-remote-args' command.
Treat ARGS as an argument string. Split the remote arguments using
gdb::remote_args::split, and then join using gdb::remote_args::join.
The split and joined arguments are printed out. Additionally, the
joined arguments are split and joined a second time, and compared to the
result of the first join, this provides some basic validation that GDB
sess the joined arguments as equivalent to the original argument
string. */
static void
test_remote_args_command (const char *args, int from_tty)
{
std::vector<std::string> split_args = gdb::remote_args::split (args);
gdb_printf ("Input (%s)\n", args);
for (const std::string &a : split_args)
gdb_printf (" (%s)\n", a.c_str ());
gdb::argv_vec tmp_split_args;
for (const std::string &a : split_args)
tmp_split_args.emplace_back (xstrdup (a.c_str ()));
std::string joined_args = gdb::remote_args::join (tmp_split_args.get ());
gdb_printf ("Output (%s)\n", joined_args.c_str ());
std::vector<std::string> resplit = gdb::remote_args::split (joined_args);
tmp_split_args.clear ();
for (const std::string &a : resplit)
tmp_split_args.emplace_back (xstrdup (a.c_str ()));
std::string rejoined = gdb::remote_args::join (tmp_split_args.get ());
if (joined_args != rejoined || split_args != resplit)
{
gdb_printf ("FAILURE ON REJOINING\n");
gdb_printf ("Resplit args:\n");
for (const auto & a : resplit)
gdb_printf (" (%s)\n", a.c_str ());
gdb_printf ("Rejoined (%s)\n", rejoined.c_str ());
}
}
#if 0
/* --------- UNIT_TEST for THREAD oriented PACKETS ------------------- */
@@ -16726,6 +16772,20 @@ from the target."),
/* Eventually initialize fileio. See fileio.c */
initialize_remote_fileio (&remote_set_cmdlist, &remote_show_cmdlist);
add_cmd ("test-remote-args", class_maintenance,
test_remote_args_command, _("\
Test remote argument splitting and joining.\n \
maintenance test-remote-args ARGS\n\
For remote targets that don't support passing inferior arguments as a\n\
single string, GDB needs to split the inferior arguments before passing\n\
them, and gdbserver needs to join the arguments it receives.\n\
This command splits ARGS just as GDB would before passing them to a\n\
remote target, and prints the result. This command then joins the\n\
arguments just as gdbserver would, and prints the results.\n\
This command is useful in diagnosing problems when passing arguments\n\
between GDB and a remote target."),
&maintenancelist);
#if GDB_SELF_TEST
selftests::register_test ("remote_memory_tagging",
selftests::test_memory_tagging_functions);

View File

@@ -0,0 +1,40 @@
# Copyright 2024-2025 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 'maint test-remote-args' command.
#
# We do minimal testing in here. If you are thinking of adding a new
# test here then you are most likely adding the test in the wrong
# place. Remote argument testing is checked in the following test
# scripts: gdb.base/args.exp, gdb.base/inferior-args.exp,
# gdb.base/startup-with-shell.exp, and gdb.python/py-inferior.exp.
# The test gdb.gdb/unittest.exp also runs 'maint selftest
# remote-args', which are the remote argument self tests.
#
# If you have a new test for an argument that was being passed
# incorrectly, then add the test to one of those scripts.
#
# This file is ONLY for validating that the 'maint test-remote-args'
# command itself is working.
gdb_start
gdb_test "maint test-remote-args a b c" \
[multi_line \
"Input \\(a b c\\)" \
" \\(a\\)" \
" \\(b\\)" \
" \\(c\\)" \
"Output \\(a b c\\)"]

View File

@@ -90,6 +90,15 @@ public:
m_args.push_back (value);
}
/* Like calling emplace_back on the underlying vector. This class takes
ownership of the value added to the vector, and will release the value
by calling xfree() on it when this object is destroyed. */
template<typename... Args>
reference emplace_back (Args &&...args)
{
return m_args.emplace_back (std::forward<Args> (args)...);
}
/* Non constant iterator to start of m_args. */
iterator begin ()
{
@@ -133,6 +142,12 @@ public:
{
return m_args.empty ();
}
/* Clear the argument vector. */
void clear ()
{
free_vector_argv (m_args);
}
};
} /* namespac gdb */