]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb: new maintenance command to help debug remote argument issues
authorAndrew Burgess <aburgess@redhat.com>
Sun, 7 Jan 2024 13:46:29 +0000 (13:46 +0000)
committerAndrew Burgess <aburgess@redhat.com>
Sun, 21 Sep 2025 16:20:57 +0000 (17:20 +0100)
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>
gdb/NEWS
gdb/doc/gdb.texinfo
gdb/remote.c
gdb/testsuite/gdb.base/maint-test-remote-args.exp [new file with mode: 0644]
gdbsupport/gdb_argv_vec.h

index 87c72cb3d9fc9b288ccda4a3f254248d08e92ef0..8be367d2424d66d3ba99b3e8f58985f9708b53b9 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
 
 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
index 462b86c87e6c3d02855c3a3c1cfb89ff757ff9ae..e851588382065e577b648716ff4ab74efcb5920e 100644 (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]
index e1b54c6f9afa5074b8eeb62e987477a54da95b64..9fc8712f6990109038156783a7e01f2b6e9db423 100644 (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);
diff --git a/gdb/testsuite/gdb.base/maint-test-remote-args.exp b/gdb/testsuite/gdb.base/maint-test-remote-args.exp
new file mode 100644 (file)
index 0000000..6cd3006
--- /dev/null
@@ -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\\)"]
index 1f3b6dbb40e6a64a86bde1ea09fc06e3c50a4a41..43571ae173a7b803c2558719ba5799bf3369b27a 100644 (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 */