public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver
@ 2023-08-16 15:54 Andrew Burgess
  2023-08-16 15:54 ` [PATCH 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
                   ` (12 more replies)
  0 siblings, 13 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:54 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

The goal of this series is to improve the user experience when using a
local (running on the same machine as GDB) gdbserver.

Outside of testing, there are still plenty of occasions when a user
can end up running gdbserver on the same machine as GDB, tools like
valgrind communicate via the remote protocol, as do many simulators
(other than those built into GDB).

This series includes the following improvements:

  1. Better handling of gdbserver's default executable and arguments.
  This applies to remote gdbservers and local gdbservers,

  2. Allow sysroot 'target:' prefix to be ignored when gdbserver is
  running locally to GDB, and

  3. Allow the user to skip setting 'remote exec-file' if gdbserver is
  running locally to GDB.

---

Andrew Burgess (10):
  gdb: have remote_target::extended_remote_run take the exec filename
  gdb: improve how 'remote exec-file' is stored and accessed
  gdb: improve show text and help text for 'remote exec-file'
  gdb/gdbserver: add new qDefaultExecAndArgs packet
  gdb: detect when gdbserver has no default executable set
  gdb: make use of is_target_filename
  gdb: add qMachineId packet
  gdb: remote filesystem can be local to GDB in some cases
  gdb: use exec_file with remote targets when possible
  gdb: remote the get_remote_exec_file function

 gdb/Makefile.in                               |   3 +
 gdb/NEWS                                      |  45 ++
 gdb/build-id.c                                |   2 +-
 gdb/configure.nat                             |   2 +-
 gdb/doc/gdb.texinfo                           | 160 ++++
 gdb/gdb_bfd.h                                 |   8 +
 gdb/linux-nat.c                               |  35 +
 gdb/nat/linux-machine-id.c                    |  60 ++
 gdb/nat/linux-machine-id.h                    |  44 ++
 gdb/remote-machine-id.c                       |  69 ++
 gdb/remote-machine-id.h                       | 108 +++
 gdb/remote.c                                  | 683 +++++++++++++++---
 gdb/testsuite/gdb.base/remote-exec-file.exp   |   7 +-
 gdb/testsuite/gdb.multi/gdb-settings.exp      |  13 +
 gdb/testsuite/gdb.server/ext-run.exp          | 131 +++-
 .../gdb.server/fetch-exec-and-args.c          |  34 +
 .../gdb.server/fetch-exec-and-args.exp        | 253 +++++++
 gdb/testsuite/gdb.server/server-local-fs.c    |  22 +
 gdb/testsuite/gdb.server/server-local-fs.exp  | 138 ++++
 gdb/testsuite/gdb.server/sysroot.exp          |  90 +--
 gdbserver/Makefile.in                         |   1 +
 gdbserver/configure.srv                       |   2 +-
 gdbserver/linux-low.cc                        |  19 +
 gdbserver/linux-low.h                         |   2 +
 gdbserver/server.cc                           |  47 ++
 gdbserver/target.cc                           |   8 +
 gdbserver/target.h                            |   9 +
 27 files changed, 1835 insertions(+), 160 deletions(-)
 create mode 100644 gdb/nat/linux-machine-id.c
 create mode 100644 gdb/nat/linux-machine-id.h
 create mode 100644 gdb/remote-machine-id.c
 create mode 100644 gdb/remote-machine-id.h
 create mode 100644 gdb/testsuite/gdb.server/fetch-exec-and-args.c
 create mode 100644 gdb/testsuite/gdb.server/fetch-exec-and-args.exp
 create mode 100644 gdb/testsuite/gdb.server/server-local-fs.c
 create mode 100644 gdb/testsuite/gdb.server/server-local-fs.exp


base-commit: a345d14fa65c2b69f2ba3abac8847b1c6a4dc656
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 01/10] gdb: have remote_target::extended_remote_run take the exec filename
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
@ 2023-08-16 15:54 ` Andrew Burgess
  2023-08-23  9:30   ` Alexandra Petlanova Hajkova
  2023-08-16 15:54 ` [PATCH 02/10] gdb: improve how 'remote exec-file' is stored and accessed Andrew Burgess
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:54 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Small refactor, have remote_target::extended_remote_run take the name
of the executable to run rather than looking it up directly, the one
caller of this function has already looked up the remote-exec
filename.

There should be no user visible changes after this commit.
---
 gdb/remote.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 6fefabac0ce..2b52ccb2555 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1223,7 +1223,8 @@ class remote_target : public process_stratum_target
   void remote_kill_k ();
 
   void extended_remote_disable_randomization (int val);
-  int extended_remote_run (const std::string &args);
+  int extended_remote_run (const char *remote_exec_file,
+			   const std::string &args);
 
   void send_environment_packet (const char *action,
 				const char *packet,
@@ -10448,11 +10449,11 @@ remote_target::extended_remote_disable_randomization (int val)
 }
 
 int
-remote_target::extended_remote_run (const std::string &args)
+remote_target::extended_remote_run (const char *remote_exec_file,
+				    const std::string &args)
 {
   struct remote_state *rs = get_remote_state ();
   int len;
-  const char *remote_exec_file = get_remote_exec_file ();
 
   /* If the user has disabled vRun support, or we have detected that
      support is not available, do not try it.  */
@@ -10647,7 +10648,7 @@ Remote replied unexpectedly while setting startup-with-shell: %s"),
   extended_remote_set_inferior_cwd ();
 
   /* Now restart the remote server.  */
-  run_worked = extended_remote_run (args) != -1;
+  run_worked = extended_remote_run (remote_exec_file, args) != -1;
   if (!run_worked)
     {
       /* vRun was not supported.  Fail if we need it to do what the
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 02/10] gdb: improve how 'remote exec-file' is stored and accessed
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
  2023-08-16 15:54 ` [PATCH 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
@ 2023-08-16 15:54 ` Andrew Burgess
  2023-08-23  8:44   ` Alexandra Petlanova Hajkova
  2023-08-16 15:54 ` [PATCH 03/10] gdb: improve show text and help text for 'remote exec-file' Andrew Burgess
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:54 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit makes two related changes.  The goal of the commit is to
update the 'remote exec-file' setting to work correctly in a
multi-inferior setup.  To do this I have switched from the older
style add_setshow_* function, which uses a single backing variable, to
the newer style add_setshow_* functions that uses a get/set callback.

The get/set callbacks now directly access the state held in the
progspace which ensures that the correct value is always returned.

However, the new get/set API requires that the get callback return a
reference to the setting's value, which in this case needs to be a
std::string.

Currently the 'remote exec-file' setting is stored as a 'char *'
string, which isn't going to work.

And so, this commit also changes 'remote exec-file' to be stored as a
std::string within the progspace.

Now, when switching between multiple inferiors, GDB can correctly
inform the user about the value of the 'remote exec-file' setting.
---
 gdb/remote.c                             | 70 ++++++++++++------------
 gdb/testsuite/gdb.multi/gdb-settings.exp | 13 +++++
 2 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 2b52ccb2555..dc5dd24797e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1223,7 +1223,7 @@ class remote_target : public process_stratum_target
   void remote_kill_k ();
 
   void extended_remote_disable_randomization (int val);
-  int extended_remote_run (const char *remote_exec_file,
+  int extended_remote_run (const std::string &remote_exec_file,
 			   const std::string &args);
 
   void send_environment_packet (const char *action,
@@ -1340,14 +1340,7 @@ is_remote_target (process_stratum_target *target)
 }
 
 /* Per-program-space data key.  */
-static const registry<program_space>::key<char, gdb::xfree_deleter<char>>
-  remote_pspace_data;
-
-/* The variable registered as the control variable used by the
-   remote exec-file commands.  While the remote exec-file setting is
-   per-program-space, the set/show machinery uses this as the 
-   location of the remote exec-file value.  */
-static std::string remote_exec_file_var;
+static const registry<program_space>::key<std::string> remote_pspace_data;
 
 /* The size to align memory write packets, when practical.  The protocol
    does not guarantee any alignment, and gdb will generate short
@@ -1669,47 +1662,50 @@ remote_target::get_remote_state ()
 
 /* Fetch the remote exec-file from the current program space.  */
 
-static const char *
+static const std::string &
 get_remote_exec_file (void)
 {
-  char *remote_exec_file;
-
-  remote_exec_file = remote_pspace_data.get (current_program_space);
-  if (remote_exec_file == NULL)
-    return "";
-
-  return remote_exec_file;
+  const std::string *remote_exec_file
+    = remote_pspace_data.get (current_program_space);
+  if (remote_exec_file == nullptr)
+    remote_exec_file = remote_pspace_data.emplace (current_program_space);
+  return *remote_exec_file;
 }
 
 /* Set the remote exec file for PSPACE.  */
 
 static void
 set_pspace_remote_exec_file (struct program_space *pspace,
-			     const char *remote_exec_file)
+			     const std::string &filename)
 {
-  char *old_file = remote_pspace_data.get (pspace);
-
-  xfree (old_file);
-  remote_pspace_data.set (pspace, xstrdup (remote_exec_file));
+  remote_pspace_data.clear (pspace);
+  remote_pspace_data.emplace (pspace, filename);
 }
 
-/* The "set/show remote exec-file" set command hook.  */
+/* The "set remote exec-file" callback.  */
 
 static void
-set_remote_exec_file (const char *ignored, int from_tty,
-		      struct cmd_list_element *c)
+set_remote_exec_file_cb (const std::string &filename)
 {
   set_pspace_remote_exec_file (current_program_space,
-			       remote_exec_file_var.c_str ());
+			       filename);
+}
+
+/* Get the value for the 'set remote exec-file' user setting.  */
+
+static const std::string &
+get_remote_exec_file_cb ()
+{
+  return get_remote_exec_file ();
 }
 
-/* The "set/show remote exec-file" show command hook.  */
+/* Implement the "show remote exec-file" command.  */
 
 static void
 show_remote_exec_file (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *cmd, const char *value)
 {
-  gdb_printf (file, "%s\n", get_remote_exec_file ());
+  gdb_printf (file, "%s\n", get_remote_exec_file ().c_str ());
 }
 
 static int
@@ -10449,7 +10445,7 @@ remote_target::extended_remote_disable_randomization (int val)
 }
 
 int
-remote_target::extended_remote_run (const char *remote_exec_file,
+remote_target::extended_remote_run (const std::string &remote_exec_file,
 				    const std::string &args)
 {
   struct remote_state *rs = get_remote_state ();
@@ -10463,10 +10459,11 @@ remote_target::extended_remote_run (const char *remote_exec_file,
   strcpy (rs->buf.data (), "vRun;");
   len = strlen (rs->buf.data ());
 
-  if (strlen (remote_exec_file) * 2 + len >= get_remote_packet_size ())
+  if (remote_exec_file.size () * 2 + len >= get_remote_packet_size ())
     error (_("Remote file name too long for run packet"));
-  len += 2 * bin2hex ((gdb_byte *) remote_exec_file, rs->buf.data () + len,
-		      strlen (remote_exec_file));
+  len += 2 * bin2hex ((gdb_byte *) remote_exec_file.data (),
+		      rs->buf.data () + len,
+		      remote_exec_file.size ());
 
   if (!args.empty ())
     {
@@ -10501,7 +10498,7 @@ remote_target::extended_remote_run (const char *remote_exec_file,
 		 "try \"set remote exec-file\"?"));
       else
 	error (_("Running \"%s\" on the remote target failed"),
-	       remote_exec_file);
+	       remote_exec_file.c_str ());
     default:
       gdb_assert_not_reached ("bad switch");
     }
@@ -10618,7 +10615,7 @@ extended_remote_target::create_inferior (const char *exec_file,
   int run_worked;
   char *stop_reply;
   struct remote_state *rs = get_remote_state ();
-  const char *remote_exec_file = get_remote_exec_file ();
+  const std::string &remote_exec_file = get_remote_exec_file ();
 
   /* If running asynchronously, register the target file descriptor
      with the event loop.  */
@@ -15477,10 +15474,11 @@ Transfer files to and from the remote target system."),
 	   &remote_cmdlist);
 
   add_setshow_string_noescape_cmd ("exec-file", class_files,
-				   &remote_exec_file_var, _("\
+				   _("\
 Set the remote pathname for \"run\"."), _("\
 Show the remote pathname for \"run\"."), NULL,
-				   set_remote_exec_file,
+				   set_remote_exec_file_cb,
+				   get_remote_exec_file_cb,
 				   show_remote_exec_file,
 				   &remote_set_cmdlist,
 				   &remote_show_cmdlist);
diff --git a/gdb/testsuite/gdb.multi/gdb-settings.exp b/gdb/testsuite/gdb.multi/gdb-settings.exp
index 0ebd2bf2998..e5922221d47 100644
--- a/gdb/testsuite/gdb.multi/gdb-settings.exp
+++ b/gdb/testsuite/gdb.multi/gdb-settings.exp
@@ -71,6 +71,7 @@ foreach_with_prefix inf $inferiors {
     gdb_test_no_output "set args inf${inf}-args"
     gdb_test_no_output "set cwd /inf${inf}-cwd"
     gdb_test_no_output "set inferior-tty /inf${inf}-tty"
+    gdb_test_no_output "set remote exec-file /inf${inf}-remote-exec"
 }
 
 # Check settings are still correct for each inferior.
@@ -88,6 +89,9 @@ foreach_with_prefix inf $inferiors {
     gdb_test "with inferior-tty tmp-value -- print 1" " = 1"
     gdb_test "show inferior-tty" "/inf${inf}-tty.*"
 
+    gdb_test "with remote exec-file tmp-value -- print 1" " = 1"
+    gdb_test "show remote exec-file" "/inf${inf}-remote-exec"
+
     # If the inferiors are running check $_gdb_setting_str and
     # $_gdb_setting return the correct values.
     if { $run } {
@@ -101,6 +105,11 @@ foreach_with_prefix inf $inferiors {
 	    "\"/inf${inf}-tty\""
 	gdb_test {print $_gdb_setting("inferior-tty")} \
 	    "\"/inf${inf}-tty\""
+
+	gdb_test {print $_gdb_setting_str("remote exec-file")} \
+	    "\"/inf${inf}-remote-exec\""
+	gdb_test {print $_gdb_setting("remote exec-file")} \
+	    "\"/inf${inf}-remote-exec\""
     }
 
     # Check the settings can be read from Python.
@@ -109,6 +118,8 @@ foreach_with_prefix inf $inferiors {
 	gdb_test "python print(gdb.parameter('cwd'))" "/inf${inf}-cwd"
 	gdb_test "python print(gdb.parameter('inferior-tty'))" \
 	    "/inf${inf}-tty"
+	gdb_test "python print(gdb.parameter('remote exec-file'))" \
+	    "/inf${inf}-remote-exec"
     }
 
     # Check the settings can be read from Guile.
@@ -119,5 +130,7 @@ foreach_with_prefix inf $inferiors {
 	    "/inf${inf}-cwd"
 	gdb_test "guile (print (parameter-value \"inferior-tty\"))" \
 	    "/inf${inf}-tty"
+	gdb_test "guile (print (parameter-value \"remote exec-file\"))" \
+	    "/inf${inf}-remote-exec"
     }
 }
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 03/10] gdb: improve show text and help text for 'remote exec-file'
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
  2023-08-16 15:54 ` [PATCH 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
  2023-08-16 15:54 ` [PATCH 02/10] gdb: improve how 'remote exec-file' is stored and accessed Andrew Burgess
@ 2023-08-16 15:54 ` Andrew Burgess
  2023-08-23 11:36   ` Mark Wielaard
  2023-08-24  8:56   ` Alexandra Petlanova Hajkova
  2023-08-16 15:55 ` [PATCH 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
                   ` (9 subsequent siblings)
  12 siblings, 2 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:54 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

The current behaviour for 'show remote exec-file' is this:

  (gdb) show remote exec-file

  (gdb) set remote exec-file /abc
  (gdb) show remote exec-file
  /abc
  (gdb)

The first output, the blank line, is just GDB showing the default
empty value.

This output is not really inline with GDB's more full sentence style
output, so in this commit I've updated things, the output is now:

  (gdb) show remote exec-file
  The remote exec-file is unset, the default remote executable will be used.
  (gdb) set remote exec-file /abc
  (gdb) show remote exec-file
  The remote exec-file is "/abc".
  (gdb)

Which I think is more helpful to the user.

I have also updated the help text for this setting.  Previously we had
a set/show header line, but no body text, now we have:

  (gdb) help show remote exec-file
  Show the remote pathname for starting inferiors.
  This is the path, on the remote target, used when starting an inferior,
  for example with the "run", "start", or "starti" commands.
  This setting is only useful when debugging a remote target, otherwise,
  this setting is not used.
  (gdb)

Which is hopefully more helpful.
---
 gdb/remote.c                                | 15 ++++++++++++---
 gdb/testsuite/gdb.base/remote-exec-file.exp |  7 +++++--
 gdb/testsuite/gdb.multi/gdb-settings.exp    |  2 +-
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index dc5dd24797e..6a61a0e41ac 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1705,7 +1705,12 @@ static void
 show_remote_exec_file (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *cmd, const char *value)
 {
-  gdb_printf (file, "%s\n", get_remote_exec_file ().c_str ());
+  const std::string &filename = get_remote_exec_file ();
+  if (filename.empty ())
+    gdb_printf (file, _("The remote exec-file is unset, the default remote "
+			"executable will be used.\n"));
+  else
+    gdb_printf (file, "The remote exec-file is \"%s\".\n", filename.c_str ());
 }
 
 static int
@@ -15475,8 +15480,12 @@ Transfer files to and from the remote target system."),
 
   add_setshow_string_noescape_cmd ("exec-file", class_files,
 				   _("\
-Set the remote pathname for \"run\"."), _("\
-Show the remote pathname for \"run\"."), NULL,
+Set the remote pathname for starting inferiors."), _("\
+Show the remote pathname for starting inferiors."), _("\
+This is the path, on the remote target, used when starting an inferior,\n\
+for example with the \"run\", \"start\", or \"starti\" commands.\n\
+This setting is only useful when debugging a remote target, otherwise,\n\
+this setting is not used."),
 				   set_remote_exec_file_cb,
 				   get_remote_exec_file_cb,
 				   show_remote_exec_file,
diff --git a/gdb/testsuite/gdb.base/remote-exec-file.exp b/gdb/testsuite/gdb.base/remote-exec-file.exp
index 0b198630a07..1411f9636be 100644
--- a/gdb/testsuite/gdb.base/remote-exec-file.exp
+++ b/gdb/testsuite/gdb.base/remote-exec-file.exp
@@ -37,10 +37,13 @@ with_test_prefix "set inf 2" {
 
 with_test_prefix "show inf 1" {
     gdb_test "inferior 1" "Switching to inferior 1.*"
-    gdb_test "show remote exec-file" "prog1"
+    gdb_test "show remote exec-file" \
+	"The remote exec-file is \"prog1\"\\."
+
 }
 
 with_test_prefix "show inf 2" {
     gdb_test "inferior 2" "Switching to inferior 2.*"
-    gdb_test "show remote exec-file" "prog2"
+    gdb_test "show remote exec-file" \
+	"The remote exec-file is \"prog2\"\\."
 }
diff --git a/gdb/testsuite/gdb.multi/gdb-settings.exp b/gdb/testsuite/gdb.multi/gdb-settings.exp
index e5922221d47..2432192ca9a 100644
--- a/gdb/testsuite/gdb.multi/gdb-settings.exp
+++ b/gdb/testsuite/gdb.multi/gdb-settings.exp
@@ -90,7 +90,7 @@ foreach_with_prefix inf $inferiors {
     gdb_test "show inferior-tty" "/inf${inf}-tty.*"
 
     gdb_test "with remote exec-file tmp-value -- print 1" " = 1"
-    gdb_test "show remote exec-file" "/inf${inf}-remote-exec"
+    gdb_test "show remote exec-file" "/inf${inf}-remote-exec.*"
 
     # If the inferiors are running check $_gdb_setting_str and
     # $_gdb_setting return the correct values.
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (2 preceding siblings ...)
  2023-08-16 15:54 ` [PATCH 03/10] gdb: improve show text and help text for 'remote exec-file' Andrew Burgess
@ 2023-08-16 15:55 ` Andrew Burgess
  2023-08-16 16:36   ` Eli Zaretskii
  2023-08-28 15:35   ` Tom Tromey
  2023-08-16 15:55 ` [PATCH 05/10] gdb: detect when gdbserver has no default executable set Andrew Burgess
                   ` (8 subsequent siblings)
  12 siblings, 2 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit adds a new remote protocol packet qDefaultExecAndArgs, and
updates GDB to use it.

When gdbserver is started a user can provide an executable and
arguments, these are used (by the remote target) to start an initial
inferior, this is the inferior to which GDB first connects.

When GDB is connected in extended-remote mode, if the user does a
'run' without specifying a new 'remote exec-file' then the executable
given on the gdbserver command line is reused to start the new
inferior.

Interestingly, the arguments given on the gdbserver command line are
only used when starting the first inferior, subsequent inferiors will
be passed an empty argument string by GDB.  This might catch out a
user, causing the rerun to behave differently than the first run.

In this commit I will add a new qDefaultExecAndArgs packet, which I
think will improve the experience in this area.

The new qDefaultExecAndArgs packet is sent from GDB, and gdbserver
replies with a packet that includes the executable filename and the
argument string that were used for starting the initial inferior.

On the GDB side this information can be used to update GDB's state,
the 'show remote exec-file' will reflect how gdbserver was started,
and 'show args' will reflect the arguments used for starting the
inferior.

As a result of updating the args, if the user restarts the inferior,
then this same argument string will be passed back to the remote
target, and used for the new inferior.  Thus, rerunning the inferior
will behave just like the initial inferior, which I think is a good
improvement.

Finally, GDB will warn if the user has 'set remote exec-file' and
then connects to a gdbserver that was started with some alternative
filename, like this:

  (gdb) set remote exec-file /tmp/foo
  (gdb) target remote | gdbserver --once - /tmp/bar
  ... snip ...
  warning: updating 'remote exec-file' to '/tmp/bar' to match remote target
  ... snip ...

I made the choice to have GDB update the remote exec-file setting to
match the remote, as, after the 'target remote', we are connected to
an inferior that is running /tmp/bar (in this case), so trying to hang
onto the non-matching user supplied setting doesn't seem helpful.

There is one case where I can see this choice being a problem, if a
user does:

  (gdb) set remote exec-file /tmp/foo
  (gdb) target extended-remote | gdbserver --once - /tmp/bar
  ... snip ...
  warning: updating 'remote exec-file' to '/tmp/bar' to match remote target
  ... snip ...
  (gdb) run

In this case, prior to this patch, they would 'run' /tmp/foo, while
after this patch, they will run /tmp/bar.  I think it is unfortunate
that I'm breaking this use case, but, I'm not _that_ sorry -- just
start the gdbserver with the correct executable and the problem goes
away.
---
 gdb/NEWS                                      |  15 ++
 gdb/doc/gdb.texinfo                           |  30 +++
 gdb/remote.c                                  | 152 ++++++++++++-
 .../gdb.server/fetch-exec-and-args.c          |  34 +++
 .../gdb.server/fetch-exec-and-args.exp        | 207 ++++++++++++++++++
 gdbserver/server.cc                           |  33 +++
 6 files changed, 470 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.server/fetch-exec-and-args.c
 create mode 100644 gdb/testsuite/gdb.server/fetch-exec-and-args.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index ef2b3b3a4f5..9839330c46d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -98,6 +98,14 @@
   user that the end of file has been reached, refers the user to the
   newly added '.' argument
 
+* When connecting to a remote server, if the server supports the
+  qDefaultExecAndArgs packet, then GDB will copy the argument string
+  from the server and update the 'args' setting, as if 'set args ...'
+  had been used.  This means that the arguments are visible from GDB
+  using 'show args', and that, if using the extended-remote protocol,
+  subsequent runs of the inferior will use the same arguments as the
+  first run.
+
 * New commands
 
 set debug breakpoint on|off
@@ -257,6 +265,13 @@ info main
   ** gdb.Progspace now has the new method "objfile_for_address".  This
      returns the gdb.Objfile, if any, that covers a given address.
 
+* New remote packets
+
+qDefaultExecAndArgs
+  This packet returns the executable filename and argument string with
+  which the server was started.  If no such information was given to
+  the server then this is reflected in the reply.
+
 *** Changes in GDB 13
 
 * MI version 1 is deprecated, and will be removed in GDB 14.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 814cc6d714a..8f8db2c9bfb 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -44718,6 +44718,36 @@
 A badly formed request or an error was encountered.
 @end table
 
+@item qDefaultExecAndArgs
+@cindex query executable, remote request
+@cindex query program arguments, remote request
+@cindex @samp{qDefaultExecAndArgs} packet
+Return the program filename and argument string with which the remote
+server was started, if the remote server was started with such things.
+If the remote server was started without the filename of a program to
+execute, or without any arguments, then the reply indicates this.
+
+Reply:
+@table @samp
+@item U
+The program filename and arguments are not set.  If @var{GDBN} wants
+to start a new inferior, for example with @samp{vRun}, then it will
+need to provide the program filename to use.
+
+@item S;@var{PP@dots{}};@var{AA@dots{}}
+The program filename provided to the remote server when it started was
+@var{PP@dots{}}, which is a hex encoded string, and the argument
+string passed to the program when started by the server was
+@var{AA@dots{}}, which is also a hex encoded string.
+
+It is valid for either, or both, of @var{PP@dots{}} and
+@var{AA@dots{}} to be the empty string.
+
+@item E @var{NN}
+@itemx E.errtext
+Indicates an error was encountered.
+@end table
+
 @item Qbtrace:bts
 Enable branch tracing for the current thread using Branch Trace Store.
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 6a61a0e41ac..870153731b0 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -79,6 +79,7 @@
 #include <unordered_map>
 #include "async-event.h"
 #include "gdbsupport/selftest.h"
+#include "cli/cli-style.h"
 
 /* The remote target.  */
 
@@ -302,6 +303,9 @@ enum {
      packets and the tag violation stop replies.  */
   PACKET_memory_tagging_feature,
 
+  /* Support the qDefaultExecAndArgs packet.  */
+  PACKET_qDefaultExecAndArgs,
+
   PACKET_MAX
 };
 
@@ -691,6 +695,79 @@ struct remote_features
   packet_config m_protocol_packets[PACKET_MAX];
 };
 
+/* Data structure used to hold the results of the qDefaultExecAndArgs
+   packet.  */
+
+struct remote_exec_and_args_info
+{
+  /* The result state reflects whether the packet is supported by this
+     remote target, and then which bits of state the remote target actually
+     returned to GDB .  */
+  enum class state
+  {
+    /* The remote does not support the qDefaultExecAndArgs packet.  GDB
+       should not make assumptions about the remote target's executable
+       and arguments.  */
+    UNKNOWN,
+
+    /* The remote does understand the qDefaultExecAndArgs, no executable
+       (and/or arguments) were set at the remote end.  If GDB wants the
+       remote to start an inferior it will need to provide this information.  */
+    UNSET,
+
+    /* The remote does understand the qDefaultExecAndArgs, an executable
+       and/or arguments were set at the remote end and this information is
+       held within this object.  */
+    SET
+  };
+
+  /* Create an empty instance, STATE should be state::UNKNOWN or
+     state::UNSET only.  */
+  remote_exec_and_args_info (state state = state::UNKNOWN)
+    : m_state (state)
+  {
+    gdb_assert (m_state != state::SET);
+  }
+
+  /* Create an instance in state::SET, move EXEC and ARGS into this
+     instance.  */
+  remote_exec_and_args_info (std::string &&exec, std::string &&args)
+    : m_state (state::SET),
+      m_exec (exec),
+      m_args (args)
+  { /* Nothing.  */ }
+
+  /* Is this object in state::SET?  */
+  bool is_set () const
+  {
+    return m_state == state::SET;
+  }
+
+  /* Return the argument string.  Only call when is_set returns true.  */
+  const std::string &args () const
+  {
+    gdb_assert (m_state == state::SET);
+    return m_args;
+  }
+
+  /* Return the executable string.  Only call when is_set returns true.  */
+  const std::string &exec () const
+  {
+    gdb_assert (m_state == state::SET);
+    return m_exec;
+  }
+
+private:
+  /* The state of this instance.  */
+  state m_state = state::UNKNOWN;
+
+  /* The executable path returned from the remote target.  */
+  std::string m_exec;
+
+  /* The argument string returned from the remote target.  */
+  std::string m_args;
+};
+
 class remote_target : public process_stratum_target
 {
 public:
@@ -1255,6 +1332,9 @@ class remote_target : public process_stratum_target
 
 private:
 
+  /* Fetch the executable filename and argument string from the remote.  */
+  remote_exec_and_args_info fetch_default_executable_and_arguments ();
+
   bool start_remote_1 (int from_tty, int extended_p);
 
   /* The remote state.  Don't reference this directly.  Use the
@@ -1707,7 +1787,7 @@ show_remote_exec_file (struct ui_file *file, int from_tty,
 {
   const std::string &filename = get_remote_exec_file ();
   if (filename.empty ())
-    gdb_printf (file, _("The remote exec-file is unset, the default remote "
+    gdb_printf (file, _("The remote exec-file is \"\", the default remote "
 			"executable will be used.\n"));
   else
     gdb_printf (file, "The remote exec-file is \"%s\".\n", filename.c_str ());
@@ -4913,6 +4993,55 @@ struct scoped_mark_target_starting
   scoped_restore_tmpl<bool> m_restore_starting_up;
 };
 
+/* See declaration in class above.   */
+
+remote_exec_and_args_info
+remote_target::fetch_default_executable_and_arguments ()
+{
+  if (m_features.packet_support (PACKET_qDefaultExecAndArgs) == PACKET_DISABLE)
+    return {};
+
+  struct remote_state *rs = get_remote_state ();
+
+  putpkt ("qDefaultExecAndArgs");
+  getpkt (&rs->buf, 0);
+
+  auto packet_result
+    = m_features.packet_ok (rs->buf, PACKET_qDefaultExecAndArgs);
+  if (packet_result == PACKET_UNKNOWN)
+    return {};
+
+  if (packet_result == PACKET_ERROR)
+    {
+      warning (_("Remote error: %s"), rs->buf.data ());
+      return {};
+    }
+
+  /* First character should be 'U', to indicate no information is set in
+     the server, or 'S' followed by the filename and arguments.  We treat
+     anything that is not a 'S' as if it were 'U'.  */
+  if (rs->buf[0] != 'S')
+    return { remote_exec_and_args_info::state::UNSET };
+
+  if (rs->buf[1] != ';')
+    {
+      warning (_("missing first ';' in qDefaultExecAndArgs reply"));
+      return { remote_exec_and_args_info::state::UNSET };
+    }
+
+  const char *p = &rs->buf[2];
+  const char *e = p;
+  for (; *e != ';' && *e != '\0'; ++e)
+    ;
+  std::string filename = hex2str (p, (e - p) / 2);
+
+  if (*e == ';')
+    ++e;
+  std::string args = hex2str (e, strlen (e) / 2);
+
+  return { std::move (filename), std::move (args) };
+}
+
 /* Helper for remote_target::start_remote, start the remote connection and
    sync state.  Return true if everything goes OK, otherwise, return false.
    This function exists so that the scoped_restore created within it will
@@ -4997,6 +5126,24 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
 	rs->noack_mode = 1;
     }
 
+  auto exec_and_args = fetch_default_executable_and_arguments ();
+
+  /* Update the inferior with the executable and argument string from the
+     target, these will be used when restarting the inferior, and also
+     allow the user to view this state.  */
+  if (exec_and_args.is_set ())
+    {
+      current_inferior ()->set_args (exec_and_args.args ());
+      const std::string &remote_exec = get_remote_exec_file ();
+      if (!remote_exec.empty () && remote_exec != exec_and_args.exec ())
+	warning (_("updating 'remote exec-file' to '%ps' to match "
+		   "remote target"),
+		 styled_string (file_name_style.style (),
+				exec_and_args.exec ().c_str ()));
+      set_pspace_remote_exec_file (current_program_space,
+				   exec_and_args.exec ());
+    }
+
   if (extended_p)
     {
       /* Tell the remote that we are using the extended protocol.  */
@@ -15410,6 +15557,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (PACKET_memory_tagging_feature,
 			 "memory-tagging-feature", "memory-tagging-feature", 0);
 
+  add_packet_config_cmd (PACKET_qDefaultExecAndArgs, "qDefaultExecAndArgs",
+			 "fetch-exec-and-args", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {
diff --git a/gdb/testsuite/gdb.server/fetch-exec-and-args.c b/gdb/testsuite/gdb.server/fetch-exec-and-args.c
new file mode 100644
index 00000000000..f48c8b4c6d3
--- /dev/null
+++ b/gdb/testsuite/gdb.server/fetch-exec-and-args.c
@@ -0,0 +1,34 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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/>.  */
+
+/* Simple test, do some work with the arguments so GDB has a chance to
+   break and check that the arguments are correct.  */
+
+volatile int global_counter;
+
+int
+main (int argc, char *argv[])
+{
+  int i;
+
+  global_counter = 0;		/* Break here.  */
+
+  for (i = 0; i < argc; ++i)
+    argv[i] = (char *) 0;
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.server/fetch-exec-and-args.exp b/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
new file mode 100644
index 00000000000..22c22d0c0b2
--- /dev/null
+++ b/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
@@ -0,0 +1,207 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2023 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 qDefaultExecAndArgs packet, specifically, the argument
+# fetching component of the packet.
+
+# Skip test if target does not support argument passing.
+require {!target_info exists noargs}
+
+load_lib gdbserver-support.exp
+
+standard_testfile .c
+
+require allow_gdbserver_tests
+
+if {[build_executable "failed to build" $binfile $srcfile]} {
+    return -1
+}
+
+# Used as an override for extended_gdbserver_load_last_file in
+# configs/native-extended-gdbserver.exp, prevents the remote exec-file
+# from being set.
+proc do_nothing {} { return 0 }
+
+# Check the 'show args' output.  If PACKET is 'on' then we expect to
+# see the arguments 'a b c', otherwise we don't expect to see any
+# arguments.
+proc check_show_args { packet } {
+    if { $packet } {
+	set args_re "a b c"
+    } else {
+	set args_re ""
+    }
+
+    gdb_test "show args" \
+	"Argument list to give program being debugged when it is started is \"$args_re\"\\."
+}
+
+# Check the 'show remote exec-file' output.  PACKET is either 'on' or
+# 'off' and reflects whether the qDefaultExecAndArgs packet is turned
+# on or off.  FILENAME is what we expect to see included in the
+# output, and is converted to a regexp by this function.
+proc check_remote_exec_file { packet filename } {
+    if { $filename eq "" } {
+	set remote_exec_re \
+	    "The remote exec-file is \"\", the default remote executable will be used\\."
+    } else {
+	set remote_exec_re \
+	    "The remote exec-file is \"[string_to_regexp $filename]\"\\."
+    }
+
+    gdb_test "show remote exec-file" $remote_exec_re
+}
+
+# Check the inferior has 4 arguments.  Arg 0 will be the program name,
+# while 1, 2, and 3 should be a, b, and c respectively.
+proc check_full_args {} {
+    set exec_filename ""
+    gdb_test "print argc" " = 4"
+    gdb_test_multiple "print argv\[0\]" "" {
+	-re -wrap " = $::hex \"(.*/${::testfile})\"" {
+	    set exec_filename $expect_out(1,string)
+	    pass $gdb_test_name
+	}
+    }
+    gdb_test "print argv\[1\]" " = $::hex \"a\""
+    gdb_test "print argv\[2\]" " = $::hex \"b\""
+    gdb_test "print argv\[3\]" " = $::hex \"c\""
+
+    return $exec_filename
+}
+
+# Check that GDB can fetch the arguments from the remote using the
+# qDefaultExecAndArgs packet.  When PACKET is 'on' we allow GDB to use
+# the packet, but when PACKET is 'off' we disable use of the
+# qDefaultExecAndArgs packet and ensure GDB falls back to the expected
+# behaviour.
+proc_with_prefix test_exec_and_arg_fetch { packet } {
+    clean_restart $::binfile
+
+    # Make sure we're disconnected, in case we're testing with an
+    # extended-remote board, therefore already connected.
+    gdb_test "disconnect" ".*"
+
+    gdb_test "set remote fetch-exec-and-args ${packet}" \
+	"Support for the 'qDefaultExecAndArgs' packet on future remote targets is set to \"${packet}\"\\."
+
+    gdbserver_run "a b c"
+
+    gdb_breakpoint [gdb_get_line_number "Break here"]
+    gdb_continue_to_breakpoint "run to breakpoint"
+
+    # Look in the inferior to check the arguments were passed
+    # correctly.  We get back the name of the executable the inferior
+    # is running.  If PACKET is 'on' then we expect GDB to have
+    # automatically fetched this executable name from the remote.
+    set exec_filename [check_full_args]
+    if { !$packet } {
+	set exec_filename ""
+    }
+
+    # Check 'show args' to ensure GDB sees the correct arguments.
+    check_show_args $packet
+
+    # Check 'show remote exec-file' to ensure GDB sees the correct
+    # filename.
+    check_remote_exec_file $packet $exec_filename
+
+    # Below this point we rely on restarting the inferior, which
+    # relies on the extended-remote protocol.
+    if {[target_info gdb_protocol] ne "extended-remote"} {
+	return
+    }
+
+    with_test_prefix "rerun" {
+	# Don't restart GDB, but re-run the inferior.
+	gdb_run_cmd
+	gdb_test "" \
+	    "Breakpoint $::decimal, main \\(\[^)\]+\\).*" \
+	    "rerun until breakpoint in main"
+
+	# If the packet is enabled then we expect the arguments to
+	# still be correct, otherwise, we should have defaulted back
+	# to no additional arguments.
+	if { $packet } {
+	    check_full_args
+	} else {
+	    gdb_test "print argc" " = 1"
+	}
+
+	# Check 'show args' to ensure GDB sees the correct arguments.
+	check_show_args ${packet}
+
+	# Check 'show remote exec-file' to ensure GDB sees the correct
+	# filename.
+	check_remote_exec_file $packet $exec_filename
+    }
+}
+
+# Start GDB and set 'remote exec-file' to some random file.  Then
+# start gdbserver with the name of the actual executable.  Connect to
+# gdbserver from GDB and check that GDB gives a warning about the
+# remove exec-file value having changed.
+proc_with_prefix test_remote_exec_warning {} {
+    clean_restart
+
+    gdb_test "disconnect" ".*"
+
+    # Set the file GDB is going to debug.  For extended-remote boards
+    # this also sets the remote exec-file.
+    gdb_file_cmd $::binfile
+
+    set invalid_remote_exec "/xxx/yyy/zzz"
+    gdb_test_no_output "set remote exec-file $invalid_remote_exec"
+    check_remote_exec_file on $invalid_remote_exec
+
+    # Start gdbserver.
+    set test "start gdbserver"
+    set target_exec [gdbserver_download_current_prog]
+    set target_exec_and_args "$target_exec a b c"
+    set catchres [catch {set res [gdbserver_start "" "$target_exec_and_args"]} errmsg]
+    if { $catchres != 0 } {
+	fail "$test: $errmsg"
+    } else {
+	pass "$test"
+    }
+
+    # And connect to gdbserver.  Check for the warning GDB emits when
+    # the remote exec-file is updated.
+    set gdbserver_protocol [lindex $res 0]
+    set gdbserver_gdbport [lindex $res 1]
+    set test "connect to gdbserver"
+    set extra_re "warning: updating 'remote exec-file' to '[string_to_regexp $target_exec]' to match remote target"
+    set res [gdb_target_cmd_ext $gdbserver_protocol $gdbserver_gdbport $extra_re]
+    if { $res == 0 } {
+	pass $test
+    } elseif { $res == 1 }  {
+	fail $test
+    } else {
+	unsupported $test
+    }
+}
+
+# This override prevents the remote exec-file from being set when
+# using the extended-remote protocol.  This is harmless when using
+# other boards.
+with_override extended_gdbserver_load_last_file do_nothing {
+    foreach_with_prefix packet { on off } {
+	test_exec_and_arg_fetch ${packet}
+    }
+
+    test_remote_exec_warning
+}
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index c57270175b4..e749194e039 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -2697,6 +2697,39 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       return;
     }
 
+  if (strcmp ("qDefaultExecAndArgs", own_buf) == 0)
+    {
+      if (program_path.get () == nullptr)
+	sprintf (own_buf, "U");
+      else
+	{
+	  std::string packet ("S;");
+
+	  packet += bin2hex ((const gdb_byte *) program_path.get (),
+			     strlen (program_path.get ()));
+	  packet += ";";
+
+	  std::string args;
+	  for (const char * arg : program_args)
+	    {
+	      if (!args.empty ())
+		args += " ";
+	      args += std::string (arg);
+	    }
+	  packet += bin2hex ((const gdb_byte *) args.c_str (), args.size ());
+
+	  if (packet.size () > PBUFSIZ)
+	    {
+	      sprintf (own_buf, "E.Program name and arguments too long.");
+	      return;
+	    }
+
+	  strcpy (own_buf, packet.c_str ());
+	  *new_packet_len_p = packet.size ();
+	}
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 05/10] gdb: detect when gdbserver has no default executable set
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (3 preceding siblings ...)
  2023-08-16 15:55 ` [PATCH 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
@ 2023-08-16 15:55 ` Andrew Burgess
  2023-08-16 15:55 ` [PATCH 06/10] gdb: make use of is_target_filename Andrew Burgess
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit extends the use of the new qDefaultExecAndArgs
packet (added in the previous commit) so that GDB now understands when
it is connected to a remote server that doesn't have a default
executable set.  We don't do much with this information right now,
other than produce more useful text for 'show remote exec-file'.

Here I've connected to a gdbserver with no default executable set:

  (gdb) show remote exec-file
  The remote exec-file is "", the remote has no default executable set.
  (gdb) file /tmp/hello.x
  Reading symbols from /tmp/hello.x...
  (gdb) run
  Starting program: /tmp/hello.x
  Running the default executable on the remote target failed; try "set remote exec-file"?
  (gdb)

I think that all makes sense.
---
 gdb/remote.c                                  | 120 ++++++++++++++----
 .../gdb.server/fetch-exec-and-args.exp        |  58 ++++++++-
 2 files changed, 150 insertions(+), 28 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 870153731b0..038980476c0 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -743,6 +743,12 @@ struct remote_exec_and_args_info
     return m_state == state::SET;
   }
 
+  /* Is this object in state::UNSET?  */
+  bool is_unset () const
+  {
+    return m_state == state::UNSET;
+  }
+
   /* Return the argument string.  Only call when is_set returns true.  */
   const std::string &args () const
   {
@@ -1419,8 +1425,58 @@ is_remote_target (process_stratum_target *target)
   return as_remote_target (target) != nullptr;
 }
 
+/* An enum used to track where the per-program-space remote exec-file data
+   came from.  This is useful when deciding which warnings to give to the
+   user.  */
+
+enum class remote_exec_source
+{
+  /* The remote exec-file has it's default (empty string) value, neither
+     the user, nor the remote target have tried to set the value yet.  */
+  DEFAULT_VALUE,
+
+  /* The remote exec-file value was set based on information fetched from
+     the remote target.  We warn the user if we are replacing a value they
+     supplied with one fetched from the remote target.  */
+  VALUE_FROM_REMOTE,
+
+  /* The remote exec-file value was set either directly by the user, or by
+     GDB after the inferior performed an exec.  */
+  VALUE_FROM_GDB,
+
+  /* The remote exec-file has it's default (empty string) value, but this
+     is because the user hasn't supplied a value yet, and the remote target
+     has specifically told GDB that it has no default executable available.  */
+  UNSET_VALUE,
+};
+
+/* Data held per program-space to represent the remote exec-file path.  The
+   first item in the pair is the exec-file path, this is set either by the
+   user with 'set remote exec-file', or automatically by GDB when
+   connecting to a remote target.
+
+   The second item in the pair is an enum flag that indicates where the
+   path value came from, or, when the path is the empty string, what this
+   actually means.  See show_remote_exec_file for details.  */
+using remote_exec_file_info = std::pair<std::string, remote_exec_source>;
+
 /* Per-program-space data key.  */
-static const registry<program_space>::key<std::string> remote_pspace_data;
+static const registry<program_space>::key<remote_exec_file_info>
+  remote_pspace_data;
+
+/* Retrieve the remote_exec_file_info object for PSPACE.  If no such object
+   yet exists then create a new one using the default constructor.  */
+
+static remote_exec_file_info &
+get_remote_exec_file_info (program_space *pspace)
+{
+  remote_exec_file_info *info = remote_pspace_data.get (pspace);
+  if (info == nullptr)
+    info = remote_pspace_data.emplace (pspace, "",
+				       remote_exec_source::DEFAULT_VALUE);
+  gdb_assert (info != nullptr);
+  return *info;
+}
 
 /* The size to align memory write packets, when practical.  The protocol
    does not guarantee any alignment, and gdb will generate short
@@ -1743,23 +1799,23 @@ remote_target::get_remote_state ()
 /* Fetch the remote exec-file from the current program space.  */
 
 static const std::string &
-get_remote_exec_file (void)
+get_remote_exec_file ()
 {
-  const std::string *remote_exec_file
-    = remote_pspace_data.get (current_program_space);
-  if (remote_exec_file == nullptr)
-    remote_exec_file = remote_pspace_data.emplace (current_program_space);
-  return *remote_exec_file;
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+  return info.first;
 }
 
 /* Set the remote exec file for PSPACE.  */
 
 static void
 set_pspace_remote_exec_file (struct program_space *pspace,
-			     const std::string &filename)
+			     const std::string &filename,
+			     remote_exec_source source)
 {
-  remote_pspace_data.clear (pspace);
-  remote_pspace_data.emplace (pspace, filename);
+  remote_exec_file_info &info = get_remote_exec_file_info (pspace);
+  info.first = filename;
+  info.second = source;
 }
 
 /* The "set remote exec-file" callback.  */
@@ -1767,8 +1823,8 @@ set_pspace_remote_exec_file (struct program_space *pspace,
 static void
 set_remote_exec_file_cb (const std::string &filename)
 {
-  set_pspace_remote_exec_file (current_program_space,
-			       filename);
+  set_pspace_remote_exec_file (current_program_space, filename,
+			       remote_exec_source::VALUE_FROM_GDB);
 }
 
 /* Get the value for the 'set remote exec-file' user setting.  */
@@ -1785,12 +1841,18 @@ static void
 show_remote_exec_file (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *cmd, const char *value)
 {
-  const std::string &filename = get_remote_exec_file ();
-  if (filename.empty ())
-    gdb_printf (file, _("The remote exec-file is \"\", the default remote "
-			"executable will be used.\n"));
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+
+  if (info.second == remote_exec_source::DEFAULT_VALUE)
+    gdb_printf (file, _("The remote exec-file is \"\", the default "
+			"remote executable will be used.\n"));
+  else if (info.second == remote_exec_source::UNSET_VALUE)
+    gdb_printf (file, _("The remote exec-file is \"\", the remote has "
+			"no default executable set.\n"));
   else
-    gdb_printf (file, "The remote exec-file is \"%s\".\n", filename.c_str ());
+    gdb_printf (file, _("The remote exec-file is \"%s\".\n"),
+		info.first.c_str ());
 }
 
 static int
@@ -5134,14 +5196,27 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
   if (exec_and_args.is_set ())
     {
       current_inferior ()->set_args (exec_and_args.args ());
-      const std::string &remote_exec = get_remote_exec_file ();
-      if (!remote_exec.empty () && remote_exec != exec_and_args.exec ())
+      remote_exec_file_info &info
+	= get_remote_exec_file_info (current_program_space);
+      if (info.second == remote_exec_source::VALUE_FROM_GDB
+	  && info.first != exec_and_args.exec ())
 	warning (_("updating 'remote exec-file' to '%ps' to match "
 		   "remote target"),
 		 styled_string (file_name_style.style (),
 				exec_and_args.exec ().c_str ()));
-      set_pspace_remote_exec_file (current_program_space,
-				   exec_and_args.exec ());
+      info.first = exec_and_args.exec ();
+      info.second = remote_exec_source::VALUE_FROM_REMOTE;
+    }
+  else if (exec_and_args.is_unset ())
+    {
+      remote_exec_file_info &info
+	= get_remote_exec_file_info (current_program_space);
+      if (info.second == remote_exec_source::DEFAULT_VALUE
+	  || info.second == remote_exec_source::VALUE_FROM_REMOTE)
+	{
+	  info.first.clear ();
+	  info.second = remote_exec_source::UNSET_VALUE;
+	}
     }
 
   if (extended_p)
@@ -6426,7 +6501,8 @@ remote_target::follow_exec (inferior *follow_inf, ptid_t ptid,
   if (is_target_filename (execd_pathname))
     execd_pathname += strlen (TARGET_SYSROOT_PREFIX);
 
-  set_pspace_remote_exec_file (follow_inf->pspace, execd_pathname);
+  set_pspace_remote_exec_file (follow_inf->pspace, execd_pathname,
+			       remote_exec_source::VALUE_FROM_GDB);
 }
 
 /* Same as remote_detach, but don't send the "D" packet; just disconnect.  */
diff --git a/gdb/testsuite/gdb.server/fetch-exec-and-args.exp b/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
index 22c22d0c0b2..93c8d23575b 100644
--- a/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
+++ b/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
@@ -56,8 +56,13 @@ proc check_show_args { packet } {
 # output, and is converted to a regexp by this function.
 proc check_remote_exec_file { packet filename } {
     if { $filename eq "" } {
-	set remote_exec_re \
-	    "The remote exec-file is \"\", the default remote executable will be used\\."
+	if { $packet } {
+	    set remote_exec_re \
+		"The remote exec-file is \"\", the remote has no default executable set\\."
+	} else {
+	    set remote_exec_re \
+		"The remote exec-file is \"\", the default remote executable will be used\\."
+	}
     } else {
 	set remote_exec_re \
 	    "The remote exec-file is \"[string_to_regexp $filename]\"\\."
@@ -195,13 +200,54 @@ proc_with_prefix test_remote_exec_warning {} {
     }
 }
 
+# Start GDB.  When PACKET is 'off' disable the qDefaultExecAndArgs
+# packet, otherwise, when PACKET is 'on' enable the packet.
+#
+# Start gdbserver in extended-remote mode, but don't provide a
+# filename when starting gdbserver.
+#
+# Connect to the remote server, and check 'show remote exec-file'.
+proc_with_prefix test_server_with_no_exec { packet set_remote_exec } {
+    clean_restart
+
+    gdb_test "disconnect" ".*"
+
+    gdb_file_cmd $::binfile
+
+    gdb_test "set remote fetch-exec-and-args ${packet}" \
+	"Support for the 'qDefaultExecAndArgs' packet on future remote targets is set to \"${packet}\"\\."
+
+    # Start gdbserver.
+    set target_exec [gdbserver_download_current_prog]
+
+    if { $set_remote_exec } {
+	gdb_test_no_output "set remote exec-file $target_exec" \
+	    "set remote exec-file"
+	set expected_filename $target_exec
+    } else {
+	set expected_filename ""
+    }
+
+    gdbserver_start_extended
+
+    check_remote_exec_file $packet $expected_filename
+}
+
 # This override prevents the remote exec-file from being set when
 # using the extended-remote protocol.  This is harmless when using
 # other boards.
 with_override extended_gdbserver_load_last_file do_nothing {
-    foreach_with_prefix packet { on off } {
-	test_exec_and_arg_fetch ${packet}
-    }
+    # This override stops GDB connecting to the gdbserver as soon as
+    # GDB is started when testing with the extended-remote protocol.
+    with_override gdbserver_start_multi do_nothing {
+	foreach_with_prefix packet { on off } {
+	    test_exec_and_arg_fetch ${packet}
+
+	    foreach_with_prefix set_remote_exec { true false } {
+		test_server_with_no_exec $packet $set_remote_exec
+	    }
+	}
 
-    test_remote_exec_warning
+	test_remote_exec_warning
+    }
 }
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 06/10] gdb: make use of is_target_filename
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (4 preceding siblings ...)
  2023-08-16 15:55 ` [PATCH 05/10] gdb: detect when gdbserver has no default executable set Andrew Burgess
@ 2023-08-16 15:55 ` Andrew Burgess
  2023-08-23 13:35   ` Mark Wielaard
  2023-08-16 15:55 ` [PATCH 07/10] gdb: add qMachineId packet Andrew Burgess
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Spotted a place where is_target_filename could be used, except that
is_target_filename takes 'const char *' and in the location I spotted
we have a std::string.  So I've added an overload for
is_target_filename that takes a std::string.

There should be no user visible change after this commit.
---
 gdb/build-id.c | 2 +-
 gdb/gdb_bfd.h  | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/gdb/build-id.c b/gdb/build-id.c
index f68384f0197..6abf04ffacd 100644
--- a/gdb/build-id.c
+++ b/gdb/build-id.c
@@ -90,7 +90,7 @@ build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len,
   /* lrealpath() is expensive even for the usually non-existent files.  */
   gdb::unique_xmalloc_ptr<char> filename_holder;
   const char *filename = nullptr;
-  if (startswith (link, TARGET_SYSROOT_PREFIX))
+  if (is_target_filename (link))
     filename = link.c_str ();
   else if (access (link.c_str (), F_OK) == 0)
     {
diff --git a/gdb/gdb_bfd.h b/gdb/gdb_bfd.h
index d15b1106d9a..5e9468dcdfd 100644
--- a/gdb/gdb_bfd.h
+++ b/gdb/gdb_bfd.h
@@ -44,6 +44,14 @@ struct registry_accessor<bfd>
 
 int is_target_filename (const char *name);
 
+/* Like the above, but for std::string.  */
+
+static inline int
+is_target_filename (const std::string &name)
+{
+  return is_target_filename (name.c_str ());
+}
+
 /* Returns nonzero if the filename associated with ABFD starts with
    TARGET_SYSROOT_PREFIX, zero otherwise.  */
 
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 07/10] gdb: add qMachineId packet
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (5 preceding siblings ...)
  2023-08-16 15:55 ` [PATCH 06/10] gdb: make use of is_target_filename Andrew Burgess
@ 2023-08-16 15:55 ` Andrew Burgess
  2023-08-16 16:34   ` Eli Zaretskii
                     ` (3 more replies)
  2023-08-16 15:55 ` [PATCH 08/10] gdb: remote filesystem can be local to GDB in some cases Andrew Burgess
                   ` (5 subsequent siblings)
  12 siblings, 4 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

In later commits I want to make two related changes to GDB, these are:

  1. Have GDB know when it can safely ignore a 'target:' prefix in the
  sysroot, and so avoid copying files from the remote target, and

  2. Have GDB know that it can safely use the file specified with the
  'file' command to start a remote inferior, rather than requiring the
  'set remote exec-file' command to have been used.

Both of these changes require that GDB be able to know if it is
running on the same host as the remote target.

In this commit I propose a mechanism by which this can be achieved,
that is, the introduction of the qMachineId packet.

The idea of the qMachineId packet is that, during the initial
connection phase, GDB will send the qMachineId packet, and the remote
will return a reply that describes the machine the remote target is
running on.

Back on the GDB side, GDB will generate a description of the machine
it is running on and compare this to the reply received from the
remote target.

If the two match then GDB will assume it is running on the same
machine as the remote target, and that it can access the same set of
files, in this case we can enable the two improvements listed above.

If the remote target doesn't support qMachineId, or the reply from the
remote target doesn't match the machine-id generated within GDB, then
GDB will assume that the target is truly remote, just as it does right
now.

This commit does NOT implement the two improvements listed above,
these will be added in follow on commits.  This commit just adds
support for the qMachineId packet.

Generating a suitable machine-id is, I think, always going to be
target specific.  As such, I've structured the code in a way that
allows different targets to provide their own implementations, but
I've only implemented a solution for the Linux targets.

The reply to a qMachineId packet looks like this:

  predicate;key=value[;key=value]*

the idea being that the reply consists of a number of key/value pairs,
each of which must match in order for GDB to consider the machine-id a
match.  I currently propose just two keys:

  linux-boot-id - this returns the value from the file
  /proc/sys/kernel/random/boot_id, which, if I understand correctly,
  should be unique(ish) for each boot of each machine, and

  cuserid - this returns the value of the cuserid call.

My thinking is that if we know we are on the same machine (thanks to
linux-boot-id), and we know we are the same effective user (thanks to
cuserid) then there's a pretty good chance that GDB and the remote can
access the same set of files.

As well as the 'predicate;' based reply, a remote can respond to a
qMachineId packet with one of these replies:

  local
  remote

The 'local' reply forces GDB to consider the remote as being local to
GDB.  I've documented this as something that should be used with
extreme care, obviously it would be easy for a user to run a remote
non-locally, in which case, if the remote claims to be local, then GDB
is going to try to access the files directly ... but maybe there will
be some use case where this is helpful.

The 'remote' reply is the opposite, it forces GDB to consider the
remote as being truly remote (which is the behaviour we have today).
It's always safe to return this reply, though this prevents GDB from
performing any of the improvements listed above.

For the GDB/gdbserver implementation, the code to generate the values
for the machine-id has been placed in gdb/nat/linux-machine-id.c, and
is shared between GDB and gdbserver.

There are no tests in this commit as there's no new commands, or user
visible behaviour changes (without turning on debug output), that can
be seen.  However, later commits will add new functionality, which
will rely on this packet working correctly.
---
 gdb/Makefile.in            |   3 +
 gdb/NEWS                   |   5 ++
 gdb/configure.nat          |   2 +-
 gdb/doc/gdb.texinfo        | 100 ++++++++++++++++++++++
 gdb/linux-nat.c            |  35 ++++++++
 gdb/nat/linux-machine-id.c |  60 +++++++++++++
 gdb/nat/linux-machine-id.h |  44 ++++++++++
 gdb/remote-machine-id.c    |  69 +++++++++++++++
 gdb/remote-machine-id.h    | 108 ++++++++++++++++++++++++
 gdb/remote.c               | 169 +++++++++++++++++++++++++++++++++++++
 gdbserver/Makefile.in      |   1 +
 gdbserver/configure.srv    |   2 +-
 gdbserver/linux-low.cc     |  19 +++++
 gdbserver/linux-low.h      |   2 +
 gdbserver/server.cc        |  14 +++
 gdbserver/target.cc        |   8 ++
 gdbserver/target.h         |   9 ++
 17 files changed, 648 insertions(+), 2 deletions(-)
 create mode 100644 gdb/nat/linux-machine-id.c
 create mode 100644 gdb/nat/linux-machine-id.h
 create mode 100644 gdb/remote-machine-id.c
 create mode 100644 gdb/remote-machine-id.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ccdd84beea6..f01ad9ab1a2 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1171,6 +1171,7 @@ COMMON_SFILES = \
 	reggroups.c \
 	remote.c \
 	remote-fileio.c \
+	remote-machine-id.c \
 	remote-notif.c \
 	reverse.c \
 	run-on-main-thread.c \
@@ -1448,6 +1449,7 @@ HFILES_NO_SRCDIR = \
 	regset.h \
 	remote.h \
 	remote-fileio.h \
+	remote-machine-id.h \
 	remote-notif.h \
 	riscv-fbsd-tdep.h \
 	riscv-ravenscar-thread.h \
@@ -1568,6 +1570,7 @@ HFILES_NO_SRCDIR = \
 	nat/gdb_thread_db.h \
 	nat/fork-inferior.h \
 	nat/linux-btrace.h \
+	nat/linux-machine-id.h \
 	nat/linux-namespaces.h \
 	nat/linux-nat.h \
 	nat/linux-osdata.h \
diff --git a/gdb/NEWS b/gdb/NEWS
index 9839330c46d..d83f097d937 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -272,6 +272,11 @@ qDefaultExecAndArgs
   which the server was started.  If no such information was given to
   the server then this is reflected in the reply.
 
+qMachineId
+  This packet returns an identifier that allows GDB to determine if
+  the remote server and GDB are running on the same host, and can see
+  the same filesystem.
+
 *** Changes in GDB 13
 
 * MI version 1 is deprecated, and will be removed in GDB 14.
diff --git a/gdb/configure.nat b/gdb/configure.nat
index aabcdeff989..92a8c4592bc 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -58,7 +58,7 @@ case ${gdb_host} in
 		proc-service.o \
 		linux-thread-db.o linux-nat.o nat/linux-osdata.o linux-fork.o \
 		nat/linux-procfs.o nat/linux-ptrace.o nat/linux-waitpid.o \
-		nat/linux-personality.o nat/linux-namespaces.o'
+		nat/linux-personality.o nat/linux-machine-id.o nat/linux-namespaces.o'
 	NAT_CDEPS='$(srcdir)/proc-service.list'
 	LOADLIBES='-ldl $(RDYNAMIC)'
 	;;
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8f8db2c9bfb..e66fffc1745 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -41666,6 +41666,7 @@
 * Traceframe Info Format::
 * Branch Trace Format::
 * Branch Trace Configuration Format::
+* Machine-Id Details::
 @end menu
 
 @node Overview
@@ -44748,6 +44749,49 @@
 Indicates an error was encountered.
 @end table
 
+@anchor{Machine-Id Packet}
+@item qMachineId
+@cindex query remote machine-id, remote request
+@cindex @samp{qMachineId} packet
+Access a remote @dfn{machine-id}.  The machine-id returned in response
+to this packet is compared to a machine-id created on the host the
+debugger is running on, if the two machine-ids match then the debugger
+will assume that the remote server and the debugger are running on the
+same machine, and can access the same files, the debugger will use
+this knowledge to avoid unnecessary copying of files from the remote
+(@pxref{File-I/O Remote Protocol Extension}).
+
+Reply:
+@table @samp
+@item predicate;@var{key}=@var{value}@r{[};@var{key}=@var{value}@r{]*}
+Returning a string starting with @samp{predicate;}, followed by one or
+more @var{key}=@var{value} pairs, defines a machine-id.  Each
+@var{key} and @var{value} is a non-empty string that must not contain
+the characters @samp{;} or @samp{=}.  Each @var{key} must be unique
+within a single reply.  See @ref{Machine-Id Details} for details of
+valid @var{key}s and their @var{value}s.
+
+@item remote
+Returning the string @samp{remote} indicates that the remote server
+should always be considered truly remote, and files the debugger needs
+to access should be first copied from the remote.
+
+@item local
+Returning the string @samp{local} indicates that the remote server
+should always be treated as running on the same host as the debugger.
+The debugger will avoid copying files from the remote server, and will
+instead try to access files directly.
+
+Sending this reply will rarely be appropriate, as it implies certainty
+about where the remote server and debugger are running, however, in
+some tightly controlled environments this might be appropriate.  Using
+a @samp{predicate} based reply would be better if at all possible.
+
+@item E @var{NN}
+@itemx E.errtext
+Indicates an error was encountered.
+@end table
+
 @item Qbtrace:bts
 Enable branch tracing for the current thread using Branch Trace Store.
 
@@ -47455,6 +47499,62 @@
 <!ATTLIST pt	size	CDATA	#IMPLIED>
 @end smallexample
 
+@node Machine-Id Details
+@section Machine-Id Details
+@cindex machine-id key-value pair details
+
+This section describes the valid @var{key}s and @var{values}s that can
+be returned in response to the @samp{qMachineId} packet
+(@pxref{Machine-Id Packet}), specifically, when using a
+@samp{predicate;} based reply.  Other reply types for the
+@samp{qMachineId} packet don't include @var{key}s and @var{value}s.
+
+There are two types of @var{key}, master keys and secondary keys.  A
+reply should contain exactly one master key, and zero or more
+secondary keys.  The set of valid secondary keys will depend on which
+master key is used.
+
+No @var{key} of @var{value} can contain the characters @samp{;} or
+@samp{=}.
+
+The order of the @var{key}/@var{value} pairs in the reply does not
+matter.
+
+Currently supported master and secondary keys are described below:
+
+@table @samp
+@item linux-boot-id
+The value for this master key contains the contents of the first line
+of the file @file{/proc/sys/kernel/random/boot_id} with any @samp{-}
+characters filtered out.
+
+@table @samp
+@item cuserid
+The value for this secondary key contains a username string associated
+with the effective user ID of the remote server process.
+@end table
+
+An example reply using @samp{linux-boot-id} and all secondary keys is:
+
+@smallexample
+predicate;linux-boot-id=28d154b3b1518383b3b4efcbd221fa7d;cuserid=username
+@end smallexample
+
+@end table
+
+When matching a machine-id @value{GDBN} first checks the reply for a
+master key that it understands.  If a suitable key is found
+@value{GDBN} checks that the value for the master key matches its
+value for the master key.  If the master key value matches, then
+@value{GDBN} checks all the remaining @var{key}/@var{value} pairs;
+each @var{key} must be known secondary key associated with the
+previously matched master key, and the secondary @var{value} must
+match @value{GDBN}'s computed value.
+
+If all @var{key}s are known, and their @var{value}s match, then
+@value{GDBN} considers the machine-id a match, otherwise, the
+machine-id is considered non-matching.
+
 @include agentexpr.texi
 
 @node Target Descriptions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 250a8f43282..58cd62f754e 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -69,6 +69,8 @@
 #include "gdbsupport/scope-exit.h"
 #include "gdbsupport/gdb-sigmask.h"
 #include "gdbsupport/common-debug.h"
+#include "remote-machine-id.h"
+#include "nat/linux-machine-id.h"
 #include <unordered_map>
 
 /* This comment documents high-level logic of this file.
@@ -4497,6 +4499,35 @@ current_lwp_ptid (void)
   return inferior_ptid;
 }
 
+struct linux_nat_machine_id_validation : public machine_id_validation
+{
+  linux_nat_machine_id_validation ()
+    : machine_id_validation ("linux-boot-id")
+  { /* Nothing.  */ }
+
+  bool check_master_key (const std::string &value) override
+  {
+    std::string boot_id = gdb_linux_machine_id_linux_boot_id ();
+    if (boot_id.empty ())
+      return false;
+    return boot_id == value;
+  }
+
+  bool check_secondary_key (const std::string &key,
+			    const std::string &value) override
+  {
+    if (key == "cuserid")
+      {
+	std::string username = gdb_linux_machine_cuserid ();
+	if (username.empty ())
+	  return false;
+	return username == value;
+      }
+
+    return false;
+  }
+};
+
 void _initialize_linux_nat ();
 void
 _initialize_linux_nat ()
@@ -4534,6 +4565,10 @@ Enables printf debugging output."),
   sigemptyset (&blocked_mask);
 
   lwp_lwpid_htab_create ();
+
+  std::unique_ptr<linux_nat_machine_id_validation> validation
+    (new linux_nat_machine_id_validation);
+  register_machine_id_validation (std::move (validation));
 }
 \f
 
diff --git a/gdb/nat/linux-machine-id.c b/gdb/nat/linux-machine-id.c
new file mode 100644
index 00000000000..eb401043260
--- /dev/null
+++ b/gdb/nat/linux-machine-id.c
@@ -0,0 +1,60 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "gdbsupport/common-defs.h"
+
+#include "nat/linux-machine-id.h"
+#include "safe-ctype.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* See nat/linux-machine-id.h.  */
+
+std::string
+gdb_linux_machine_id_linux_boot_id ()
+{
+  int fd = open ("/proc/sys/kernel/random/boot_id", O_RDONLY);
+  if (fd < 0)
+    return "";
+
+  std::string boot_id;
+  char buf;
+  while (read (fd, &buf, sizeof (buf)) == sizeof (buf))
+    {
+      if (ISXDIGIT (buf))
+	boot_id += buf;
+    }
+
+  close (fd);
+
+  return boot_id;
+}
+
+/* See nat/linux-machine-id.h.  */
+
+std::string
+gdb_linux_machine_cuserid ()
+{
+  char cuserid_str[L_cuserid];
+  char *res = cuserid (cuserid_str);
+  if (res == nullptr)
+    return "";
+
+  return std::string (cuserid_str);
+}
diff --git a/gdb/nat/linux-machine-id.h b/gdb/nat/linux-machine-id.h
new file mode 100644
index 00000000000..9cf63da0fd0
--- /dev/null
+++ b/gdb/nat/linux-machine-id.h
@@ -0,0 +1,44 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef NAT_LINUX_MACHINE_ID_H
+#define NAT_LINUX_MACHINE_ID_H
+
+#include <string>
+
+/* Return a string that contains the Linux boot-id, formatted for use in
+   the qMachineId packet.  If anything goes wrong then an empty string is
+   returned, otherwise a non-empty string is returned.
+
+   This is used by gdbserver when sending the reply to a qMachineId packet,
+   and used by GDB to check the value returned in for a qMachineId
+   packet.  */
+
+extern std::string gdb_linux_machine_id_linux_boot_id ();
+
+/* Return a string that contains the result of calling cuserid, that is, a
+   username associated with the effective user-id of the current process.
+   If anything goes wrong then an empty string is returned, otherwise a
+   non-empty string is returned.
+
+   This is used by gdbserver when sending the reply to a qMachineId packet,
+   and used by GDB to check the value returned in for a qMachineId
+   packet.  */
+
+extern std::string gdb_linux_machine_cuserid ();
+
+#endif /* NAT_LINUX_MACHINE_ID_H */
diff --git a/gdb/remote-machine-id.c b/gdb/remote-machine-id.c
new file mode 100644
index 00000000000..155bf5b3a0c
--- /dev/null
+++ b/gdb/remote-machine-id.c
@@ -0,0 +1,69 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "defs.h"
+#include "remote-machine-id.h"
+
+#include <string>
+#include <vector>
+
+/* List of all registered machine_id_validation objects.  */
+static std::vector<std::unique_ptr<machine_id_validation>> validation_list;
+
+/* See remote-machine-id.h.  */
+
+void
+register_machine_id_validation (std::unique_ptr<machine_id_validation> &&validation)
+{
+  validation_list.emplace_back (std::move (validation));
+}
+
+/* See remote-machine-id.  */
+
+bool
+validate_machine_id (const std::unordered_map<std::string, std::string> &kv_pairs)
+{
+  for (const auto &validator : validation_list)
+    {
+      const auto kv_master = kv_pairs.find (validator->master_key ());
+      if (kv_master == kv_pairs.end ())
+	continue;
+
+      if (!validator->check_master_key (kv_master->second))
+	continue;
+
+      /* Check all the secondary keys in KV_PAIRS.  */
+      bool match_failed = false;
+      for (const auto &kv : kv_pairs)
+	{
+	  if (kv.first == validator->master_key ())
+	    continue;
+
+	  if (!validator->check_secondary_key (kv.first, kv.second))
+	    {
+	      match_failed = true;
+	      break;
+	    }
+	}
+
+      if (!match_failed)
+	return true;
+    }
+
+  /* None of the machine_id_validation objects matched KV_PAIRS.  */
+  return false;
+}
diff --git a/gdb/remote-machine-id.h b/gdb/remote-machine-id.h
new file mode 100644
index 00000000000..8dd3b377591
--- /dev/null
+++ b/gdb/remote-machine-id.h
@@ -0,0 +1,108 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef REMOTE_MACHINE_ID_H
+#define REMOTE_MACHINE_ID_H
+
+#include <memory>
+#include <unordered_map>
+
+/* A base class from which machine-id validation objects can be created.
+   A remote target can send GDB a machine-id, which can be used to check
+   if the remote target and GDB are running on the same machine, and have
+   a common view of the file-system.  Knowing this allows GDB to optimise
+   some of its interactions with the remote target.
+
+   A machine-id consists of a set of key-value pairs, where both keys and
+   values are std::string objects.  A machine-id has a single master key
+   and some number of secondary keys.
+
+   Within GDB the native target will register one or more of these objects
+   by calling register_machine_id_validation.  When GDB receives a
+   machine-id from a remote-target each registered machine_id_validation
+   object will be checked in turn to see if it matches the machined-id.
+   If any machine_id_validation matches then this indicates that GDB and
+   the remote target are on the same machine.  */
+struct machine_id_validation
+{
+  /* Constructor.  MASTER_KEY is the name of the master key that this
+     machine_id_validation object validates for.  */
+  machine_id_validation (std::string &&master_key)
+    : m_master_key (master_key)
+  { /* Nothing.  */ }
+
+  /* Destructor.  */
+  virtual ~machine_id_validation ()
+  { /* Nothing.  */ }
+
+  /* Return a reference to the master key.  */
+  const std::string &
+  master_key () const
+  {
+    return m_master_key;
+  }
+
+  /* VALUE is a string passed from the remote target corresponding to the
+     key for master_key().  If the remote target didn't pass a key
+     matching master_key() then this function should not be called.
+
+     Return true if VALUE matches the value calculated for the host on
+     which GDB is currently running.  */
+  virtual bool
+  check_master_key (const std::string &value) = 0;
+
+  /* This function will only be called for a machine-id which contains a
+     key matching master_key(), and for which check_master_key() returned
+     true.
+
+     KEY and VALUE are a key-value pair passed from the remote target.
+     This function should return true if KEY is known, and VALUE matches
+     the value calculated for the host on which GDB is running.  If KEY is
+     not known, or VALUE doesn't match, then this function should return
+     false.  */
+  virtual bool
+  check_secondary_key (const std::string &key, const std::string &value) = 0;
+
+private:
+  /* The master key for which this object validates machine-ids.  */
+  std::string m_master_key;
+};
+
+/* Register a new machine-id.  */
+
+extern void register_machine_id_validation
+  (std::unique_ptr<machine_id_validation> &&validation);
+
+/* KV_PAIRS contains all machine-id obtained from the remote target, the
+   keys are the index into the map, and the values are the values of the
+   map.  These pairs are checked against all of the registered
+   machine_id_validation objects.
+
+   If any machine_id_validation matches all the data in KV_PAIRS then this
+   function returns true, otherwise, this function returns false.
+
+   For KV_PAIRS to match against a machine_id_validation object, KV_PAIRS
+   must contain a key matching machine_id_validation::master_key(), and the
+   value for that key must return true when passed to the function
+   machine_id_validation::check_master_key().  Then, for every other
+   key/value pair machine_id_validation::check_secondary_key() must return
+   true.  */
+
+extern bool validate_machine_id
+  (const std::unordered_map<std::string, std::string> &kv_pairs);
+
+#endif /* REMOTE_MACHINE_ID_H */
diff --git a/gdb/remote.c b/gdb/remote.c
index 038980476c0..7536333d823 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -80,6 +80,7 @@
 #include "async-event.h"
 #include "gdbsupport/selftest.h"
 #include "cli/cli-style.h"
+#include "remote-machine-id.h"
 
 /* The remote target.  */
 
@@ -306,6 +307,9 @@ enum {
   /* Support the qDefaultExecAndArgs packet.  */
   PACKET_qDefaultExecAndArgs,
 
+  /* Support the qMachineId packet.  */
+  PACKET_qMachineId,
+
   PACKET_MAX
 };
 
@@ -557,6 +561,15 @@ class remote_state
      this can go away.  */
   int wait_forever_enabled_p = 1;
 
+  /* Set to true if the remote target returned a machine-id (see
+     qMachineId packet) which matched one of the registered validation
+     objects.  This indicates that the remote target is running on the
+     same host as GDB (and can see the same filesystem as GDB.
+
+     Otherwise, this is false, which indicates the remote target should be
+     treated as truly remote.  */
+  bool remote_target_is_local_p = false;
+
 private:
   /* Mapping of remote protocol data for each gdbarch.  Usually there
      is only one entry here, though we may see more with stubs that
@@ -1341,6 +1354,12 @@ class remote_target : public process_stratum_target
   /* Fetch the executable filename and argument string from the remote.  */
   remote_exec_and_args_info fetch_default_executable_and_arguments ();
 
+  /* Send the qMachineId packet and process the reply.  Update the
+     remote_state::remote_target_is_local_p field based on the result.  We
+     assume that when this is called remote_target_is_local_p will be
+     false by default.  */
+  void fetch_remote_machine_id ();
+
   bool start_remote_1 (int from_tty, int extended_p);
 
   /* The remote state.  Don't reference this directly.  Use the
@@ -5055,6 +5074,151 @@ struct scoped_mark_target_starting
   scoped_restore_tmpl<bool> m_restore_starting_up;
 };
 
+/* Extract a machine-id key/value pair from the null-terminated string
+   **STRP, and update STRP to point to the first character after the parsed
+   key/value pair, including skipping any ';' that appears after the
+   key/value pair.
+
+   A key/value pair consists of two strings separated by an '=' character,
+   neither string will contain a '=' or ';' character.
+
+   Characters are read from *STRP until '=', ';' or the null character are
+   found, this forms the key string.  If ';' or null character were found
+   then the value string is empty.  Otherwise, '=' was found, the '=' is
+   skipped, and character are read until ';' or the null character are
+   found, this forms the value string.
+
+   This function will throw an error if the key string is found to be zero
+   length (e.g. '=abc' is invalid), or if the value string contains a '='
+   character (e.g. 'foo=def=ghi' is invalid).
+
+   The pair <key, value> is then returned.  */
+
+static
+std::pair<std::string, std::string> extract_kv_pair (const char **strp)
+{
+  gdb_assert (strp != nullptr);
+  gdb_assert (*strp != nullptr);
+  gdb_assert (**strp != '\0');
+
+  std::string key, value;
+  const char *str = *strp;
+  while (*str != '=' && *str != ';' && *str != '\0')
+    {
+      key += *str;
+      ++str;
+    }
+
+  if (key.empty ())
+    error (_("empty key while parsing '%s'"), *strp);
+
+  if (*str == '\0' || *str == ';')
+    {
+      if (*str == ';')
+	++str;
+      *strp = str;
+      return { key, "" };
+    }
+
+  gdb_assert (*str == '=');
+  ++str;
+
+  while (*str != ';' && *str != '\0')
+    {
+      if (*str == '=')
+	error (_("found '=' character in value string while parsing '%s'"),
+	       *strp);
+      value += *str;
+      ++str;
+    }
+
+  if (*str == ';')
+    ++str;
+  *strp = str;
+  return { key, value };
+}
+
+/* See declaration in class above.   */
+
+void
+remote_target::fetch_remote_machine_id ()
+{
+  struct remote_state *rs = get_remote_state ();
+
+  /* This should only be called for newly created remote_target objects, so
+     the remote_state::remote_target_is_local_p within the remote_target
+     should be false by default.  */
+  gdb_assert (!rs->remote_target_is_local_p);
+
+  if (m_features.packet_support (PACKET_qMachineId) == PACKET_DISABLE)
+    return;
+
+  putpkt ("qMachineId");
+  getpkt (&rs->buf, 0);
+
+  auto packet_result = m_features.packet_ok (rs->buf, PACKET_qMachineId);
+  if (packet_result == PACKET_UNKNOWN)
+    return;
+
+  if (packet_result == PACKET_ERROR)
+    {
+      warning (_("Remote error: %s"), rs->buf.data ());
+      return;
+    }
+
+  /* If the machine-id is the string 'remote' then we are done.  The
+     remote_target_is_local_p field is false by default.  */
+  const char *id = rs->buf.data ();
+  if (startswith (id, "remote") && (id[6] == ';' || id[6] == '\0'))
+    return;
+
+  /* If the machine-id is the string 'local' then the remote claims to
+     "know" that it is on the same machine as GDB.  Good luck with that.  */
+  if (startswith (id, "local") && (id[5] == ';' || id[5] == '\0'))
+    {
+      rs->remote_target_is_local_p = true;
+      return;
+    }
+
+  /* If the machine-id starts with the string 'predicate;', then
+     everything after that string is the part of the machine-id that we
+     need to match against to confirm we are on the same machine as the
+     remote target.  */
+  static const char *predicate_prefix = "predicate;";
+  if (!startswith (id, predicate_prefix))
+    return;
+  id += strlen (predicate_prefix);
+
+  /* Split the ID string into key/value pairs.  */
+  std::unordered_map<std::string, std::string> kv;
+  try
+    {
+      while (*id != '\0')
+	{
+	  auto kv_pair = extract_kv_pair (&id);
+	  kv.emplace (std::move (kv_pair.first), std::move (kv_pair.second));
+	}
+    }
+  catch (const gdb_exception &ex)
+    {
+      /* Let the user know something went wrong, and then return, treating
+	 the target as truly remote.  */
+      warning (_("Error parsing qMachineId packet: %s"), ex.what ());
+      return;
+    }
+
+  /* If there were no predicates, then this looks like a badly behaved
+     remote target, warn the user, and assume the target is remote.  */
+  if (kv.empty ())
+    {
+      warning (_("no machine-id predicates in qMachineId packet reply"));
+      return;
+    }
+
+  /* Check to see if the remote machine is actually local.  */
+  rs->remote_target_is_local_p = validate_machine_id (kv);
+}
+
 /* See declaration in class above.   */
 
 remote_exec_and_args_info
@@ -5188,6 +5352,8 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
 	rs->noack_mode = 1;
     }
 
+  fetch_remote_machine_id ();
+
   auto exec_and_args = fetch_default_executable_and_arguments ();
 
   /* Update the inferior with the executable and argument string from the
@@ -15636,6 +15802,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (PACKET_qDefaultExecAndArgs, "qDefaultExecAndArgs",
 			 "fetch-exec-and-args", 0);
 
+  add_packet_config_cmd (PACKET_qMachineId, "qMachineId",
+			 "fetch-machine-id", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 39cb9e7a151..c746a950bed 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -220,6 +220,7 @@ SFILES = \
 	$(srcdir)/../gdb/nat/aarch64-mte-linux-ptrace.c \
 	$(srcdir)/../gdb/nat/aarch64-sve-linux-ptrace.c \
 	$(srcdir)/../gdb/nat/linux-btrace.c \
+	$(srcdir)/../gdb/nat/linux-machine-id.c \
 	$(srcdir)/../gdb/nat/linux-namespaces.c \
 	$(srcdir)/../gdb/nat/linux-osdata.c \
 	$(srcdir)/../gdb/nat/linux-personality.c \
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index f0101994529..838d446d53e 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -26,7 +26,7 @@ ipa_ppc_linux_regobj="powerpc-32l-ipa.o powerpc-altivec32l-ipa.o powerpc-vsx32l-
 
 # Linux object files.  This is so we don't have to repeat
 # these files over and over again.
-srv_linux_obj="linux-low.o nat/linux-osdata.o nat/linux-procfs.o nat/linux-ptrace.o nat/linux-waitpid.o nat/linux-personality.o nat/linux-namespaces.o fork-child.o nat/fork-inferior.o"
+srv_linux_obj="linux-low.o nat/linux-osdata.o nat/linux-procfs.o nat/linux-ptrace.o nat/linux-waitpid.o nat/linux-personality.o nat/linux-machine-id.o nat/linux-namespaces.o fork-child.o nat/fork-inferior.o"
 
 # Input is taken from the "${host}" and "${target}" variables.
 
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index e1806ade82f..5fce0b87fac 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -61,6 +61,7 @@
 #include <elf.h>
 #endif
 #include "nat/linux-namespaces.h"
+#include "nat/linux-machine-id.h"
 
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
@@ -6940,6 +6941,24 @@ linux_process_target::thread_pending_child (thread_info *thread)
   return get_lwp_thread (child);
 }
 
+/* See target.h.  */
+
+std::string
+linux_process_target::get_machine_id () const
+{
+  std::string boot_id = gdb_linux_machine_id_linux_boot_id ();
+  if (boot_id.empty ())
+    return "";
+  boot_id = "linux-boot-id=" + boot_id;
+
+  std::string username = gdb_linux_machine_cuserid ();
+  if (username.empty ())
+    return "";
+  username = "cuserid=" + username;
+
+  return boot_id + ";" + username;
+}
+
 /* Default implementation of linux_target_ops method "set_pc" for
    32-bit pc register which is literally named "pc".  */
 
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index 6dc93197f5c..1728370d1f2 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -317,6 +317,8 @@ class linux_process_target : public process_stratum_target
 
   bool supports_catch_syscall () override;
 
+  std::string get_machine_id () const override;
+
   /* Return the information to access registers.  This has public
      visibility because proc-service uses it.  */
   virtual const regs_info *get_regs_info () = 0;
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index e749194e039..ae40e885e70 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -51,6 +51,8 @@
 #include "gdbsupport/scoped_restore.h"
 #include "gdbsupport/search.h"
 
+#include <systemd/sd-id128.h>
+
 /* PBUFSIZ must also be at least as big as IPA_CMD_BUF_SIZE, because
    the client state data is passed directly to some agent
    functions.  */
@@ -2730,6 +2732,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       return;
     }
 
+  if (strcmp ("qMachineId", own_buf) == 0)
+    {
+      std::string machine_id = the_target->get_machine_id ();
+      if (!machine_id.empty ())
+	machine_id = std::string ("predicate;") + machine_id;
+      else
+	machine_id = std::string ("remote");
+
+      strcpy (own_buf, machine_id.c_str ());
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index f8e592d20c3..2d43dfbe8de 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -442,6 +442,14 @@ process_stratum_target::store_memtags (CORE_ADDR address, size_t len,
   gdb_assert_not_reached ("target op store_memtags not supported");
 }
 
+/* See target.h.  */
+
+std::string
+process_stratum_target::get_machine_id () const
+{
+  return "";
+}
+
 int
 process_stratum_target::read_offsets (CORE_ADDR *text, CORE_ADDR *data)
 {
diff --git a/gdbserver/target.h b/gdbserver/target.h
index d993e361b76..092a6d9d3df 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -508,6 +508,15 @@ class process_stratum_target
      Returns true if successful and false otherwise.  */
   virtual bool store_memtags (CORE_ADDR address, size_t len,
 			      const gdb::byte_vector &tags, int type);
+
+  /* Return a string representing a machine-id suitable for returning
+     within a qMachineId packet response, but don't include the
+     'predicate;' prefix.
+
+     If the current target doesn't support machine-id, or if we fail to
+     build the machine-id for any reason, then return an empty string, the
+     server will send back a suitable reply to the debugger.  */
+  virtual std::string get_machine_id () const;
 };
 
 extern process_stratum_target *the_target;
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 08/10] gdb: remote filesystem can be local to GDB in some cases
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (6 preceding siblings ...)
  2023-08-16 15:55 ` [PATCH 07/10] gdb: add qMachineId packet Andrew Burgess
@ 2023-08-16 15:55 ` Andrew Burgess
  2023-08-16 16:40   ` Eli Zaretskii
  2023-08-16 15:55 ` [PATCH 09/10] gdb: use exec_file with remote targets when possible Andrew Burgess
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit makes use of the qMachineId packet (added in the previous
commit) to detect when a remote target is actually running on the same
host as GDB.

If GDB detects that the remote target is running on the same host as
GDB then GDB is able to avoid fetching files from the remote, and can
instead, access the files directly, which is much quicker.

There is a new setting to control this behaviour:

  set remote local-filesystem auto|on|off
  show remote local-filesystem

By default this setting is 'auto', in which case GDB will use the
result of the qMachineId packet to choose between acting like 'on' or
'off', but the user can force this on (maybe for targets that don't
support qMachineId?), or can force it off if this feature is causing
problems.
---
 gdb/NEWS                                     |  25 ++++
 gdb/doc/gdb.texinfo                          |  30 ++++
 gdb/remote.c                                 | 148 ++++++++++++++-----
 gdb/testsuite/gdb.server/server-local-fs.c   |  22 +++
 gdb/testsuite/gdb.server/server-local-fs.exp | 138 +++++++++++++++++
 gdb/testsuite/gdb.server/sysroot.exp         |  90 ++++++-----
 6 files changed, 371 insertions(+), 82 deletions(-)
 create mode 100644 gdb/testsuite/gdb.server/server-local-fs.c
 create mode 100644 gdb/testsuite/gdb.server/server-local-fs.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index d83f097d937..99ed057c412 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -106,6 +106,13 @@
   subsequent runs of the inferior will use the same arguments as the
   first run.
 
+* For remote targets that support the qMachineId packet, if GDB
+  believes that the remote target is on the same host as GDB itself,
+  and that GDB can access the same files as the remote target, then
+  GDB will take advantage of this to access files directly, rather
+  than copying them from the remote target.  This behaviour can be
+  disable using the new command 'set remote local-filesystem off'.
+
 * New commands
 
 set debug breakpoint on|off
@@ -133,6 +140,24 @@ show always-read-ctf
 info main
   Get main symbol to identify entry point into program.
 
+set remote local-filesystem on|off|auto
+show remote local-filesystem
+  When 'on' GDB will assume that its local filesystem is the same
+  filesystem as the remote target, this effectively means GDB will
+  ignore any 'target:' prefix in the sysroot setting.  When 'off' GDB
+  will use the sysroot value to determine if a path is remote or not;
+  a sysroot starting 'target:' indicates that paths should be treated
+  as remote.
+
+  The default value for this setting is 'auto', in this mode GDB will
+  make use of the qMachineId packet to determine if the remote target
+  is on the same host as GDB or not.  For remote targets that don't
+  support qMachineId, or in cases where the qMachineId indicates that
+  the remote target is truly remote, GDB will behave as if this
+  setting is 'off'.  Only when qMachineId is supported, and qMachineId
+  indicates the remote target is on the same host as GDB, will GDB
+  treat this setting as 'on'.
+
 * New convenience function "$_shell", to execute a shell command and
   return the result.  This lets you run shell commands in expressions.
   Some examples:
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e66fffc1745..f135d430403 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21892,6 +21892,7 @@
 @cindex system root, alternate
 @kindex set solib-absolute-prefix
 @kindex set sysroot
+@anchor{set sysroot}
 @item set sysroot @var{path}
 Use @var{path} as the system root for the program being debugged.  Any
 absolute shared library paths will be prefixed with @var{path}; many
@@ -21920,6 +21921,15 @@
 named @file{target:} or @file{remote:}, you need to use some
 equivalent variant of the name like @file{./target:}.
 
+If @value{GDBN} is connected to a remote target, but the remote target
+is running on the same host as @value{GDBN}, then @value{GDBN} is able
+to avoid fetching files from the remote target, effectively ignoring
+the @file{target:} prefix in the sysroot.  @value{GDBN} uses the
+@code{qMachineId} packet (@pxref{Machine-Id Packet}) to determine if a
+remote target and @value{GDBN} are running on the same host or not.
+This behaviour can be controlled using @code{set remote
+local-filesystem} (@pxref{set remote local-filesystem}).
+
 For targets with an MS-DOS based filesystem, such as MS-Windows,
 @value{GDBN} tries prefixing a few variants of the target
 absolute file name with @var{path}.  But first, on Unix hosts,
@@ -23967,6 +23977,26 @@
 target system.  If it is not set, the target will use a default
 filename (e.g.@: the last program run).
 
+@anchor{set remote local-filesystem}
+@item set remote local-filesystem @r{[}auto@r{|}on@r{|}off@r{]}
+@itemx show remote local-filesystem
+@cindex filesystem, for remote target
+When set to @samp{on}, @value{GDBN} will ignore any @file{target:}
+prefix in the sysroot setting (@pxref{set sysroot,,Setting the
+sysroot}) when accessing files on a remote target.  The result of this
+is that @value{GDBN} will act as though @value{GDBN} and the remote
+target are sharing a filesystem.
+
+When set to @samp{off}, @value{GDBN} treats a @file{target:} prefix in
+the usual way; files will be copied from the remote target before
+being read.
+
+When set to @samp{auto}, which is the default, @value{GDBN} will use
+the @code{qMachineId} packet (@pxref{Machine-Id Packet}) to determine
+if the remote target and @value{GDBN} are running on the same host,
+and can access the same filesystem, in which case, this setting act as
+if @samp{on}.  Otherwise, this setting acts as if @samp{off}.
+
 @item set remote interrupt-sequence
 @cindex interrupt remote programs
 @cindex select Ctrl-C, BREAK or BREAK-g
diff --git a/gdb/remote.c b/gdb/remote.c
index 7536333d823..1d5e098e91f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1444,6 +1444,52 @@ is_remote_target (process_stratum_target *target)
   return as_remote_target (target) != nullptr;
 }
 
+/* Get a pointer to the current remote target.  If not connected to a
+   remote target, return NULL.  */
+
+static remote_target *
+get_current_remote_target ()
+{
+  target_ops *proc_target = current_inferior ()->process_target ();
+  return dynamic_cast<remote_target *> (proc_target);
+}
+
+/* Should GDB assume that the remote target is on the same local
+   filesystem?  For example, when starting a target using the '|'
+   notation, the target will be on the local machine.
+
+   When this is set to auto GDB will try to figure this out itself, while
+   setting this to true forces GDB to assume the remote is actually
+   local.  */
+
+static enum auto_boolean remote_filesystem_is_local = AUTO_BOOLEAN_AUTO;
+
+/* Implement 'show remote local-filesystem'.  */
+
+static void
+show_remote_local_filesystem (struct ui_file *file, int from_tty,
+			      struct cmd_list_element *cmd,
+			      const char *value)
+{
+  if (remote_filesystem_is_local == AUTO_BOOLEAN_AUTO)
+    {
+      remote_target *remote = get_current_remote_target ();
+
+      if (remote == nullptr)
+	gdb_printf (file, _("Whether the remote filesystem is local to GDB "
+			    "is \"auto\" (current target is not remote).\n"));
+      else if (target_filesystem_is_local ())
+	gdb_printf (file, _("Whether the remote filesystem is local to GDB "
+			    "is \"auto\" (currently \"on\").\n"));
+      else
+	gdb_printf (file, _("Whether the remote filesystem is local to GDB "
+			    "is \"auto\" (currently \"off\").\n"));
+    }
+  else
+    gdb_printf (file, _("Whether the remote filesystem is local to GDB "
+			"is \"%s\".\n"), value);
+}
+
 /* An enum used to track where the per-program-space remote exec-file data
    came from.  This is useful when deciding which warnings to give to the
    user.  */
@@ -1973,16 +2019,6 @@ remote_arch_state::remote_arch_state (struct gdbarch *gdbarch)
     this->remote_packet_size = (this->sizeof_g_packet * 2 + 32);
 }
 
-/* Get a pointer to the current remote target.  If not connected to a
-   remote target, return NULL.  */
-
-static remote_target *
-get_current_remote_target ()
-{
-  target_ops *proc_target = current_inferior ()->process_target ();
-  return dynamic_cast<remote_target *> (proc_target);
-}
-
 /* Return the current allowed size of a remote packet.  This is
    inferred from the current architecture, and should be used to
    limit the length of outgoing packets.  */
@@ -13212,49 +13248,60 @@ remote_target::fileio_fstat (int fd, struct stat *st, fileio_error *remote_errno
 bool
 remote_target::filesystem_is_local ()
 {
-  /* Valgrind GDB presents itself as a remote target but works
-     on the local filesystem: it does not implement remote get
-     and users are not expected to set a sysroot.  To handle
-     this case we treat the remote filesystem as local if the
-     sysroot is exactly TARGET_SYSROOT_PREFIX and if the stub
-     does not support vFile:open.  */
-  if (gdb_sysroot == TARGET_SYSROOT_PREFIX)
+  if (remote_filesystem_is_local == AUTO_BOOLEAN_AUTO)
     {
-      packet_support ps = m_features.packet_support (PACKET_vFile_open);
+      /* If the remote appears to be on the same local machine then assume
+	 the filesystem is local.  */
+      struct remote_state *rs = get_remote_state ();
+      if (rs->remote_target_is_local_p)
+	return true;
 
-      if (ps == PACKET_SUPPORT_UNKNOWN)
+      /* Valgrind GDB presents itself as a remote target but works
+	 on the local filesystem: it does not implement remote get
+	 and users are not expected to set a sysroot.  To handle
+	 this case we treat the remote filesystem as local if the
+	 sysroot is exactly TARGET_SYSROOT_PREFIX and if the stub
+	 does not support vFile:open.  */
+      if (gdb_sysroot == TARGET_SYSROOT_PREFIX)
 	{
-	  int fd;
-	  fileio_error remote_errno;
+	  packet_support ps = m_features.packet_support (PACKET_vFile_open);
 
-	  /* Try opening a file to probe support.  The supplied
-	     filename is irrelevant, we only care about whether
-	     the stub recognizes the packet or not.  */
-	  fd = remote_hostio_open (NULL, "just probing",
-				   FILEIO_O_RDONLY, 0700, 0,
-				   &remote_errno);
+	  if (ps == PACKET_SUPPORT_UNKNOWN)
+	    {
+	      int fd;
+	      fileio_error remote_errno;
 
-	  if (fd >= 0)
-	    remote_hostio_close (fd, &remote_errno);
+	      /* Try opening a file to probe support.  The supplied
+		 filename is irrelevant, we only care about whether
+		 the stub recognizes the packet or not.  */
+	      fd = remote_hostio_open (NULL, "just probing",
+				       FILEIO_O_RDONLY, 0700, 0,
+				       &remote_errno);
 
-	  ps = m_features.packet_support (PACKET_vFile_open);
-	}
+	      if (fd >= 0)
+		remote_hostio_close (fd, &remote_errno);
 
-      if (ps == PACKET_DISABLE)
-	{
-	  static int warning_issued = 0;
+	      ps = m_features.packet_support (PACKET_vFile_open);
+	    }
 
-	  if (!warning_issued)
+	  if (ps == PACKET_DISABLE)
 	    {
-	      warning (_("remote target does not support file"
-			 " transfer, attempting to access files"
-			 " from local filesystem."));
-	      warning_issued = 1;
-	    }
+	      static int warning_issued = 0;
 
-	  return true;
+	      if (!warning_issued)
+		{
+		  warning (_("remote target does not support file"
+			     " transfer, attempting to access files"
+			     " from local filesystem."));
+		  warning_issued = 1;
+		}
+
+	      return true;
+	    }
 	}
     }
+  else if (remote_filesystem_is_local == AUTO_BOOLEAN_TRUE)
+    return true;
 
   return false;
 }
@@ -15887,6 +15934,25 @@ this setting is not used."),
 				   &remote_set_cmdlist,
 				   &remote_show_cmdlist);
 
+  add_setshow_auto_boolean_cmd ("local-filesystem", class_files,
+				&remote_filesystem_is_local, _("\
+Set whether the remote's filesystem is local to GDB."), _("\
+Show whether the remote's filesystem is local to GDB."), _("\
+When 'on' GDB assumes that the remote target's filesystem is the same\n\
+local filesystem as GDB sees.  GDB can avoid transferring files over\n\
+the remote protocol, and will instead access the files directly.\n\
+When 'off' GDB will always fetch files using the remote protocol,\n\
+e.g. when an inferior loads a library, GDB will read the libraries\n\
+debug information using the remote protocol, which is slower than\n\
+accessing the library directly.\n\
+\n\
+The default for the setting is 'auto', in which case GDB will try to\n\
+detect when the remote target is running on the same host as GDB."),
+				nullptr,
+				show_remote_local_filesystem,
+				&remote_set_cmdlist,
+				&remote_show_cmdlist);
+
   add_setshow_boolean_cmd ("range-stepping", class_run,
 			   &use_range_stepping, _("\
 Enable or disable range stepping."), _("\
diff --git a/gdb/testsuite/gdb.server/server-local-fs.c b/gdb/testsuite/gdb.server/server-local-fs.c
new file mode 100644
index 00000000000..439a0927f73
--- /dev/null
+++ b/gdb/testsuite/gdb.server/server-local-fs.c
@@ -0,0 +1,22 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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
+main (void)
+{
+  return 0;	/* Break here.  */
+}
diff --git a/gdb/testsuite/gdb.server/server-local-fs.exp b/gdb/testsuite/gdb.server/server-local-fs.exp
new file mode 100644
index 00000000000..b830c06d033
--- /dev/null
+++ b/gdb/testsuite/gdb.server/server-local-fs.exp
@@ -0,0 +1,138 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2023 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/>.
+
+# Check that GDB can detect when the remote target and GDB running on
+# the same host and can automatically choose to ignore the 'target:'
+# prefix in the sysroot.
+
+load_lib gdbserver-support.exp
+
+standard_testfile
+
+require allow_gdbserver_tests
+
+# This test requires that GDB and the remote be on the same host.
+require {!is_remote host}
+
+if { [build_executable "failed to prepare" ${testfile}] } {
+    return -1
+}
+
+# Call after connecting to a remote target, return true if the
+# GDB/GDBserver combo supports the qMachineId packet, otherwise,
+# return false.
+
+proc supports_qMachineId {} {
+    gdb_test_multiple "show remote fetch-machine-id-packet" "" {
+	-re -wrap "Support for the 'qMachineId' packet on the current remote target is \"auto\", currently disabled\\." {
+	    return false
+	}
+	-re -wrap "Support for the 'qMachineId' packet on the current remote target is \"off\"\\." {
+	    return false
+	}
+	-re -wrap "Support for the 'qMachineId' packet on the current remote target is \"auto\", currently enabled\\." {
+	    return true
+	}
+	-re -wrap "Support for the 'qMachineId' packet on the current remote target is \"on\"\\." {
+	    return true
+	}
+    }
+}
+
+clean_restart $binfile
+
+# This test is all about ignoring the 'target:' prefix in the sysroot.
+# If the test is being run with some board that sets a specific
+# sysroot value then this test might now work, so just don't try.
+gdb_test_multiple "show sysroot" "" {
+    -re -wrap "The current system root is \"target:\"\\." {
+	pass $gdb_test_name
+    }
+    -re -wrap "The current system root is \"\"\\." {
+	gdb_test_no_output "set sysroot target:"
+	send_gdb "show sysroot\n"
+	exp_continue
+    }
+    -re -wrap "" {
+	# Any other sysroot setting is not going to work with this
+	# test.
+	unsupport "unsupported sysroot setting"
+	return
+    }
+}
+
+# Start a gdbserver and connect to it from GDB.  Check to see if GDB
+# downloads any files from the remote target.
+#
+# FS_MODE is used to set 'remote local-filesystem'.
+#
+# Depending on whether the target is remote or local, and the value of
+# FS_MODE, this test might, or might not, expect to see files being
+# downloaded.
+proc test_for_remote_read { fs_mode } {
+
+    clean_restart $::binfile
+
+    # We already checked that the sysroot setting either is already
+    # 'target:', or is '', but can be set to 'target:', so at this
+    # point we just force the sysroot setting.
+    gdb_test_no_output "set sysroot target:"
+
+    # Setup whether GDB thinks the remote filesystem is local or not.
+    gdb_test_no_output "set remote local-filesystem $fs_mode"
+
+    # Disconnect in case we are using extended-remote and are already
+    # connected.
+    gdb_test "disconnect" ".*"
+
+    # Start the gdbserver.
+    set res [gdbserver_spawn ""]
+    set gdbserver_protocol [lindex $res 0]
+    set gdbserver_gdbport [lindex $res 1]
+
+    # Connect to gdbserver.
+    gdb_test_multiple "target $gdbserver_protocol $gdbserver_gdbport" \
+	"connect to gdbserver" {
+	-re -wrap "Reading \[^\r\n\]+ from remote target\.\.\.\r\n.*" {
+	    if {$fs_mode eq "off"
+		|| [is_remote target]
+		|| ![supports_qMachineId]} {
+		pass $gdb_test_name
+	    } else {
+		fail $gdb_test_name
+	    }
+	}
+	-re -wrap "" {
+	    if {![is_remote target]
+		&& $fs_mode ne "off"} {
+		pass $gdb_test_name
+	    } else {
+		fail $gdb_test_name
+	    }
+	}
+    }
+}
+
+foreach_with_prefix fs_mode { auto on off } {
+    if {$fs_mode eq "on" && [is_remote target]} {
+	# It doesn't make sense to force GDB to try and use the local
+	# filesystem when the target is actually remote.
+	continue
+    }
+
+    test_for_remote_read $fs_mode
+}
diff --git a/gdb/testsuite/gdb.server/sysroot.exp b/gdb/testsuite/gdb.server/sysroot.exp
index 35f15d6c60e..7662787f705 100644
--- a/gdb/testsuite/gdb.server/sysroot.exp
+++ b/gdb/testsuite/gdb.server/sysroot.exp
@@ -42,53 +42,61 @@ if { ( ![is_remote host] && ![is_remote target ] )
 }
 lappend modes "remote"
 
-foreach_with_prefix sysroot $modes {
-    global srcdir
-    global subdir
-    global binfile
-
-    if { $sysroot == "local" } {
-	set sysroot_command "/"
-	set reading_symbols "Reading symbols from $host_binfile..."
-	set timeout_factor 1
-    } else {
-	set sysroot_command "target:"
-	set reading_symbols "Reading .*$target_binfile from remote target..."
-	set timeout_factor 5
-    }
+foreach_with_prefix fs_mode $modes {
+    foreach_with_prefix sysroot $modes {
+	global srcdir
+	global subdir
+	global binfile
 
-    # Reading debug info from the remote target can take a bit of time, so
-    # increase the timeout in that case.
-    with_timeout_factor $timeout_factor {
-	# Restart GDB.
-	clean_restart
+	if { $sysroot == "local" || $fs_mode == "local" } {
+	    set sysroot_command "/"
+	    set reading_symbols "Reading symbols from $host_binfile..."
+	    set timeout_factor 1
+	} else {
+	    set sysroot_command "target:"
+	    set reading_symbols "Reading .*$target_binfile from remote target..."
+	    set timeout_factor 5
+	}
 
-	# Make sure we're disconnected, in case we're testing with an
-	# extended-remote board, therefore already connected.
-	gdb_test "disconnect" ".*"
+	# Reading debug info from the remote target can take a bit of time, so
+	# increase the timeout in that case.
+	with_timeout_factor $timeout_factor {
+	    # Restart GDB.
+	    clean_restart
 
-	# Start GDBserver.
-	set res [gdbserver_start "" $target_binfile]
-	set gdbserver_protocol [lindex $res 0]
-	set gdbserver_gdbport [lindex $res 1]
+	    if { $fs_mode == "local" } {
+		gdb_test_no_output "set remote local-filesystem on"
+	    } else {
+		gdb_test_no_output "set remote local-filesystem off"
+	    }
 
-	# Set the sysroot.
-	gdb_test_no_output "set sysroot $sysroot_command"
+	    # Make sure we're disconnected, in case we're testing with an
+	    # extended-remote board, therefore already connected.
+	    gdb_test "disconnect" ".*"
 
-	# Connect to gdbserver, making sure GDB reads in the binary correctly.
-	set test "connect to remote and read binary"
-	if {[gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport $reading_symbols] == 0} {
-	    pass $test
-	} else {
-	    fail $test
-	}
+	    # Start GDBserver.
+	    set res [gdbserver_start "" $target_binfile]
+	    set gdbserver_protocol [lindex $res 0]
+	    set gdbserver_gdbport [lindex $res 1]
+
+	    # Set the sysroot.
+	    gdb_test_no_output "set sysroot $sysroot_command"
 
-	gdb_breakpoint main
-	gdb_test "continue" "Breakpoint $decimal.* main.*" "continue to main"
+	    # Connect to gdbserver, making sure GDB reads in the binary correctly.
+	    set test "connect to remote and read binary"
+	    if {[gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport $reading_symbols] == 0} {
+		pass $test
+	    } else {
+		fail $test
+	    }
 
-	# Test that we can stop inside a library.
-	gdb_breakpoint printf
-	gdb_test "continue" "Breakpoint $decimal.* (__)?printf.*" \
-	    "continue to printf"
+	    gdb_breakpoint main
+	    gdb_test "continue" "Breakpoint $decimal.* main.*" "continue to main"
+
+	    # Test that we can stop inside a library.
+	    gdb_breakpoint printf
+	    gdb_test "continue" "Breakpoint $decimal.* (__)?printf.*" \
+		"continue to printf"
+	}
     }
 }
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 09/10] gdb: use exec_file with remote targets when possible
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (7 preceding siblings ...)
  2023-08-16 15:55 ` [PATCH 08/10] gdb: remote filesystem can be local to GDB in some cases Andrew Burgess
@ 2023-08-16 15:55 ` Andrew Burgess
  2023-08-16 15:55 ` [PATCH 10/10] gdb: remote the get_remote_exec_file function Andrew Burgess
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit allows GDB to make use of the file set with the 'file'
command when starting a new inferior on an extended-remote target.
There are however some restrictions.

If the user has used 'set remote exec-file', then this is always used
in preference to the file set with the 'file' command.

Similarly, if the qDefaultExecAndArgs packet has succeeded, and GDB
knows that the remote target has a default executable set, then this
will be used in preference to the file set with the 'file' command;
this preserves GDB's existing behaviour.

And, GDB can only use the file set with the 'file' command if it
believes that both GDB and the remote target will both be able to
access this file.  This means that either, the sysroot has been
updated by the user and no longer contains a 'target:' prefix, or, the
the remote_target::filesystem_is_local function returns true (see the
implementation of that function for details of when this can happen).

If all of these conditions are met, then GDB will use the file set
with the 'file' command when starting a new inferior, in all other
cases, GDB will use the file set with 'set remote exec-file'.
---
 gdb/remote.c                         |  52 ++++++++++-
 gdb/testsuite/gdb.server/ext-run.exp | 131 +++++++++++++++++++++------
 2 files changed, 154 insertions(+), 29 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 1d5e098e91f..e2d21c65b70 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1396,6 +1396,9 @@ class extended_remote_target final : public remote_target
 
   void post_attach (int) override;
   bool supports_disable_randomization () override;
+
+private:
+  std::string get_exec_file_for_create_inferior (const char *exec_file);
 };
 
 struct stop_reply : public notif_event
@@ -11031,6 +11034,51 @@ directory: %s"),
     }
 }
 
+/* Return the path for the executable that GDB should ask the remote target
+   to use when starting an inferior.  EXEC_FILE is GDB's idea of the
+   current inferior (e.g. set with the 'file' command), however, this is
+   often not the right thing for a remote target to use, which is why GDB
+   also has the 'set remote exec-file' command.
+
+   If the user has 'set remote exec-file', then this function returns the
+   path the user set.  Otherwise, if it is possible that GDB and the remote
+   target can see the same filesystem, this function will return
+   EXEC_FILE.  */
+
+std::string
+extended_remote_target::get_exec_file_for_create_inferior
+  (const char *exec_file)
+{
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+
+  /* If INFO.SECOND is remote_exec_source::DEFAULT_VALUE, then this
+     indicates that the remote target has failed to inform us if it has a
+     default-executable set or not, maybe the remote doesn't support the
+     qDefaultExecAndArgs packet?  In this case, we pass the empty string to
+     the remote and expect it to use the default executable (if one is
+     set).  */
+  if (exec_file != nullptr
+      && info.second == remote_exec_source::UNSET_VALUE)
+    {
+      /* If the sysroot has been set to something that does not have a
+	 'target:' prefix then GDB will not be trying to fetch files from
+	 the remote anyway, so we can assume that the executable is visible
+	 to both the remote and GDB.
+
+	 Otherwise, if GDB is able to determine that the remote filesystem
+	 is actually local, then we are still OK to use the local
+	 executable.  */
+      if (!is_target_filename (gdb_sysroot)
+	  || target_filesystem_is_local ())
+	return exec_file;
+    }
+
+  /* The user has set the remote exec-file, or GDB doesn't think the remote
+     target and GDB can see the same filesystem.  */
+  return info.first;
+}
+
 /* In the extended protocol we want to be able to do things like
    "run" and have them basically work as expected.  So we need
    a special create_inferior function.  We support changing the
@@ -11045,7 +11093,9 @@ extended_remote_target::create_inferior (const char *exec_file,
   int run_worked;
   char *stop_reply;
   struct remote_state *rs = get_remote_state ();
-  const std::string &remote_exec_file = get_remote_exec_file ();
+
+  std::string remote_exec_file
+    = get_exec_file_for_create_inferior (exec_file);
 
   /* If running asynchronously, register the target file descriptor
      with the event loop.  */
diff --git a/gdb/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp
index 59ead666d7b..3a4bf3e7eb1 100644
--- a/gdb/testsuite/gdb.server/ext-run.exp
+++ b/gdb/testsuite/gdb.server/ext-run.exp
@@ -30,43 +30,118 @@ if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} {
 # allow_xml_test must be called while gdb is not running.
 set do_xml_test [allow_xml_test]
 
-save_vars { GDBFLAGS } {
-    # If GDB and GDBserver are both running locally, set the sysroot to avoid
-    # reading files via the remote protocol.
-    if { ![is_remote host] && ![is_remote target] } {
-	set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\""
+# This is used as an override function.
+proc do_nothing {} { return 0 }
+
+# Start an exetended-remote gdbserver, connect to it, and then use
+# 'run' to start an inferior.
+#
+# If CLEAR_SYSROOT is true then the 'set sysroot' command is issued,
+# clearing the sysroot, otherwise the sysroot is left unchanged.
+#
+# If SET_REMOTE_EXEC is true then the 'set remote-exec ...' command is
+# issued to point GDB at the executable on the target (after copying
+# the executable over).  Otherwise, we rely on GDB and gdbserver being
+# able to see the same filesystem, remote exec-file is not set, and
+# GDB will just use the path to the executable.
+proc do_test { clear_sysroot set_remote_exec fetch_exec_and_args } {
+    save_vars { ::GDBFLAGS } {
+	if { $clear_sysroot } {
+	    set ::GDBFLAGS "$::GDBFLAGS -ex \"set sysroot\""
+	}
+
+	clean_restart $::binfile
     }
 
-    clean_restart $binfile
-}
+    # Disable, or enable, use of the qDefaultExecAndArgs packet.
+    gdb_test "set remote fetch-exec-and-args-packet ${fetch_exec_and_args}" \
+	".*"
 
-# Make sure we're disconnected, in case we're testing with an
-# extended-remote board, therefore already connected.
-gdb_test "disconnect" ".*"
+    # Make sure we're disconnected, in case we're testing with an
+    # extended-remote board, therefore already connected.
+    gdb_test "disconnect" ".*"
 
-set target_exec [gdbserver_download_current_prog]
-gdbserver_start_extended
+    gdbserver_start_extended
 
-gdb_test_no_output "set remote exec-file $target_exec" "set remote exec-file"
+    gdb_test "show remote exec-file" \
+	"The remote exec-file is \"\"\[^\r\n\]*" \
+	"check remote exec-file is unset"
 
-gdb_breakpoint main
-gdb_test "run" "Breakpoint.* main .*" "continue to main"
+    if { $set_remote_exec } {
+	set target_exec [gdbserver_download_current_prog]
+	gdb_test_no_output "set remote exec-file $target_exec" \
+	    "set remote exec-file"
+    }
 
-if { [istarget *-*-linux*] } {
-    # On Linux, gdbserver can also report the list of processes.
-    # But only if xml support is compiled in.
-    if { $do_xml_test } {
-	# This is done in a way to avoid the timeout that can occur from
-	# applying .* regexp to large output.
-	gdb_test_sequence "info os processes" "get process list" \
-	    { "pid +user +command" "1 +root +\[/a-z\]*(init|systemd)" }
+    gdb_breakpoint main
+    gdb_test_multiple "run" "continue to main" {
+	-re -wrap "Breakpoint.* main .*" {
+	    pass $gdb_test_name
+	}
+	-re -wrap "Running the default executable on the remote target failed; try \"set remote exec-file\"." {
+
+	    # If 'set remote exec-file' has been used then we should
+	    # not get here.
+	    gdb_assert {!$set_remote_exec} \
+		"confirm remote exec-file is not set"
+
+	    if {!$fetch_exec_and_args} {
+		# We deliberately disabled GDB's ability to know that
+		# the remote doesn't have a default executable set (by
+		# disabling the qDefaultExecAndArgs packet).  We got
+		# the result we expected, but the inferior is not
+		# running, so we're done with this phase of testing.
+		pass $gdb_test_name
+		return
+	    } else {
+		# This means that the qMachineId packet is not supported,
+		# and GDB could not tell if it is running on the same host
+		# as gdbserver.  Confirm that qMachineId is showing as
+		# disabled, and then move onto the next testing mode.
+		gdb_test "show remote fetch-machine-id-packet" \
+		    "Support for the 'qMachineId' packet on the current remote target is \"auto\", currently disabled\\." \
+		    "confirm qMachineId packet is not supported"
+		return
+	    }
+	}
+    }
+
+    if { [istarget *-*-linux*] } {
+	# On Linux, gdbserver can also report the list of processes.
+	# But only if xml support is compiled in.
+	if { $::do_xml_test } {
+	    # This is done in a way to avoid the timeout that can occur from
+	    # applying .* regexp to large output.
+	    gdb_test_sequence "info os processes" "get process list" \
+		{ "pid +user +command" "1 +root +\[/a-z\]*(init|systemd)" }
+	}
     }
-}
 
-gdb_test "kill" "" "kill" "Kill the program being debugged. .y or n. " "y"
+    gdb_test "kill" "" "kill" "Kill the program being debugged. .y or n. " "y"
 
-gdb_load $binfile
-gdb_test "monitor help" "The following monitor commands.*" \
+    gdb_load $::binfile
+    gdb_test "monitor help" "The following monitor commands.*" \
         "load new file without any gdbserver inferior"
 
-gdb_test_no_output "monitor exit"
+    gdb_test_no_output "monitor exit"
+}
+
+set clear_sysroot_modes { false }
+set set_remote_exec_modes { true }
+if {![is_remote target] && ![is_remote host]} {
+    lappend set_remote_exec_modes false
+    lappend clear_sysroot_modes true
+}
+
+# This override prevents GDB from automatically setting the 'remote
+# exec-file' when using the extended-remote protocol.  If we want the
+# exec-file set, then this test takes care of it.
+with_override extended_gdbserver_load_last_file do_nothing {
+    foreach_with_prefix clear_sysroot $clear_sysroot_modes {
+	foreach_with_prefix set_remote_exec $set_remote_exec_modes {
+	    foreach_with_prefix fetch_exec_and_args { on off } {
+		do_test $clear_sysroot $set_remote_exec $fetch_exec_and_args
+	    }
+	}
+    }
+}
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH 10/10] gdb: remote the get_remote_exec_file function
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (8 preceding siblings ...)
  2023-08-16 15:55 ` [PATCH 09/10] gdb: use exec_file with remote targets when possible Andrew Burgess
@ 2023-08-16 15:55 ` Andrew Burgess
  2023-08-23 13:42   ` Mark Wielaard
  2023-08-22 10:41 ` [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Alexandra Petlanova Hajkova
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-16 15:55 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

After the previous commit, the get_remote_exec_file function is only
used in one place, so inline it and delete get_remote_exec_file.

There should be no user visible changes after this commit.
---
 gdb/remote.c | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index e2d21c65b70..966bacd7fb5 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1864,16 +1864,6 @@ remote_target::get_remote_state ()
   return &m_remote_state;
 }
 
-/* Fetch the remote exec-file from the current program space.  */
-
-static const std::string &
-get_remote_exec_file ()
-{
-  const remote_exec_file_info &info
-    = get_remote_exec_file_info (current_program_space);
-  return info.first;
-}
-
 /* Set the remote exec file for PSPACE.  */
 
 static void
@@ -1900,7 +1890,9 @@ set_remote_exec_file_cb (const std::string &filename)
 static const std::string &
 get_remote_exec_file_cb ()
 {
-  return get_remote_exec_file ();
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+  return info.first;
 }
 
 /* Implement the "show remote exec-file" command.  */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-08-16 15:55 ` [PATCH 07/10] gdb: add qMachineId packet Andrew Burgess
@ 2023-08-16 16:34   ` Eli Zaretskii
  2023-08-25 14:49     ` Andrew Burgess
  2023-08-22  2:39   ` Thiago Jung Bauermann
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 46+ messages in thread
From: Eli Zaretskii @ 2023-08-16 16:34 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> Cc: Andrew Burgess <aburgess@redhat.com>
> Date: Wed, 16 Aug 2023 16:55:03 +0100
> From: Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org>
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 9839330c46d..d83f097d937 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -272,6 +272,11 @@ qDefaultExecAndArgs
>    which the server was started.  If no such information was given to
>    the server then this is reflected in the reply.
>  
> +qMachineId
> +  This packet returns an identifier that allows GDB to determine if
> +  the remote server and GDB are running on the same host, and can see
> +  the same filesystem.
> +
>  *** Changes in GDB 13

This part is OK.

> @@ -44748,6 +44749,49 @@
>  Indicates an error was encountered.
>  @end table
>  
> +@anchor{Machine-Id Packet}
> +@item qMachineId
> +@cindex query remote machine-id, remote request
> +@cindex @samp{qMachineId} packet

Please move the @cindex entries before the @item line, so that
index-search in an Info reader lands you on the line corresponding to
@item, not on the text after that.

> +within a single reply.  See @ref{Machine-Id Details} for details of
                                                       ^
Comma there, please (it is needed for some older versions of Texinfo).

Btw, when a sentence starts with "See @ref", you could simplify by
using @xref, as that is its main purpose.

Thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet
  2023-08-16 15:55 ` [PATCH 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
@ 2023-08-16 16:36   ` Eli Zaretskii
  2023-08-28 15:35   ` Tom Tromey
  1 sibling, 0 replies; 46+ messages in thread
From: Eli Zaretskii @ 2023-08-16 16:36 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> Cc: Andrew Burgess <aburgess@redhat.com>
> Date: Wed, 16 Aug 2023 16:55:00 +0100
> From: Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org>
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index ef2b3b3a4f5..9839330c46d 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -98,6 +98,14 @@
>    user that the end of file has been reached, refers the user to the
>    newly added '.' argument
>  
> +* When connecting to a remote server, if the server supports the
> +  qDefaultExecAndArgs packet, then GDB will copy the argument string
> +  from the server and update the 'args' setting, as if 'set args ...'
> +  had been used.  This means that the arguments are visible from GDB
> +  using 'show args', and that, if using the extended-remote protocol,
> +  subsequent runs of the inferior will use the same arguments as the
> +  first run.
> +
>  * New commands
>  
>  set debug breakpoint on|off
> @@ -257,6 +265,13 @@ info main
>    ** gdb.Progspace now has the new method "objfile_for_address".  This
>       returns the gdb.Objfile, if any, that covers a given address.
>  
> +* New remote packets
> +
> +qDefaultExecAndArgs
> +  This packet returns the executable filename and argument string with
> +  which the server was started.  If no such information was given to
> +  the server then this is reflected in the reply.
> +
>  *** Changes in GDB 13

This part is OK.

> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -44718,6 +44718,36 @@
>  A badly formed request or an error was encountered.
>  @end table
>  
> +@item qDefaultExecAndArgs
> +@cindex query executable, remote request
> +@cindex query program arguments, remote request
> +@cindex @samp{qDefaultExecAndArgs} packet

Once again, the @cindex entries should be before the @item line.

Thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 08/10] gdb: remote filesystem can be local to GDB in some cases
  2023-08-16 15:55 ` [PATCH 08/10] gdb: remote filesystem can be local to GDB in some cases Andrew Burgess
@ 2023-08-16 16:40   ` Eli Zaretskii
  0 siblings, 0 replies; 46+ messages in thread
From: Eli Zaretskii @ 2023-08-16 16:40 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> Cc: Andrew Burgess <aburgess@redhat.com>
> Date: Wed, 16 Aug 2023 16:55:04 +0100
> From: Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org>
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index d83f097d937..99ed057c412 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -106,6 +106,13 @@
>    subsequent runs of the inferior will use the same arguments as the
>    first run.
>  
> +* For remote targets that support the qMachineId packet, if GDB
> +  believes that the remote target is on the same host as GDB itself,
> +  and that GDB can access the same files as the remote target, then
> +  GDB will take advantage of this to access files directly, rather
> +  than copying them from the remote target.  This behaviour can be
> +  disable using the new command 'set remote local-filesystem off'.
> +
>  * New commands
>  
>  set debug breakpoint on|off
> @@ -133,6 +140,24 @@ show always-read-ctf
>  info main
>    Get main symbol to identify entry point into program.
>  
> +set remote local-filesystem on|off|auto
> +show remote local-filesystem
> +  When 'on' GDB will assume that its local filesystem is the same
> +  filesystem as the remote target, this effectively means GDB will
> +  ignore any 'target:' prefix in the sysroot setting.  When 'off' GDB
> +  will use the sysroot value to determine if a path is remote or not;
> +  a sysroot starting 'target:' indicates that paths should be treated
> +  as remote.
> +
> +  The default value for this setting is 'auto', in this mode GDB will
> +  make use of the qMachineId packet to determine if the remote target
> +  is on the same host as GDB or not.  For remote targets that don't
> +  support qMachineId, or in cases where the qMachineId indicates that
> +  the remote target is truly remote, GDB will behave as if this
> +  setting is 'off'.  Only when qMachineId is supported, and qMachineId
> +  indicates the remote target is on the same host as GDB, will GDB
> +  treat this setting as 'on'.
> +

This part is OK.

> +@anchor{set remote local-filesystem}
> +@item set remote local-filesystem @r{[}auto@r{|}on@r{|}off@r{]}
> +@itemx show remote local-filesystem
> +@cindex filesystem, for remote target

@cindex before @item again.

> +  add_setshow_auto_boolean_cmd ("local-filesystem", class_files,
> +				&remote_filesystem_is_local, _("\
> +Set whether the remote's filesystem is local to GDB."), _("\
> +Show whether the remote's filesystem is local to GDB."), _("\
> +When 'on' GDB assumes that the remote target's filesystem is the same\n\
            ^
Comma missing there.

> +When 'off' GDB will always fetch files using the remote protocol,\n\
             ^
And there.

Thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-08-16 15:55 ` [PATCH 07/10] gdb: add qMachineId packet Andrew Burgess
  2023-08-16 16:34   ` Eli Zaretskii
@ 2023-08-22  2:39   ` Thiago Jung Bauermann
  2023-08-23  9:24   ` Mark Wielaard
  2023-08-28 16:06   ` Tom Tromey
  3 siblings, 0 replies; 46+ messages in thread
From: Thiago Jung Bauermann @ 2023-08-22  2:39 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches


Hello Andrew,

This is a very interesting feature, thanks. For now at least, I have
just one comment on the machine-id for Linux:

Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org> writes:

> Generating a suitable machine-id is, I think, always going to be
> target specific.  As such, I've structured the code in a way that
> allows different targets to provide their own implementations, but
> I've only implemented a solution for the Linux targets.
>
> The reply to a qMachineId packet looks like this:
>
>   predicate;key=value[;key=value]*
>
> the idea being that the reply consists of a number of key/value pairs,
> each of which must match in order for GDB to consider the machine-id a
> match.  I currently propose just two keys:
>
>   linux-boot-id - this returns the value from the file
>   /proc/sys/kernel/random/boot_id, which, if I understand correctly,
>   should be unique(ish) for each boot of each machine, and
>
>   cuserid - this returns the value of the cuserid call.
>
> My thinking is that if we know we are on the same machine (thanks to
> linux-boot-id), and we know we are the same effective user (thanks to
> cuserid) then there's a pretty good chance that GDB and the remote can
> access the same set of files.

Unfortunately the two keys above won't detect when GDB and gdbserver are
running on the same machine but in different containers. I suggest
adding a new key for that:

  mountinfo - The hash of the contents of the target's
  /proc/self/mountinfo file.

The hash algorithm could be either SHA1 or MD5, which have
implementations in libiberty.

Disclaimer: I'm no expert in Linux containers, so I wouldn't be
surprised if there were a better way to differentiate containers (or,
more specifically for this use case, filesystem namespaces).

-- 
Thiago

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (9 preceding siblings ...)
  2023-08-16 15:55 ` [PATCH 10/10] gdb: remote the get_remote_exec_file function Andrew Burgess
@ 2023-08-22 10:41 ` Alexandra Petlanova Hajkova
  2023-08-23 14:32 ` Mark Wielaard
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
  12 siblings, 0 replies; 46+ messages in thread
From: Alexandra Petlanova Hajkova @ 2023-08-22 10:41 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1049 bytes --]

On Wed, Aug 16, 2023 at 5:55 PM Andrew Burgess via Gdb-patches <
gdb-patches@sourceware.org> wrote:

> The goal of this series is to improve the user experience when using a
> local (running on the same machine as GDB) gdbserver.
>
> Outside of testing, there are still plenty of occasions when a user
> can end up running gdbserver on the same machine as GDB, tools like
> valgrind communicate via the remote protocol, as do many simulators
> (other than those built into GDB).
>
> This series includes the following improvements:
>
>   1. Better handling of gdbserver's default executable and arguments.
>   This applies to remote gdbservers and local gdbservers,
>
>   2. Allow sysroot 'target:' prefix to be ignored when gdbserver is
>   running locally to GDB, and
>
>   3. Allow the user to skip setting 'remote exec-file' if gdbserver is
>   running locally to GDB.
>
>
> I think the series is great, I'm really excited to see them. I especially
appreciate how this improves using GDB with Valgrind's vgdb --multi.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 02/10] gdb: improve how 'remote exec-file' is stored and accessed
  2023-08-16 15:54 ` [PATCH 02/10] gdb: improve how 'remote exec-file' is stored and accessed Andrew Burgess
@ 2023-08-23  8:44   ` Alexandra Petlanova Hajkova
  0 siblings, 0 replies; 46+ messages in thread
From: Alexandra Petlanova Hajkova @ 2023-08-23  8:44 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1153 bytes --]

On Wed, Aug 16, 2023 at 5:55 PM Andrew Burgess via Gdb-patches <
gdb-patches@sourceware.org> wrote:

> This commit makes two related changes.  The goal of the commit is to
> update the 'remote exec-file' setting to work correctly in a
> multi-inferior setup.  To do this I have switched from the older
> style add_setshow_* function, which uses a single backing variable, to
> the newer style add_setshow_* functions that uses a get/set callback.
>
> The get/set callbacks now directly access the state held in the
> progspace which ensures that the correct value is always returned.
>
> However, the new get/set API requires that the get callback return a
> reference to the setting's value, which in this case needs to be a
> std::string.
>
> Currently the 'remote exec-file' setting is stored as a 'char *'
> string, which isn't going to work.
>
> And so, this commit also changes 'remote exec-file' to be stored as a
> std::string within the progspace.
>
> Now, when switching between multiple inferiors, GDB can correctly
> inform the user about the value of the 'remote exec-file' setting.
>
>
Looks super reasonable.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-08-16 15:55 ` [PATCH 07/10] gdb: add qMachineId packet Andrew Burgess
  2023-08-16 16:34   ` Eli Zaretskii
  2023-08-22  2:39   ` Thiago Jung Bauermann
@ 2023-08-23  9:24   ` Mark Wielaard
  2023-08-23 11:36     ` Andrew Burgess
  2023-08-28 16:06   ` Tom Tromey
  3 siblings, 1 reply; 46+ messages in thread
From: Mark Wielaard @ 2023-08-23  9:24 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: Alexandra Petlanova Hajkova

Hi Andrew,

On Wed, 2023-08-16 at 16:55 +0100, Andrew Burgess wrote:
> diff --git a/gdbserver/server.cc b/gdbserver/server.cc
> index e749194e039..ae40e885e70 100644
> --- a/gdbserver/server.cc
> +++ b/gdbserver/server.cc
> @@ -51,6 +51,8 @@
>  #include "gdbsupport/scoped_restore.h"
>  #include "gdbsupport/search.h"
>  
> +#include <systemd/sd-id128.h>
> +
>  /* PBUFSIZ must also be at least as big as IPA_CMD_BUF_SIZE, because
>     the client state data is passed directly to some agent
>     functions.  */

This include is only available when systemd-devel headers are
installed. But it seems not to be used, so can just be removed as far
as I can tell.

Cheers,

Mark

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 01/10] gdb: have remote_target::extended_remote_run take the exec filename
  2023-08-16 15:54 ` [PATCH 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
@ 2023-08-23  9:30   ` Alexandra Petlanova Hajkova
  0 siblings, 0 replies; 46+ messages in thread
From: Alexandra Petlanova Hajkova @ 2023-08-23  9:30 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 423 bytes --]

On Wed, Aug 16, 2023 at 5:55 PM Andrew Burgess via Gdb-patches <
gdb-patches@sourceware.org> wrote:

> Small refactor, have remote_target::extended_remote_run take the name
> of the executable to run rather than looking it up directly, the one
> caller of this function has already looked up the remote-exec
> filename.
>
> There should be no user visible changes after this commit.
>
> A nice clean up patch.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-08-23  9:24   ` Mark Wielaard
@ 2023-08-23 11:36     ` Andrew Burgess
  0 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-23 11:36 UTC (permalink / raw)
  To: Mark Wielaard, gdb-patches; +Cc: Alexandra Petlanova Hajkova

Mark Wielaard <mark@klomp.org> writes:

> Hi Andrew,
>
> On Wed, 2023-08-16 at 16:55 +0100, Andrew Burgess wrote:
>> diff --git a/gdbserver/server.cc b/gdbserver/server.cc
>> index e749194e039..ae40e885e70 100644
>> --- a/gdbserver/server.cc
>> +++ b/gdbserver/server.cc
>> @@ -51,6 +51,8 @@
>>  #include "gdbsupport/scoped_restore.h"
>>  #include "gdbsupport/search.h"
>>  
>> +#include <systemd/sd-id128.h>
>> +
>>  /* PBUFSIZ must also be at least as big as IPA_CMD_BUF_SIZE, because
>>     the client state data is passed directly to some agent
>>     functions.  */
>
> This include is only available when systemd-devel headers are
> installed. But it seems not to be used, so can just be removed as far
> as I can tell.

Thanks for spotting that.  An earlier version of this patch made use of
sd_id128_get_machine, but in the end I figured it was easier just to
read the file directly.

I've fixed this locally for now, the fix will be in V2.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 03/10] gdb: improve show text and help text for 'remote exec-file'
  2023-08-16 15:54 ` [PATCH 03/10] gdb: improve show text and help text for 'remote exec-file' Andrew Burgess
@ 2023-08-23 11:36   ` Mark Wielaard
  2023-08-24  8:56   ` Alexandra Petlanova Hajkova
  1 sibling, 0 replies; 46+ messages in thread
From: Mark Wielaard @ 2023-08-23 11:36 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Hi Andrew,

On Wed, 2023-08-16 at 16:54 +0100, Andrew Burgess wrote:
> The current behaviour for 'show remote exec-file' is this:
> 
>   (gdb) show remote exec-file
> 
>   (gdb) set remote exec-file /abc
>   (gdb) show remote exec-file
>   /abc
>   (gdb)
> 
> The first output, the blank line, is just GDB showing the default
> empty value.
> 
> This output is not really inline with GDB's more full sentence style
> output, so in this commit I've updated things, the output is now:
> 
>   (gdb) show remote exec-file
>   The remote exec-file is unset, the default remote executable will be used.
>   (gdb) set remote exec-file /abc
>   (gdb) show remote exec-file
>   The remote exec-file is "/abc".
>   (gdb)
> 
> Which I think is more helpful to the user.
> 
> I have also updated the help text for this setting.  Previously we had
> a set/show header line, but no body text, now we have:
> 
>   (gdb) help show remote exec-file
>   Show the remote pathname for starting inferiors.
>   This is the path, on the remote target, used when starting an inferior,
>   for example with the "run", "start", or "starti" commands.
>   This setting is only useful when debugging a remote target, otherwise,
>   this setting is not used.
>   (gdb)
> 
> Which is hopefully more helpful.

Yes, I like this, much more helpful to the user.

Reviewed-by: Mark Wielaard <mark@klomp.org>
Tested-by: Mark Wielaard <mark@klomp.org>

> ---
>  gdb/remote.c                                | 15 ++++++++++++---
>  gdb/testsuite/gdb.base/remote-exec-file.exp |  7 +++++--
>  gdb/testsuite/gdb.multi/gdb-settings.exp    |  2 +-
>  3 files changed, 18 insertions(+), 6 deletions(-)
> 
> diff --git a/gdb/remote.c b/gdb/remote.c
> index dc5dd24797e..6a61a0e41ac 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -1705,7 +1705,12 @@ static void
>  show_remote_exec_file (struct ui_file *file, int from_tty,
>  		       struct cmd_list_element *cmd, const char *value)
>  {
> -  gdb_printf (file, "%s\n", get_remote_exec_file ().c_str ());
> +  const std::string &filename = get_remote_exec_file ();
> +  if (filename.empty ())
> +    gdb_printf (file, _("The remote exec-file is unset, the default remote "
> +			"executable will be used.\n"));
> +  else
> +    gdb_printf (file, "The remote exec-file is \"%s\".\n", filename.c_str ());
>  }

OK.

>  static int
> @@ -15475,8 +15480,12 @@ Transfer files to and from the remote target system."),
>  
>    add_setshow_string_noescape_cmd ("exec-file", class_files,
>  				   _("\
> -Set the remote pathname for \"run\"."), _("\
> -Show the remote pathname for \"run\"."), NULL,
> +Set the remote pathname for starting inferiors."), _("\
> +Show the remote pathname for starting inferiors."), _("\
> +This is the path, on the remote target, used when starting an inferior,\n\
> +for example with the \"run\", \"start\", or \"starti\" commands.\n\
> +This setting is only useful when debugging a remote target, otherwise,\n\
> +this setting is not used."),
>  				   set_remote_exec_file_cb,
>  				   get_remote_exec_file_cb,
>  				   show_remote_exec_file,

OK.

> diff --git a/gdb/testsuite/gdb.base/remote-exec-file.exp b/gdb/testsuite/gdb.base/remote-exec-file.exp
> index 0b198630a07..1411f9636be 100644
> --- a/gdb/testsuite/gdb.base/remote-exec-file.exp
> +++ b/gdb/testsuite/gdb.base/remote-exec-file.exp
> @@ -37,10 +37,13 @@ with_test_prefix "set inf 2" {
>  
>  with_test_prefix "show inf 1" {
>      gdb_test "inferior 1" "Switching to inferior 1.*"
> -    gdb_test "show remote exec-file" "prog1"
> +    gdb_test "show remote exec-file" \
> +	"The remote exec-file is \"prog1\"\\."
> +
>  }
>  
>  with_test_prefix "show inf 2" {
>      gdb_test "inferior 2" "Switching to inferior 2.*"
> -    gdb_test "show remote exec-file" "prog2"
> +    gdb_test "show remote exec-file" \
> +	"The remote exec-file is \"prog2\"\\."
>  }

OK. New expected ouput.

> diff --git a/gdb/testsuite/gdb.multi/gdb-settings.exp b/gdb/testsuite/gdb.multi/gdb-settings.exp
> index e5922221d47..2432192ca9a 100644
> --- a/gdb/testsuite/gdb.multi/gdb-settings.exp
> +++ b/gdb/testsuite/gdb.multi/gdb-settings.exp
> @@ -90,7 +90,7 @@ foreach_with_prefix inf $inferiors {
>      gdb_test "show inferior-tty" "/inf${inf}-tty.*"
>  
>      gdb_test "with remote exec-file tmp-value -- print 1" " = 1"
> -    gdb_test "show remote exec-file" "/inf${inf}-remote-exec"
> +    gdb_test "show remote exec-file" "/inf${inf}-remote-exec.*"
>  
>      # If the inferiors are running check $_gdb_setting_str and
>      # $_gdb_setting return the correct values.

OK. the .* is necessary because there is extra text to match now.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 06/10] gdb: make use of is_target_filename
  2023-08-16 15:55 ` [PATCH 06/10] gdb: make use of is_target_filename Andrew Burgess
@ 2023-08-23 13:35   ` Mark Wielaard
  0 siblings, 0 replies; 46+ messages in thread
From: Mark Wielaard @ 2023-08-23 13:35 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Hi Andrew,

On Wed, 2023-08-16 at 16:55 +0100, Andrew Burgess wrote:
> Spotted a place where is_target_filename could be used, except that
> is_target_filename takes 'const char *' and in the location I spotted
> we have a std::string.  So I've added an overload for
> is_target_filename that takes a std::string.
> 
> There should be no user visible change after this commit.

This makes sense to me given that all is_target_filename really does
is: return startswith (name, TARGET_SYSROOT_PREFIX);
So this just moves the logic in one place.

Reviewed-by: Mark Wielaard <mark@klomp.org>

> ---
>  gdb/build-id.c | 2 +-
>  gdb/gdb_bfd.h  | 8 ++++++++
>  2 files changed, 9 insertions(+), 1 deletion(-)
> 
> diff --git a/gdb/build-id.c b/gdb/build-id.c
> index f68384f0197..6abf04ffacd 100644
> --- a/gdb/build-id.c
> +++ b/gdb/build-id.c
> @@ -90,7 +90,7 @@ build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len,
>    /* lrealpath() is expensive even for the usually non-existent files.  */
>    gdb::unique_xmalloc_ptr<char> filename_holder;
>    const char *filename = nullptr;
> -  if (startswith (link, TARGET_SYSROOT_PREFIX))
> +  if (is_target_filename (link))
>      filename = link.c_str ();
>    else if (access (link.c_str (), F_OK) == 0)
>      {
> diff --git a/gdb/gdb_bfd.h b/gdb/gdb_bfd.h
> index d15b1106d9a..5e9468dcdfd 100644
> --- a/gdb/gdb_bfd.h
> +++ b/gdb/gdb_bfd.h
> @@ -44,6 +44,14 @@ struct registry_accessor<bfd>
>  
>  int is_target_filename (const char *name);
>  
> +/* Like the above, but for std::string.  */
> +
> +static inline int
> +is_target_filename (const std::string &name)
> +{
> +  return is_target_filename (name.c_str ());
> +}
> +
>  /* Returns nonzero if the filename associated with ABFD starts with
>     TARGET_SYSROOT_PREFIX, zero otherwise.  */
>  


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 10/10] gdb: remote the get_remote_exec_file function
  2023-08-16 15:55 ` [PATCH 10/10] gdb: remote the get_remote_exec_file function Andrew Burgess
@ 2023-08-23 13:42   ` Mark Wielaard
  0 siblings, 0 replies; 46+ messages in thread
From: Mark Wielaard @ 2023-08-23 13:42 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Hi Andrew,

On Wed, 2023-08-16 at 16:55 +0100, Andrew Burgess wrote:
> After the previous commit, the get_remote_exec_file function is only
> used in one place, so inline it and delete get_remote_exec_file.
> 
> There should be no user visible changes after this commit.

This looks sane to me. Is the subject a clever joke or a typo?
It made me smile, but also I had to read it three times before I
understood what was really going on. Maybe s/remote the/remove the/ for
clarity?

Reviewed-by: Mark Wielaard <mark@klomp.org>

> ---
>  gdb/remote.c | 14 +++-----------
>  1 file changed, 3 insertions(+), 11 deletions(-)
> 
> diff --git a/gdb/remote.c b/gdb/remote.c
> index e2d21c65b70..966bacd7fb5 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -1864,16 +1864,6 @@ remote_target::get_remote_state ()
>    return &m_remote_state;
>  }
>  
> -/* Fetch the remote exec-file from the current program space.  */
> -
> -static const std::string &
> -get_remote_exec_file ()
> -{
> -  const remote_exec_file_info &info
> -    = get_remote_exec_file_info (current_program_space);
> -  return info.first;
> -}
> -
>  /* Set the remote exec file for PSPACE.  */
>  
>  static void
> @@ -1900,7 +1890,9 @@ set_remote_exec_file_cb (const std::string &filename)
>  static const std::string &
>  get_remote_exec_file_cb ()
>  {
> -  return get_remote_exec_file ();
> +  const remote_exec_file_info &info
> +    = get_remote_exec_file_info (current_program_space);
> +  return info.first;
>  }
>  
>  /* Implement the "show remote exec-file" command.  */


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (10 preceding siblings ...)
  2023-08-22 10:41 ` [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Alexandra Petlanova Hajkova
@ 2023-08-23 14:32 ` Mark Wielaard
  2023-08-23 15:26   ` Andrew Burgess
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
  12 siblings, 1 reply; 46+ messages in thread
From: Mark Wielaard @ 2023-08-23 14:32 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches

Hi Andrew,

On Wed, 2023-08-16 at 16:54 +0100, Andrew Burgess wrote:
> The goal of this series is to improve the user experience when using a
> local (running on the same machine as GDB) gdbserver.
> 
> Outside of testing, there are still plenty of occasions when a user
> can end up running gdbserver on the same machine as GDB, tools like
> valgrind communicate via the remote protocol, as do many simulators
> (other than those built into GDB).
> 
> This series includes the following improvements:
> 
>   1. Better handling of gdbserver's default executable and arguments.
>   This applies to remote gdbservers and local gdbservers,
> 
>   2. Allow sysroot 'target:' prefix to be ignored when gdbserver is
>   running locally to GDB, and
> 
>   3. Allow the user to skip setting 'remote exec-file' if gdbserver is
>   running locally to GDB.

I read through most of this patch series and created a trivial
qDefaultExecAndArgs for vgdb that just returns 'U' and it seems to work
just fine, no more setting of remote exec-file needed \o/
https://bugs.kde.org/show_bug.cgi?id=473687

I have to think a bit more about qMachineId.
There were also some opinions expressed in this bug:
https://sourceware.org/bugzilla/show_bug.cgi?id=30541

Thanks,

Mark

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver
  2023-08-23 14:32 ` Mark Wielaard
@ 2023-08-23 15:26   ` Andrew Burgess
  0 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-23 15:26 UTC (permalink / raw)
  To: Mark Wielaard, gdb-patches

Mark Wielaard <mark@klomp.org> writes:

> Hi Andrew,
>
> On Wed, 2023-08-16 at 16:54 +0100, Andrew Burgess wrote:
>> The goal of this series is to improve the user experience when using a
>> local (running on the same machine as GDB) gdbserver.
>> 
>> Outside of testing, there are still plenty of occasions when a user
>> can end up running gdbserver on the same machine as GDB, tools like
>> valgrind communicate via the remote protocol, as do many simulators
>> (other than those built into GDB).
>> 
>> This series includes the following improvements:
>> 
>>   1. Better handling of gdbserver's default executable and arguments.
>>   This applies to remote gdbservers and local gdbservers,
>> 
>>   2. Allow sysroot 'target:' prefix to be ignored when gdbserver is
>>   running locally to GDB, and
>> 
>>   3. Allow the user to skip setting 'remote exec-file' if gdbserver is
>>   running locally to GDB.
>
> I read through most of this patch series and created a trivial
> qDefaultExecAndArgs for vgdb that just returns 'U' and it seems to work
> just fine, no more setting of remote exec-file needed \o/
> https://bugs.kde.org/show_bug.cgi?id=473687

Excellent.

> I have to think a bit more about qMachineId.
> There were also some opinions expressed in this bug:
> https://sourceware.org/bugzilla/show_bug.cgi?id=30541

Thanks for the link.  I'd not read that.  I think the only thing I took
from that was that the boot-id that I'm currently using likely isn't
going to work w.r.t. containers -- but Thiago Jung Bauermann already
pointed that out.

We already have some container detection support (at least, I believe
that's what it's doing) in linux_nat_target::filesystem_is_local (), so
I'm hoping I can use something based off that to extend what I currently
have.

It's just testing it that's going to slow me down, but I hope to take
another pass at it soon.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 03/10] gdb: improve show text and help text for 'remote exec-file'
  2023-08-16 15:54 ` [PATCH 03/10] gdb: improve show text and help text for 'remote exec-file' Andrew Burgess
  2023-08-23 11:36   ` Mark Wielaard
@ 2023-08-24  8:56   ` Alexandra Petlanova Hajkova
  1 sibling, 0 replies; 46+ messages in thread
From: Alexandra Petlanova Hajkova @ 2023-08-24  8:56 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 1639 bytes --]

On Wed, Aug 16, 2023 at 5:56 PM Andrew Burgess via Gdb-patches <
gdb-patches@sourceware.org> wrote:

> The current behaviour for 'show remote exec-file' is this:
>
>   (gdb) show remote exec-file
>
>   (gdb) set remote exec-file /abc
>   (gdb) show remote exec-file
>   /abc
>   (gdb)
>
> The first output, the blank line, is just GDB showing the default
> empty value.
>
> This output is not really inline with GDB's more full sentence style
> output, so in this commit I've updated things, the output is now:
>
>   (gdb) show remote exec-file
>   The remote exec-file is unset, the default remote executable will be
> used.
>   (gdb) set remote exec-file /abc
>   (gdb) show remote exec-file
>   The remote exec-file is "/abc".
>   (gdb)
>
> Which I think is more helpful to the user.
>
> I have also updated the help text for this setting.  Previously we had
> a set/show header line, but no body text, now we have:
>
>   (gdb) help show remote exec-file
>   Show the remote pathname for starting inferiors.
>   This is the path, on the remote target, used when starting an inferior,
>   for example with the "run", "start", or "starti" commands.
>   This setting is only useful when debugging a remote target, otherwise,
>   this setting is not used.
>   (gdb)
>
> Which is hopefully more helpful.
>
>
I really like the change in behaviour. Showing the blank line for the
default remote exec-file is confusing. The more verbose help is also much
more informative. I think more verbose is almost always better.

I can confirm this change causes no regressions on ppc64le Fedora-Rawhide.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-08-16 16:34   ` Eli Zaretskii
@ 2023-08-25 14:49     ` Andrew Burgess
  2023-08-25 15:01       ` Eli Zaretskii
  0 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 14:49 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

Eli Zaretskii <eliz@gnu.org> writes:

>> Cc: Andrew Burgess <aburgess@redhat.com>
>> Date: Wed, 16 Aug 2023 16:55:03 +0100
>> From: Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org>
>> 
>> diff --git a/gdb/NEWS b/gdb/NEWS
>> index 9839330c46d..d83f097d937 100644
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -272,6 +272,11 @@ qDefaultExecAndArgs
>>    which the server was started.  If no such information was given to
>>    the server then this is reflected in the reply.
>>  
>> +qMachineId
>> +  This packet returns an identifier that allows GDB to determine if
>> +  the remote server and GDB are running on the same host, and can see
>> +  the same filesystem.
>> +
>>  *** Changes in GDB 13
>
> This part is OK.
>
>> @@ -44748,6 +44749,49 @@
>>  Indicates an error was encountered.
>>  @end table
>>  
>> +@anchor{Machine-Id Packet}
>> +@item qMachineId
>> +@cindex query remote machine-id, remote request
>> +@cindex @samp{qMachineId} packet
>
> Please move the @cindex entries before the @item line, so that
> index-search in an Info reader lands you on the line corresponding to
> @item, not on the text after that.
>
>> +within a single reply.  See @ref{Machine-Id Details} for details of
>                                                        ^
> Comma there, please (it is needed for some older versions of Texinfo).

Thanks for the feedback, I'll get that fixed.

> Btw, when a sentence starts with "See @ref", you could simplify by
> using @xref, as that is its main purpose.

When generating the 'info' docs @xref doesn't add the 'See' prefix, but
when generating the pdf 'See' is added.  I guess this explains why 'See
@ref' is common throughout the GDB manual.

It seems strange the prefix is added in some contexts, but not in
others.  The info docs don't seem to read as clearly without the 'See'
prefix, so for me at least, @xref seems the worse choice.

My question is, what am I missing here?  I'm sure there's some logic
behind this difference, but I'm not seeing it.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-08-25 14:49     ` Andrew Burgess
@ 2023-08-25 15:01       ` Eli Zaretskii
  2023-09-26 14:42         ` Andrew Burgess
  0 siblings, 1 reply; 46+ messages in thread
From: Eli Zaretskii @ 2023-08-25 15:01 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> From: Andrew Burgess <aburgess@redhat.com>
> Cc: gdb-patches@sourceware.org
> Date: Fri, 25 Aug 2023 15:49:00 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > Btw, when a sentence starts with "See @ref", you could simplify by
> > using @xref, as that is its main purpose.
> 
> When generating the 'info' docs @xref doesn't add the 'See' prefix, but
> when generating the pdf 'See' is added.  I guess this explains why 'See
> @ref' is common throughout the GDB manual.

No, @xref always produces "See".  Where did you see it without "See"?

^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 00/10] Improve GDB/gdbserver experience when using a local gdbserver
  2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
                   ` (11 preceding siblings ...)
  2023-08-23 14:32 ` Mark Wielaard
@ 2023-08-25 15:34 ` Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
                     ` (9 more replies)
  12 siblings, 10 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

The goal of this series is to improve the user experience when using a
local (running on the same machine as GDB) gdbserver.

Outside of testing, there are still plenty of occasions when a user
can end up running gdbserver on the same machine as GDB, tools like
valgrind communicate via the remote protocol, as do many simulators
(other than those built into GDB).

This series includes the following improvements:

  1. Better handling of gdbserver's default executable and arguments.
  This applies to remote gdbservers and local gdbservers,

  2. Allow sysroot 'target:' prefix to be ignored when gdbserver is
  running locally to GDB, and

  3. Allow the user to skip setting 'remote exec-file' if gdbserver is
  running locally to GDB.

Changes since V1:

  - I've added a new 'namespaces' key to the qMachineId reply (in
    patch #7), this will catch the case where the remote debug server
    is running inside a container.  At least, that's my hope.  For my
    limited testing it does seem to do what I want.

  - However, I don't think this is the full solution.  The current
    linux_nat_target::filesystem_is_local implementation is more
    dynamic, checking on each request if the inferior is in the same
    namespace, which I guess allows for some inferiors being within a
    different namespace, while others are not.  When all accesses are
    through gdbserver this is all handled correctly, but with the new
    "local" access strategy we're going to loose this.  I plan to come
    back and address this in a V3 of this series, but this isn't going
    to happen until some time in September now, so I wanted to share
    what I currently have.

---

Andrew Burgess (10):
  gdb: have remote_target::extended_remote_run take the exec filename
  gdb: improve how 'remote exec-file' is stored and accessed
  gdb: improve show text and help text for 'remote exec-file'
  gdb/gdbserver: add new qDefaultExecAndArgs packet
  gdb: detect when gdbserver has no default executable set
  gdb: make use of is_target_filename
  gdb: add qMachineId packet
  gdb: remote filesystem can be local to GDB in some cases
  gdb: use exec_file with remote targets when possible
  gdb: remove the get_remote_exec_file function

 gdb/Makefile.in                               |   3 +
 gdb/NEWS                                      |  45 ++
 gdb/build-id.c                                |   2 +-
 gdb/configure.nat                             |   2 +-
 gdb/doc/gdb.texinfo                           | 186 +++++
 gdb/gdb_bfd.h                                 |   8 +
 gdb/linux-nat.c                               |  42 ++
 gdb/nat/linux-machine-id.c                    |  85 +++
 gdb/nat/linux-machine-id.h                    |  75 ++
 gdb/nat/linux-namespaces.c                    |  13 +
 gdb/nat/linux-namespaces.h                    |  14 +
 gdb/remote-machine-id.c                       |  69 ++
 gdb/remote-machine-id.h                       | 108 +++
 gdb/remote.c                                  | 683 +++++++++++++++---
 gdb/testsuite/gdb.base/remote-exec-file.exp   |   7 +-
 gdb/testsuite/gdb.multi/gdb-settings.exp      |  13 +
 gdb/testsuite/gdb.server/ext-run.exp          | 131 +++-
 .../gdb.server/fetch-exec-and-args.c          |  34 +
 .../gdb.server/fetch-exec-and-args.exp        | 253 +++++++
 gdb/testsuite/gdb.server/server-local-fs.c    |  22 +
 gdb/testsuite/gdb.server/server-local-fs.exp  | 138 ++++
 gdb/testsuite/gdb.server/sysroot.exp          |  90 +--
 gdbserver/Makefile.in                         |   1 +
 gdbserver/configure.srv                       |   2 +-
 gdbserver/linux-low.cc                        |  24 +
 gdbserver/linux-low.h                         |   2 +
 gdbserver/server.cc                           |  45 ++
 gdbserver/target.cc                           |   8 +
 gdbserver/target.h                            |   9 +
 29 files changed, 1954 insertions(+), 160 deletions(-)
 create mode 100644 gdb/nat/linux-machine-id.c
 create mode 100644 gdb/nat/linux-machine-id.h
 create mode 100644 gdb/remote-machine-id.c
 create mode 100644 gdb/remote-machine-id.h
 create mode 100644 gdb/testsuite/gdb.server/fetch-exec-and-args.c
 create mode 100644 gdb/testsuite/gdb.server/fetch-exec-and-args.exp
 create mode 100644 gdb/testsuite/gdb.server/server-local-fs.c
 create mode 100644 gdb/testsuite/gdb.server/server-local-fs.exp


base-commit: cdb090c88b4ebf6f728a000d1ee73d9bdee9ebb3
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 01/10] gdb: have remote_target::extended_remote_run take the exec filename
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 02/10] gdb: improve how 'remote exec-file' is stored and accessed Andrew Burgess
                     ` (8 subsequent siblings)
  9 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

Small refactor, have remote_target::extended_remote_run take the name
of the executable to run rather than looking it up directly, the one
caller of this function has already looked up the remote-exec
filename.

There should be no user visible changes after this commit.
---
 gdb/remote.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 7abe08439b5..bbe81ccb54f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1223,7 +1223,8 @@ class remote_target : public process_stratum_target
   void remote_kill_k ();
 
   void extended_remote_disable_randomization (int val);
-  int extended_remote_run (const std::string &args);
+  int extended_remote_run (const char *remote_exec_file,
+			   const std::string &args);
 
   void send_environment_packet (const char *action,
 				const char *packet,
@@ -10448,11 +10449,11 @@ remote_target::extended_remote_disable_randomization (int val)
 }
 
 int
-remote_target::extended_remote_run (const std::string &args)
+remote_target::extended_remote_run (const char *remote_exec_file,
+				    const std::string &args)
 {
   struct remote_state *rs = get_remote_state ();
   int len;
-  const char *remote_exec_file = get_remote_exec_file ();
 
   /* If the user has disabled vRun support, or we have detected that
      support is not available, do not try it.  */
@@ -10647,7 +10648,7 @@ Remote replied unexpectedly while setting startup-with-shell: %s"),
   extended_remote_set_inferior_cwd ();
 
   /* Now restart the remote server.  */
-  run_worked = extended_remote_run (args) != -1;
+  run_worked = extended_remote_run (remote_exec_file, args) != -1;
   if (!run_worked)
     {
       /* vRun was not supported.  Fail if we need it to do what the
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 02/10] gdb: improve how 'remote exec-file' is stored and accessed
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 03/10] gdb: improve show text and help text for 'remote exec-file' Andrew Burgess
                     ` (7 subsequent siblings)
  9 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit makes two related changes.  The goal of the commit is to
update the 'remote exec-file' setting to work correctly in a
multi-inferior setup.  To do this I have switched from the older
style add_setshow_* function, which uses a single backing variable, to
the newer style add_setshow_* functions that uses a get/set callback.

The get/set callbacks now directly access the state held in the
progspace which ensures that the correct value is always returned.

However, the new get/set API requires that the get callback return a
reference to the setting's value, which in this case needs to be a
std::string.

Currently the 'remote exec-file' setting is stored as a 'char *'
string, which isn't going to work.

And so, this commit also changes 'remote exec-file' to be stored as a
std::string within the progspace.

Now, when switching between multiple inferiors, GDB can correctly
inform the user about the value of the 'remote exec-file' setting.
---
 gdb/remote.c                             | 70 ++++++++++++------------
 gdb/testsuite/gdb.multi/gdb-settings.exp | 13 +++++
 2 files changed, 47 insertions(+), 36 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index bbe81ccb54f..8a3bb629837 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1223,7 +1223,7 @@ class remote_target : public process_stratum_target
   void remote_kill_k ();
 
   void extended_remote_disable_randomization (int val);
-  int extended_remote_run (const char *remote_exec_file,
+  int extended_remote_run (const std::string &remote_exec_file,
 			   const std::string &args);
 
   void send_environment_packet (const char *action,
@@ -1340,14 +1340,7 @@ is_remote_target (process_stratum_target *target)
 }
 
 /* Per-program-space data key.  */
-static const registry<program_space>::key<char, gdb::xfree_deleter<char>>
-  remote_pspace_data;
-
-/* The variable registered as the control variable used by the
-   remote exec-file commands.  While the remote exec-file setting is
-   per-program-space, the set/show machinery uses this as the 
-   location of the remote exec-file value.  */
-static std::string remote_exec_file_var;
+static const registry<program_space>::key<std::string> remote_pspace_data;
 
 /* The size to align memory write packets, when practical.  The protocol
    does not guarantee any alignment, and gdb will generate short
@@ -1669,47 +1662,50 @@ remote_target::get_remote_state ()
 
 /* Fetch the remote exec-file from the current program space.  */
 
-static const char *
+static const std::string &
 get_remote_exec_file (void)
 {
-  char *remote_exec_file;
-
-  remote_exec_file = remote_pspace_data.get (current_program_space);
-  if (remote_exec_file == NULL)
-    return "";
-
-  return remote_exec_file;
+  const std::string *remote_exec_file
+    = remote_pspace_data.get (current_program_space);
+  if (remote_exec_file == nullptr)
+    remote_exec_file = remote_pspace_data.emplace (current_program_space);
+  return *remote_exec_file;
 }
 
 /* Set the remote exec file for PSPACE.  */
 
 static void
 set_pspace_remote_exec_file (struct program_space *pspace,
-			     const char *remote_exec_file)
+			     const std::string &filename)
 {
-  char *old_file = remote_pspace_data.get (pspace);
-
-  xfree (old_file);
-  remote_pspace_data.set (pspace, xstrdup (remote_exec_file));
+  remote_pspace_data.clear (pspace);
+  remote_pspace_data.emplace (pspace, filename);
 }
 
-/* The "set/show remote exec-file" set command hook.  */
+/* The "set remote exec-file" callback.  */
 
 static void
-set_remote_exec_file (const char *ignored, int from_tty,
-		      struct cmd_list_element *c)
+set_remote_exec_file_cb (const std::string &filename)
 {
   set_pspace_remote_exec_file (current_program_space,
-			       remote_exec_file_var.c_str ());
+			       filename);
+}
+
+/* Get the value for the 'set remote exec-file' user setting.  */
+
+static const std::string &
+get_remote_exec_file_cb ()
+{
+  return get_remote_exec_file ();
 }
 
-/* The "set/show remote exec-file" show command hook.  */
+/* Implement the "show remote exec-file" command.  */
 
 static void
 show_remote_exec_file (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *cmd, const char *value)
 {
-  gdb_printf (file, "%s\n", get_remote_exec_file ());
+  gdb_printf (file, "%s\n", get_remote_exec_file ().c_str ());
 }
 
 static int
@@ -10449,7 +10445,7 @@ remote_target::extended_remote_disable_randomization (int val)
 }
 
 int
-remote_target::extended_remote_run (const char *remote_exec_file,
+remote_target::extended_remote_run (const std::string &remote_exec_file,
 				    const std::string &args)
 {
   struct remote_state *rs = get_remote_state ();
@@ -10463,10 +10459,11 @@ remote_target::extended_remote_run (const char *remote_exec_file,
   strcpy (rs->buf.data (), "vRun;");
   len = strlen (rs->buf.data ());
 
-  if (strlen (remote_exec_file) * 2 + len >= get_remote_packet_size ())
+  if (remote_exec_file.size () * 2 + len >= get_remote_packet_size ())
     error (_("Remote file name too long for run packet"));
-  len += 2 * bin2hex ((gdb_byte *) remote_exec_file, rs->buf.data () + len,
-		      strlen (remote_exec_file));
+  len += 2 * bin2hex ((gdb_byte *) remote_exec_file.data (),
+		      rs->buf.data () + len,
+		      remote_exec_file.size ());
 
   if (!args.empty ())
     {
@@ -10501,7 +10498,7 @@ remote_target::extended_remote_run (const char *remote_exec_file,
 		 "try \"set remote exec-file\"?"));
       else
 	error (_("Running \"%s\" on the remote target failed"),
-	       remote_exec_file);
+	       remote_exec_file.c_str ());
     default:
       gdb_assert_not_reached ("bad switch");
     }
@@ -10618,7 +10615,7 @@ extended_remote_target::create_inferior (const char *exec_file,
   int run_worked;
   char *stop_reply;
   struct remote_state *rs = get_remote_state ();
-  const char *remote_exec_file = get_remote_exec_file ();
+  const std::string &remote_exec_file = get_remote_exec_file ();
 
   /* If running asynchronously, register the target file descriptor
      with the event loop.  */
@@ -15481,10 +15478,11 @@ Transfer files to and from the remote target system."),
 	   &remote_cmdlist);
 
   add_setshow_string_noescape_cmd ("exec-file", class_files,
-				   &remote_exec_file_var, _("\
+				   _("\
 Set the remote pathname for \"run\"."), _("\
 Show the remote pathname for \"run\"."), NULL,
-				   set_remote_exec_file,
+				   set_remote_exec_file_cb,
+				   get_remote_exec_file_cb,
 				   show_remote_exec_file,
 				   &remote_set_cmdlist,
 				   &remote_show_cmdlist);
diff --git a/gdb/testsuite/gdb.multi/gdb-settings.exp b/gdb/testsuite/gdb.multi/gdb-settings.exp
index 0ebd2bf2998..e5922221d47 100644
--- a/gdb/testsuite/gdb.multi/gdb-settings.exp
+++ b/gdb/testsuite/gdb.multi/gdb-settings.exp
@@ -71,6 +71,7 @@ foreach_with_prefix inf $inferiors {
     gdb_test_no_output "set args inf${inf}-args"
     gdb_test_no_output "set cwd /inf${inf}-cwd"
     gdb_test_no_output "set inferior-tty /inf${inf}-tty"
+    gdb_test_no_output "set remote exec-file /inf${inf}-remote-exec"
 }
 
 # Check settings are still correct for each inferior.
@@ -88,6 +89,9 @@ foreach_with_prefix inf $inferiors {
     gdb_test "with inferior-tty tmp-value -- print 1" " = 1"
     gdb_test "show inferior-tty" "/inf${inf}-tty.*"
 
+    gdb_test "with remote exec-file tmp-value -- print 1" " = 1"
+    gdb_test "show remote exec-file" "/inf${inf}-remote-exec"
+
     # If the inferiors are running check $_gdb_setting_str and
     # $_gdb_setting return the correct values.
     if { $run } {
@@ -101,6 +105,11 @@ foreach_with_prefix inf $inferiors {
 	    "\"/inf${inf}-tty\""
 	gdb_test {print $_gdb_setting("inferior-tty")} \
 	    "\"/inf${inf}-tty\""
+
+	gdb_test {print $_gdb_setting_str("remote exec-file")} \
+	    "\"/inf${inf}-remote-exec\""
+	gdb_test {print $_gdb_setting("remote exec-file")} \
+	    "\"/inf${inf}-remote-exec\""
     }
 
     # Check the settings can be read from Python.
@@ -109,6 +118,8 @@ foreach_with_prefix inf $inferiors {
 	gdb_test "python print(gdb.parameter('cwd'))" "/inf${inf}-cwd"
 	gdb_test "python print(gdb.parameter('inferior-tty'))" \
 	    "/inf${inf}-tty"
+	gdb_test "python print(gdb.parameter('remote exec-file'))" \
+	    "/inf${inf}-remote-exec"
     }
 
     # Check the settings can be read from Guile.
@@ -119,5 +130,7 @@ foreach_with_prefix inf $inferiors {
 	    "/inf${inf}-cwd"
 	gdb_test "guile (print (parameter-value \"inferior-tty\"))" \
 	    "/inf${inf}-tty"
+	gdb_test "guile (print (parameter-value \"remote exec-file\"))" \
+	    "/inf${inf}-remote-exec"
     }
 }
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 03/10] gdb: improve show text and help text for 'remote exec-file'
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 02/10] gdb: improve how 'remote exec-file' is stored and accessed Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
                     ` (6 subsequent siblings)
  9 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Mark Wielaard

The current behaviour for 'show remote exec-file' is this:

  (gdb) show remote exec-file

  (gdb) set remote exec-file /abc
  (gdb) show remote exec-file
  /abc
  (gdb)

The first output, the blank line, is just GDB showing the default
empty value.

This output is not really inline with GDB's more full sentence style
output, so in this commit I've updated things, the output is now:

  (gdb) show remote exec-file
  The remote exec-file is unset, the default remote executable will be used.
  (gdb) set remote exec-file /abc
  (gdb) show remote exec-file
  The remote exec-file is "/abc".
  (gdb)

Which I think is more helpful to the user.

I have also updated the help text for this setting.  Previously we had
a set/show header line, but no body text, now we have:

  (gdb) help show remote exec-file
  Show the remote pathname for starting inferiors.
  This is the path, on the remote target, used when starting an inferior,
  for example with the "run", "start", or "starti" commands.
  This setting is only useful when debugging a remote target, otherwise,
  this setting is not used.
  (gdb)

Which is hopefully more helpful.

Reviewed-By: Mark Wielaard <mark@klomp.org>
Tested-By: Mark Wielaard <mark@klomp.org>
---
 gdb/remote.c                                | 15 ++++++++++++---
 gdb/testsuite/gdb.base/remote-exec-file.exp |  7 +++++--
 gdb/testsuite/gdb.multi/gdb-settings.exp    |  2 +-
 3 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 8a3bb629837..1d40e3cf746 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1705,7 +1705,12 @@ static void
 show_remote_exec_file (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *cmd, const char *value)
 {
-  gdb_printf (file, "%s\n", get_remote_exec_file ().c_str ());
+  const std::string &filename = get_remote_exec_file ();
+  if (filename.empty ())
+    gdb_printf (file, _("The remote exec-file is unset, the default remote "
+			"executable will be used.\n"));
+  else
+    gdb_printf (file, "The remote exec-file is \"%s\".\n", filename.c_str ());
 }
 
 static int
@@ -15479,8 +15484,12 @@ Transfer files to and from the remote target system."),
 
   add_setshow_string_noescape_cmd ("exec-file", class_files,
 				   _("\
-Set the remote pathname for \"run\"."), _("\
-Show the remote pathname for \"run\"."), NULL,
+Set the remote pathname for starting inferiors."), _("\
+Show the remote pathname for starting inferiors."), _("\
+This is the path, on the remote target, used when starting an inferior,\n\
+for example with the \"run\", \"start\", or \"starti\" commands.\n\
+This setting is only useful when debugging a remote target, otherwise,\n\
+this setting is not used."),
 				   set_remote_exec_file_cb,
 				   get_remote_exec_file_cb,
 				   show_remote_exec_file,
diff --git a/gdb/testsuite/gdb.base/remote-exec-file.exp b/gdb/testsuite/gdb.base/remote-exec-file.exp
index 0b198630a07..1411f9636be 100644
--- a/gdb/testsuite/gdb.base/remote-exec-file.exp
+++ b/gdb/testsuite/gdb.base/remote-exec-file.exp
@@ -37,10 +37,13 @@ with_test_prefix "set inf 2" {
 
 with_test_prefix "show inf 1" {
     gdb_test "inferior 1" "Switching to inferior 1.*"
-    gdb_test "show remote exec-file" "prog1"
+    gdb_test "show remote exec-file" \
+	"The remote exec-file is \"prog1\"\\."
+
 }
 
 with_test_prefix "show inf 2" {
     gdb_test "inferior 2" "Switching to inferior 2.*"
-    gdb_test "show remote exec-file" "prog2"
+    gdb_test "show remote exec-file" \
+	"The remote exec-file is \"prog2\"\\."
 }
diff --git a/gdb/testsuite/gdb.multi/gdb-settings.exp b/gdb/testsuite/gdb.multi/gdb-settings.exp
index e5922221d47..2432192ca9a 100644
--- a/gdb/testsuite/gdb.multi/gdb-settings.exp
+++ b/gdb/testsuite/gdb.multi/gdb-settings.exp
@@ -90,7 +90,7 @@ foreach_with_prefix inf $inferiors {
     gdb_test "show inferior-tty" "/inf${inf}-tty.*"
 
     gdb_test "with remote exec-file tmp-value -- print 1" " = 1"
-    gdb_test "show remote exec-file" "/inf${inf}-remote-exec"
+    gdb_test "show remote exec-file" "/inf${inf}-remote-exec.*"
 
     # If the inferiors are running check $_gdb_setting_str and
     # $_gdb_setting return the correct values.
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
                     ` (2 preceding siblings ...)
  2023-08-25 15:34   ` [PATCHv2 03/10] gdb: improve show text and help text for 'remote exec-file' Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-26  6:46     ` Eli Zaretskii
  2023-08-25 15:34   ` [PATCHv2 05/10] gdb: detect when gdbserver has no default executable set Andrew Burgess
                     ` (5 subsequent siblings)
  9 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Eli Zaretskii

This commit adds a new remote protocol packet qDefaultExecAndArgs, and
updates GDB to use it.

When gdbserver is started a user can provide an executable and
arguments, these are used (by the remote target) to start an initial
inferior, this is the inferior to which GDB first connects.

When GDB is connected in extended-remote mode, if the user does a
'run' without specifying a new 'remote exec-file' then the executable
given on the gdbserver command line is reused to start the new
inferior.

Interestingly, the arguments given on the gdbserver command line are
only used when starting the first inferior, subsequent inferiors will
be passed an empty argument string by GDB.  This might catch out a
user, causing the rerun to behave differently than the first run.

In this commit I will add a new qDefaultExecAndArgs packet, which I
think will improve the experience in this area.

The new qDefaultExecAndArgs packet is sent from GDB, and gdbserver
replies with a packet that includes the executable filename and the
argument string that were used for starting the initial inferior.

On the GDB side this information can be used to update GDB's state,
the 'show remote exec-file' will reflect how gdbserver was started,
and 'show args' will reflect the arguments used for starting the
inferior.

As a result of updating the args, if the user restarts the inferior,
then this same argument string will be passed back to the remote
target, and used for the new inferior.  Thus, rerunning the inferior
will behave just like the initial inferior, which I think is a good
improvement.

Finally, GDB will warn if the user has 'set remote exec-file' and
then connects to a gdbserver that was started with some alternative
filename, like this:

  (gdb) set remote exec-file /tmp/foo
  (gdb) target remote | gdbserver --once - /tmp/bar
  ... snip ...
  warning: updating 'remote exec-file' to '/tmp/bar' to match remote target
  ... snip ...

I made the choice to have GDB update the remote exec-file setting to
match the remote, as, after the 'target remote', we are connected to
an inferior that is running /tmp/bar (in this case), so trying to hang
onto the non-matching user supplied setting doesn't seem helpful.

There is one case where I can see this choice being a problem, if a
user does:

  (gdb) set remote exec-file /tmp/foo
  (gdb) target extended-remote | gdbserver --once - /tmp/bar
  ... snip ...
  warning: updating 'remote exec-file' to '/tmp/bar' to match remote target
  ... snip ...
  (gdb) run

In this case, prior to this patch, they would 'run' /tmp/foo, while
after this patch, they will run /tmp/bar.  I think it is unfortunate
that I'm breaking this use case, but, I'm not _that_ sorry -- just
start the gdbserver with the correct executable and the problem goes
away.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                                      |  15 ++
 gdb/doc/gdb.texinfo                           |  30 +++
 gdb/remote.c                                  | 152 ++++++++++++-
 .../gdb.server/fetch-exec-and-args.c          |  34 +++
 .../gdb.server/fetch-exec-and-args.exp        | 207 ++++++++++++++++++
 gdbserver/server.cc                           |  33 +++
 6 files changed, 470 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.server/fetch-exec-and-args.c
 create mode 100644 gdb/testsuite/gdb.server/fetch-exec-and-args.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index c4b1f7a7e3b..d78929c1398 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -105,6 +105,14 @@
   'inferior' keyword with either the 'thread' or 'task' keywords when
   creating a breakpoint.
 
+* When connecting to a remote server, if the server supports the
+  qDefaultExecAndArgs packet, then GDB will copy the argument string
+  from the server and update the 'args' setting, as if 'set args ...'
+  had been used.  This means that the arguments are visible from GDB
+  using 'show args', and that, if using the extended-remote protocol,
+  subsequent runs of the inferior will use the same arguments as the
+  first run.
+
 * New commands
 
 set debug breakpoint on|off
@@ -278,6 +286,13 @@ info main
      inferior specific, then this field contains None.  This field can
      be written too.
 
+* New remote packets
+
+qDefaultExecAndArgs
+  This packet returns the executable filename and argument string with
+  which the server was started.  If no such information was given to
+  the server then this is reflected in the reply.
+
 *** Changes in GDB 13
 
 * MI version 1 is deprecated, and will be removed in GDB 14.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8be9725d1a2..fa062e25bca 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -44782,6 +44782,36 @@
 A badly formed request or an error was encountered.
 @end table
 
+@cindex query executable, remote request
+@cindex query program arguments, remote request
+@cindex @samp{qDefaultExecAndArgs} packet
+@item qDefaultExecAndArgs
+Return the program filename and argument string with which the remote
+server was started, if the remote server was started with such things.
+If the remote server was started without the filename of a program to
+execute, or without any arguments, then the reply indicates this.
+
+Reply:
+@table @samp
+@item U
+The program filename and arguments are not set.  If @var{GDBN} wants
+to start a new inferior, for example with @samp{vRun}, then it will
+need to provide the program filename to use.
+
+@item S;@var{PP@dots{}};@var{AA@dots{}}
+The program filename provided to the remote server when it started was
+@var{PP@dots{}}, which is a hex encoded string, and the argument
+string passed to the program when started by the server was
+@var{AA@dots{}}, which is also a hex encoded string.
+
+It is valid for either, or both, of @var{PP@dots{}} and
+@var{AA@dots{}} to be the empty string.
+
+@item E @var{NN}
+@itemx E.errtext
+Indicates an error was encountered.
+@end table
+
 @item Qbtrace:bts
 Enable branch tracing for the current thread using Branch Trace Store.
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 1d40e3cf746..ae3fcdc0bb3 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -79,6 +79,7 @@
 #include <unordered_map>
 #include "async-event.h"
 #include "gdbsupport/selftest.h"
+#include "cli/cli-style.h"
 
 /* The remote target.  */
 
@@ -302,6 +303,9 @@ enum {
      packets and the tag violation stop replies.  */
   PACKET_memory_tagging_feature,
 
+  /* Support the qDefaultExecAndArgs packet.  */
+  PACKET_qDefaultExecAndArgs,
+
   PACKET_MAX
 };
 
@@ -691,6 +695,79 @@ struct remote_features
   packet_config m_protocol_packets[PACKET_MAX];
 };
 
+/* Data structure used to hold the results of the qDefaultExecAndArgs
+   packet.  */
+
+struct remote_exec_and_args_info
+{
+  /* The result state reflects whether the packet is supported by this
+     remote target, and then which bits of state the remote target actually
+     returned to GDB .  */
+  enum class state
+  {
+    /* The remote does not support the qDefaultExecAndArgs packet.  GDB
+       should not make assumptions about the remote target's executable
+       and arguments.  */
+    UNKNOWN,
+
+    /* The remote does understand the qDefaultExecAndArgs, no executable
+       (and/or arguments) were set at the remote end.  If GDB wants the
+       remote to start an inferior it will need to provide this information.  */
+    UNSET,
+
+    /* The remote does understand the qDefaultExecAndArgs, an executable
+       and/or arguments were set at the remote end and this information is
+       held within this object.  */
+    SET
+  };
+
+  /* Create an empty instance, STATE should be state::UNKNOWN or
+     state::UNSET only.  */
+  remote_exec_and_args_info (state state = state::UNKNOWN)
+    : m_state (state)
+  {
+    gdb_assert (m_state != state::SET);
+  }
+
+  /* Create an instance in state::SET, move EXEC and ARGS into this
+     instance.  */
+  remote_exec_and_args_info (std::string &&exec, std::string &&args)
+    : m_state (state::SET),
+      m_exec (exec),
+      m_args (args)
+  { /* Nothing.  */ }
+
+  /* Is this object in state::SET?  */
+  bool is_set () const
+  {
+    return m_state == state::SET;
+  }
+
+  /* Return the argument string.  Only call when is_set returns true.  */
+  const std::string &args () const
+  {
+    gdb_assert (m_state == state::SET);
+    return m_args;
+  }
+
+  /* Return the executable string.  Only call when is_set returns true.  */
+  const std::string &exec () const
+  {
+    gdb_assert (m_state == state::SET);
+    return m_exec;
+  }
+
+private:
+  /* The state of this instance.  */
+  state m_state = state::UNKNOWN;
+
+  /* The executable path returned from the remote target.  */
+  std::string m_exec;
+
+  /* The argument string returned from the remote target.  */
+  std::string m_args;
+};
+
 class remote_target : public process_stratum_target
 {
 public:
@@ -1255,6 +1332,9 @@ class remote_target : public process_stratum_target
 
 private:
 
+  /* Fetch the executable filename and argument string from the remote.  */
+  remote_exec_and_args_info fetch_default_executable_and_arguments ();
+
   bool start_remote_1 (int from_tty, int extended_p);
 
   /* The remote state.  Don't reference this directly.  Use the
@@ -1707,7 +1787,7 @@ show_remote_exec_file (struct ui_file *file, int from_tty,
 {
   const std::string &filename = get_remote_exec_file ();
   if (filename.empty ())
-    gdb_printf (file, _("The remote exec-file is unset, the default remote "
+    gdb_printf (file, _("The remote exec-file is \"\", the default remote "
 			"executable will be used.\n"));
   else
     gdb_printf (file, "The remote exec-file is \"%s\".\n", filename.c_str ());
@@ -4913,6 +4993,55 @@ struct scoped_mark_target_starting
   scoped_restore_tmpl<bool> m_restore_starting_up;
 };
 
+/* See declaration in class above.   */
+
+remote_exec_and_args_info
+remote_target::fetch_default_executable_and_arguments ()
+{
+  if (m_features.packet_support (PACKET_qDefaultExecAndArgs) == PACKET_DISABLE)
+    return {};
+
+  struct remote_state *rs = get_remote_state ();
+
+  putpkt ("qDefaultExecAndArgs");
+  getpkt (&rs->buf, 0);
+
+  auto packet_result
+    = m_features.packet_ok (rs->buf, PACKET_qDefaultExecAndArgs);
+  if (packet_result == PACKET_UNKNOWN)
+    return {};
+
+  if (packet_result == PACKET_ERROR)
+    {
+      warning (_("Remote error: %s"), rs->buf.data ());
+      return {};
+    }
+
+  /* First character should be 'U', to indicate no information is set in
+     the server, or 'S' followed by the filename and arguments.  We treat
+     anything that is not a 'S' as if it were 'U'.  */
+  if (rs->buf[0] != 'S')
+    return { remote_exec_and_args_info::state::UNSET };
+
+  if (rs->buf[1] != ';')
+    {
+      warning (_("missing first ';' in qDefaultExecAndArgs reply"));
+      return { remote_exec_and_args_info::state::UNSET };
+    }
+
+  const char *p = &rs->buf[2];
+  const char *e = p;
+  for (; *e != ';' && *e != '\0'; ++e)
+    ;
+  std::string filename = hex2str (p, (e - p) / 2);
+
+  if (*e == ';')
+    ++e;
+  std::string args = hex2str (e, strlen (e) / 2);
+
+  return { std::move (filename), std::move (args) };
+}
+
 /* Helper for remote_target::start_remote, start the remote connection and
    sync state.  Return true if everything goes OK, otherwise, return false.
    This function exists so that the scoped_restore created within it will
@@ -4997,6 +5126,24 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
 	rs->noack_mode = 1;
     }
 
+  auto exec_and_args = fetch_default_executable_and_arguments ();
+
+  /* Update the inferior with the executable and argument string from the
+     target, these will be used when restarting the inferior, and also
+     allow the user to view this state.  */
+  if (exec_and_args.is_set ())
+    {
+      current_inferior ()->set_args (exec_and_args.args ());
+      const std::string &remote_exec = get_remote_exec_file ();
+      if (!remote_exec.empty () && remote_exec != exec_and_args.exec ())
+	warning (_("updating 'remote exec-file' to '%ps' to match "
+		   "remote target"),
+		 styled_string (file_name_style.style (),
+				exec_and_args.exec ().c_str ()));
+      set_pspace_remote_exec_file (current_program_space,
+				   exec_and_args.exec ());
+    }
+
   if (extended_p)
     {
       /* Tell the remote that we are using the extended protocol.  */
@@ -15414,6 +15561,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (PACKET_memory_tagging_feature,
 			 "memory-tagging-feature", "memory-tagging-feature", 0);
 
+  add_packet_config_cmd (PACKET_qDefaultExecAndArgs, "qDefaultExecAndArgs",
+			 "fetch-exec-and-args", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {
diff --git a/gdb/testsuite/gdb.server/fetch-exec-and-args.c b/gdb/testsuite/gdb.server/fetch-exec-and-args.c
new file mode 100644
index 00000000000..f48c8b4c6d3
--- /dev/null
+++ b/gdb/testsuite/gdb.server/fetch-exec-and-args.c
@@ -0,0 +1,34 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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/>.  */
+
+/* Simple test, do some work with the arguments so GDB has a chance to
+   break and check that the arguments are correct.  */
+
+volatile int global_counter;
+
+int
+main (int argc, char *argv[])
+{
+  int i;
+
+  global_counter = 0;		/* Break here.  */
+
+  for (i = 0; i < argc; ++i)
+    argv[i] = (char *) 0;
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.server/fetch-exec-and-args.exp b/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
new file mode 100644
index 00000000000..22c22d0c0b2
--- /dev/null
+++ b/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
@@ -0,0 +1,207 @@
+# This testcase is part of GDB, the GNU debugger.
+
+# Copyright 2023 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 qDefaultExecAndArgs packet, specifically, the argument
+# fetching component of the packet.
+
+# Skip test if target does not support argument passing.
+require {!target_info exists noargs}
+
+load_lib gdbserver-support.exp
+
+standard_testfile .c
+
+require allow_gdbserver_tests
+
+if {[build_executable "failed to build" $binfile $srcfile]} {
+    return -1
+}
+
+# Used as an override for extended_gdbserver_load_last_file in
+# configs/native-extended-gdbserver.exp, prevents the remote exec-file
+# from being set.
+proc do_nothing {} { return 0 }
+
+# Check the 'show args' output.  If PACKET is 'on' then we expect to
+# see the arguments 'a b c', otherwise we don't expect to see any
+# arguments.
+proc check_show_args { packet } {
+    if { $packet } {
+	set args_re "a b c"
+    } else {
+	set args_re ""
+    }
+
+    gdb_test "show args" \
+	"Argument list to give program being debugged when it is started is \"$args_re\"\\."
+}
+
+# Check the 'show remote exec-file' output.  PACKET is either 'on' or
+# 'off' and reflects whether the qDefaultExecAndArgs packet is turned
+# on or off.  FILENAME is what we expect to see included in the
+# output, and is converted to a regexp by this function.
+proc check_remote_exec_file { packet filename } {
+    if { $filename eq "" } {
+	set remote_exec_re \
+	    "The remote exec-file is \"\", the default remote executable will be used\\."
+    } else {
+	set remote_exec_re \
+	    "The remote exec-file is \"[string_to_regexp $filename]\"\\."
+    }
+
+    gdb_test "show remote exec-file" $remote_exec_re
+}
+
+# Check the inferior has 4 arguments.  Arg 0 will be the program name,
+# while 1, 2, and 3 should be a, b, and c respectively.
+proc check_full_args {} {
+    set exec_filename ""
+    gdb_test "print argc" " = 4"
+    gdb_test_multiple "print argv\[0\]" "" {
+	-re -wrap " = $::hex \"(.*/${::testfile})\"" {
+	    set exec_filename $expect_out(1,string)
+	    pass $gdb_test_name
+	}
+    }
+    gdb_test "print argv\[1\]" " = $::hex \"a\""
+    gdb_test "print argv\[2\]" " = $::hex \"b\""
+    gdb_test "print argv\[3\]" " = $::hex \"c\""
+
+    return $exec_filename
+}
+
+# Check that GDB can fetch the arguments from the remote using the
+# qDefaultExecAndArgs packet.  When PACKET is 'on' we allow GDB to use
+# the packet, but when PACKET is 'off' we disable use of the
+# qDefaultExecAndArgs packet and ensure GDB falls back to the expected
+# behaviour.
+proc_with_prefix test_exec_and_arg_fetch { packet } {
+    clean_restart $::binfile
+
+    # Make sure we're disconnected, in case we're testing with an
+    # extended-remote board, therefore already connected.
+    gdb_test "disconnect" ".*"
+
+    gdb_test "set remote fetch-exec-and-args ${packet}" \
+	"Support for the 'qDefaultExecAndArgs' packet on future remote targets is set to \"${packet}\"\\."
+
+    gdbserver_run "a b c"
+
+    gdb_breakpoint [gdb_get_line_number "Break here"]
+    gdb_continue_to_breakpoint "run to breakpoint"
+
+    # Look in the inferior to check the arguments were passed
+    # correctly.  We get back the name of the executable the inferior
+    # is running.  If PACKET is 'on' then we expect GDB to have
+    # automatically fetched this executable name from the remote.
+    set exec_filename [check_full_args]
+    if { !$packet } {
+	set exec_filename ""
+    }
+
+    # Check 'show args' to ensure GDB sees the correct arguments.
+    check_show_args $packet
+
+    # Check 'show remote exec-file' to ensure GDB sees the correct
+    # filename.
+    check_remote_exec_file $packet $exec_filename
+
+    # Below this point we rely on restarting the inferior, which
+    # relies on the extended-remote protocol.
+    if {[target_info gdb_protocol] ne "extended-remote"} {
+	return
+    }
+
+    with_test_prefix "rerun" {
+	# Don't restart GDB, but re-run the inferior.
+	gdb_run_cmd
+	gdb_test "" \
+	    "Breakpoint $::decimal, main \\(\[^)\]+\\).*" \
+	    "rerun until breakpoint in main"
+
+	# If the packet is enabled then we expect the arguments to
+	# still be correct, otherwise, we should have defaulted back
+	# to no additional arguments.
+	if { $packet } {
+	    check_full_args
+	} else {
+	    gdb_test "print argc" " = 1"
+	}
+
+	# Check 'show args' to ensure GDB sees the correct arguments.
+	check_show_args ${packet}
+
+	# Check 'show remote exec-file' to ensure GDB sees the correct
+	# filename.
+	check_remote_exec_file $packet $exec_filename
+    }
+}
+
+# Start GDB and set 'remote exec-file' to some random file.  Then
+# start gdbserver with the name of the actual executable.  Connect to
+# gdbserver from GDB and check that GDB gives a warning about the
+# remove exec-file value having changed.
+proc_with_prefix test_remote_exec_warning {} {
+    clean_restart
+
+    gdb_test "disconnect" ".*"
+
+    # Set the file GDB is going to debug.  For extended-remote boards
+    # this also sets the remote exec-file.
+    gdb_file_cmd $::binfile
+
+    set invalid_remote_exec "/xxx/yyy/zzz"
+    gdb_test_no_output "set remote exec-file $invalid_remote_exec"
+    check_remote_exec_file on $invalid_remote_exec
+
+    # Start gdbserver.
+    set test "start gdbserver"
+    set target_exec [gdbserver_download_current_prog]
+    set target_exec_and_args "$target_exec a b c"
+    set catchres [catch {set res [gdbserver_start "" "$target_exec_and_args"]} errmsg]
+    if { $catchres != 0 } {
+	fail "$test: $errmsg"
+    } else {
+	pass "$test"
+    }
+
+    # And connect to gdbserver.  Check for the warning GDB emits when
+    # the remote exec-file is updated.
+    set gdbserver_protocol [lindex $res 0]
+    set gdbserver_gdbport [lindex $res 1]
+    set test "connect to gdbserver"
+    set extra_re "warning: updating 'remote exec-file' to '[string_to_regexp $target_exec]' to match remote target"
+    set res [gdb_target_cmd_ext $gdbserver_protocol $gdbserver_gdbport $extra_re]
+    if { $res == 0 } {
+	pass $test
+    } elseif { $res == 1 }  {
+	fail $test
+    } else {
+	unsupported $test
+    }
+}
+
+# This override prevents the remote exec-file from being set when
+# using the extended-remote protocol.  This is harmless when using
+# other boards.
+with_override extended_gdbserver_load_last_file do_nothing {
+    foreach_with_prefix packet { on off } {
+	test_exec_and_arg_fetch ${packet}
+    }
+
+    test_remote_exec_warning
+}
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index c57270175b4..e749194e039 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -2697,6 +2697,39 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       return;
     }
 
+  if (strcmp ("qDefaultExecAndArgs", own_buf) == 0)
+    {
+      if (program_path.get () == nullptr)
+	sprintf (own_buf, "U");
+      else
+	{
+	  std::string packet ("S;");
+
+	  packet += bin2hex ((const gdb_byte *) program_path.get (),
+			     strlen (program_path.get ()));
+	  packet += ";";
+
+	  std::string args;
+	  for (const char * arg : program_args)
+	    {
+	      if (!args.empty ())
+		args += " ";
+	      args += std::string (arg);
+	    }
+	  packet += bin2hex ((const gdb_byte *) args.c_str (), args.size ());
+
+	  if (packet.size () > PBUFSIZ)
+	    {
+	      sprintf (own_buf, "E.Program name and arguments too long.");
+	      return;
+	    }
+
+	  strcpy (own_buf, packet.c_str ());
+	  *new_packet_len_p = packet.size ();
+	}
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 05/10] gdb: detect when gdbserver has no default executable set
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
                     ` (3 preceding siblings ...)
  2023-08-25 15:34   ` [PATCHv2 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 06/10] gdb: make use of is_target_filename Andrew Burgess
                     ` (4 subsequent siblings)
  9 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit extends the use of the new qDefaultExecAndArgs
packet (added in the previous commit) so that GDB now understands when
it is connected to a remote server that doesn't have a default
executable set.  We don't do much with this information right now,
other than produce more useful text for 'show remote exec-file'.

Here I've connected to a gdbserver with no default executable set:

  (gdb) show remote exec-file
  The remote exec-file is "", the remote has no default executable set.
  (gdb) file /tmp/hello.x
  Reading symbols from /tmp/hello.x...
  (gdb) run
  Starting program: /tmp/hello.x
  Running the default executable on the remote target failed; try "set remote exec-file"?
  (gdb)

I think that all makes sense.
---
 gdb/remote.c                                  | 120 ++++++++++++++----
 .../gdb.server/fetch-exec-and-args.exp        |  58 ++++++++-
 2 files changed, 150 insertions(+), 28 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index ae3fcdc0bb3..77dc9607997 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -743,6 +743,12 @@ struct remote_exec_and_args_info
     return m_state == state::SET;
   }
 
+  /* Is this object in state::UNSET?  */
+  bool is_unset () const
+  {
+    return m_state == state::UNSET;
+  }
+
   /* Return the argument string.  Only call when is_set returns true.  */
   const std::string &args () const
   {
@@ -1419,8 +1425,58 @@ is_remote_target (process_stratum_target *target)
   return as_remote_target (target) != nullptr;
 }
 
+/* An enum used to track where the per-program-space remote exec-file data
+   came from.  This is useful when deciding which warnings to give to the
+   user.  */
+
+enum class remote_exec_source
+{
+  /* The remote exec-file has it's default (empty string) value, neither
+     the user, nor the remote target have tried to set the value yet.  */
+  DEFAULT_VALUE,
+
+  /* The remote exec-file value was set based on information fetched from
+     the remote target.  We warn the user if we are replacing a value they
+     supplied with one fetched from the remote target.  */
+  VALUE_FROM_REMOTE,
+
+  /* The remote exec-file value was set either directly by the user, or by
+     GDB after the inferior performed an exec.  */
+  VALUE_FROM_GDB,
+
+  /* The remote exec-file has it's default (empty string) value, but this
+     is because the user hasn't supplied a value yet, and the remote target
+     has specifically told GDB that it has no default executable available.  */
+  UNSET_VALUE,
+};
+
+/* Data held per program-space to represent the remote exec-file path.  The
+   first item in the pair is the exec-file path, this is set either by the
+   user with 'set remote exec-file', or automatically by GDB when
+   connecting to a remote target.
+
+   The second item in the pair is an enum flag that indicates where the
+   path value came from, or, when the path is the empty string, what this
+   actually means.  See show_remote_exec_file for details.  */
+using remote_exec_file_info = std::pair<std::string, remote_exec_source>;
+
 /* Per-program-space data key.  */
-static const registry<program_space>::key<std::string> remote_pspace_data;
+static const registry<program_space>::key<remote_exec_file_info>
+  remote_pspace_data;
+
+/* Retrieve the remote_exec_file_info object for PSPACE.  If no such object
+   yet exists then create a new one using the default constructor.  */
+
+static remote_exec_file_info &
+get_remote_exec_file_info (program_space *pspace)
+{
+  remote_exec_file_info *info = remote_pspace_data.get (pspace);
+  if (info == nullptr)
+    info = remote_pspace_data.emplace (pspace, "",
+				       remote_exec_source::DEFAULT_VALUE);
+  gdb_assert (info != nullptr);
+  return *info;
+}
 
 /* The size to align memory write packets, when practical.  The protocol
    does not guarantee any alignment, and gdb will generate short
@@ -1743,23 +1799,23 @@ remote_target::get_remote_state ()
 /* Fetch the remote exec-file from the current program space.  */
 
 static const std::string &
-get_remote_exec_file (void)
+get_remote_exec_file ()
 {
-  const std::string *remote_exec_file
-    = remote_pspace_data.get (current_program_space);
-  if (remote_exec_file == nullptr)
-    remote_exec_file = remote_pspace_data.emplace (current_program_space);
-  return *remote_exec_file;
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+  return info.first;
 }
 
 /* Set the remote exec file for PSPACE.  */
 
 static void
 set_pspace_remote_exec_file (struct program_space *pspace,
-			     const std::string &filename)
+			     const std::string &filename,
+			     remote_exec_source source)
 {
-  remote_pspace_data.clear (pspace);
-  remote_pspace_data.emplace (pspace, filename);
+  remote_exec_file_info &info = get_remote_exec_file_info (pspace);
+  info.first = filename;
+  info.second = source;
 }
 
 /* The "set remote exec-file" callback.  */
@@ -1767,8 +1823,8 @@ set_pspace_remote_exec_file (struct program_space *pspace,
 static void
 set_remote_exec_file_cb (const std::string &filename)
 {
-  set_pspace_remote_exec_file (current_program_space,
-			       filename);
+  set_pspace_remote_exec_file (current_program_space, filename,
+			       remote_exec_source::VALUE_FROM_GDB);
 }
 
 /* Get the value for the 'set remote exec-file' user setting.  */
@@ -1785,12 +1841,18 @@ static void
 show_remote_exec_file (struct ui_file *file, int from_tty,
 		       struct cmd_list_element *cmd, const char *value)
 {
-  const std::string &filename = get_remote_exec_file ();
-  if (filename.empty ())
-    gdb_printf (file, _("The remote exec-file is \"\", the default remote "
-			"executable will be used.\n"));
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+
+  if (info.second == remote_exec_source::DEFAULT_VALUE)
+    gdb_printf (file, _("The remote exec-file is \"\", the default "
+			"remote executable will be used.\n"));
+  else if (info.second == remote_exec_source::UNSET_VALUE)
+    gdb_printf (file, _("The remote exec-file is \"\", the remote has "
+			"no default executable set.\n"));
   else
-    gdb_printf (file, "The remote exec-file is \"%s\".\n", filename.c_str ());
+    gdb_printf (file, _("The remote exec-file is \"%s\".\n"),
+		info.first.c_str ());
 }
 
 static int
@@ -5134,14 +5196,27 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
   if (exec_and_args.is_set ())
     {
       current_inferior ()->set_args (exec_and_args.args ());
-      const std::string &remote_exec = get_remote_exec_file ();
-      if (!remote_exec.empty () && remote_exec != exec_and_args.exec ())
+      remote_exec_file_info &info
+	= get_remote_exec_file_info (current_program_space);
+      if (info.second == remote_exec_source::VALUE_FROM_GDB
+	  && info.first != exec_and_args.exec ())
 	warning (_("updating 'remote exec-file' to '%ps' to match "
 		   "remote target"),
 		 styled_string (file_name_style.style (),
 				exec_and_args.exec ().c_str ()));
-      set_pspace_remote_exec_file (current_program_space,
-				   exec_and_args.exec ());
+      info.first = exec_and_args.exec ();
+      info.second = remote_exec_source::VALUE_FROM_REMOTE;
+    }
+  else if (exec_and_args.is_unset ())
+    {
+      remote_exec_file_info &info
+	= get_remote_exec_file_info (current_program_space);
+      if (info.second == remote_exec_source::DEFAULT_VALUE
+	  || info.second == remote_exec_source::VALUE_FROM_REMOTE)
+	{
+	  info.first.clear ();
+	  info.second = remote_exec_source::UNSET_VALUE;
+	}
     }
 
   if (extended_p)
@@ -6426,7 +6501,8 @@ remote_target::follow_exec (inferior *follow_inf, ptid_t ptid,
   if (is_target_filename (execd_pathname))
     execd_pathname += strlen (TARGET_SYSROOT_PREFIX);
 
-  set_pspace_remote_exec_file (follow_inf->pspace, execd_pathname);
+  set_pspace_remote_exec_file (follow_inf->pspace, execd_pathname,
+			       remote_exec_source::VALUE_FROM_GDB);
 }
 
 /* Same as remote_detach, but don't send the "D" packet; just disconnect.  */
diff --git a/gdb/testsuite/gdb.server/fetch-exec-and-args.exp b/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
index 22c22d0c0b2..93c8d23575b 100644
--- a/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
+++ b/gdb/testsuite/gdb.server/fetch-exec-and-args.exp
@@ -56,8 +56,13 @@ proc check_show_args { packet } {
 # output, and is converted to a regexp by this function.
 proc check_remote_exec_file { packet filename } {
     if { $filename eq "" } {
-	set remote_exec_re \
-	    "The remote exec-file is \"\", the default remote executable will be used\\."
+	if { $packet } {
+	    set remote_exec_re \
+		"The remote exec-file is \"\", the remote has no default executable set\\."
+	} else {
+	    set remote_exec_re \
+		"The remote exec-file is \"\", the default remote executable will be used\\."
+	}
     } else {
 	set remote_exec_re \
 	    "The remote exec-file is \"[string_to_regexp $filename]\"\\."
@@ -195,13 +200,54 @@ proc_with_prefix test_remote_exec_warning {} {
     }
 }
 
+# Start GDB.  When PACKET is 'off' disable the qDefaultExecAndArgs
+# packet, otherwise, when PACKET is 'on' enable the packet.
+#
+# Start gdbserver in extended-remote mode, but don't provide a
+# filename when starting gdbserver.
+#
+# Connect to the remote server, and check 'show remote exec-file'.
+proc_with_prefix test_server_with_no_exec { packet set_remote_exec } {
+    clean_restart
+
+    gdb_test "disconnect" ".*"
+
+    gdb_file_cmd $::binfile
+
+    gdb_test "set remote fetch-exec-and-args ${packet}" \
+	"Support for the 'qDefaultExecAndArgs' packet on future remote targets is set to \"${packet}\"\\."
+
+    # Start gdbserver.
+    set target_exec [gdbserver_download_current_prog]
+
+    if { $set_remote_exec } {
+	gdb_test_no_output "set remote exec-file $target_exec" \
+	    "set remote exec-file"
+	set expected_filename $target_exec
+    } else {
+	set expected_filename ""
+    }
+
+    gdbserver_start_extended
+
+    check_remote_exec_file $packet $expected_filename
+}
+
 # This override prevents the remote exec-file from being set when
 # using the extended-remote protocol.  This is harmless when using
 # other boards.
 with_override extended_gdbserver_load_last_file do_nothing {
-    foreach_with_prefix packet { on off } {
-	test_exec_and_arg_fetch ${packet}
-    }
+    # This override stops GDB connecting to the gdbserver as soon as
+    # GDB is started when testing with the extended-remote protocol.
+    with_override gdbserver_start_multi do_nothing {
+	foreach_with_prefix packet { on off } {
+	    test_exec_and_arg_fetch ${packet}
+
+	    foreach_with_prefix set_remote_exec { true false } {
+		test_server_with_no_exec $packet $set_remote_exec
+	    }
+	}
 
-    test_remote_exec_warning
+	test_remote_exec_warning
+    }
 }
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 06/10] gdb: make use of is_target_filename
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
                     ` (4 preceding siblings ...)
  2023-08-25 15:34   ` [PATCHv2 05/10] gdb: detect when gdbserver has no default executable set Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 07/10] gdb: add qMachineId packet Andrew Burgess
                     ` (3 subsequent siblings)
  9 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Mark Wielaard

Spotted a place where is_target_filename could be used, except that
is_target_filename takes 'const char *' and in the location I spotted
we have a std::string.  So I've added an overload for
is_target_filename that takes a std::string.

There should be no user visible change after this commit.

Reviewed-By: Mark Wielaard <mark@klomp.org>
---
 gdb/build-id.c | 2 +-
 gdb/gdb_bfd.h  | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/gdb/build-id.c b/gdb/build-id.c
index f68384f0197..6abf04ffacd 100644
--- a/gdb/build-id.c
+++ b/gdb/build-id.c
@@ -90,7 +90,7 @@ build_id_to_debug_bfd_1 (const std::string &link, size_t build_id_len,
   /* lrealpath() is expensive even for the usually non-existent files.  */
   gdb::unique_xmalloc_ptr<char> filename_holder;
   const char *filename = nullptr;
-  if (startswith (link, TARGET_SYSROOT_PREFIX))
+  if (is_target_filename (link))
     filename = link.c_str ();
   else if (access (link.c_str (), F_OK) == 0)
     {
diff --git a/gdb/gdb_bfd.h b/gdb/gdb_bfd.h
index d15b1106d9a..5e9468dcdfd 100644
--- a/gdb/gdb_bfd.h
+++ b/gdb/gdb_bfd.h
@@ -44,6 +44,14 @@ struct registry_accessor<bfd>
 
 int is_target_filename (const char *name);
 
+/* Like the above, but for std::string.  */
+
+static inline int
+is_target_filename (const std::string &name)
+{
+  return is_target_filename (name.c_str ());
+}
+
 /* Returns nonzero if the filename associated with ABFD starts with
    TARGET_SYSROOT_PREFIX, zero otherwise.  */
 
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 07/10] gdb: add qMachineId packet
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
                     ` (5 preceding siblings ...)
  2023-08-25 15:34   ` [PATCHv2 06/10] gdb: make use of is_target_filename Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-26  6:54     ` Eli Zaretskii
  2023-08-25 15:34   ` [PATCHv2 08/10] gdb: remote filesystem can be local to GDB in some cases Andrew Burgess
                     ` (2 subsequent siblings)
  9 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

In later commits I want to make two related changes to GDB, these are:

  1. Have GDB know when it can safely ignore a 'target:' prefix in the
  sysroot, and so avoid copying files from the remote target, and

  2. Have GDB know that it can safely use the file specified with the
  'file' command to start a remote inferior, rather than requiring the
  'set remote exec-file' command to have been used.

Both of these changes require that GDB be able to know if it is
running on the same host as the remote target.

In this commit I propose a mechanism by which this can be achieved,
that is, the introduction of the qMachineId packet.

The idea of the qMachineId packet is that, during the initial
connection phase, GDB will send the qMachineId packet, and the remote
will return a reply that describes the machine the remote target is
running on.

Back on the GDB side, GDB will generate a description of the machine
it is running on and compare this to the reply received from the
remote target.

If the two match then GDB will assume it is running on the same
machine as the remote target, and that it can access the same set of
files, in this case we can enable the two improvements listed above.

If the remote target doesn't support qMachineId, or the reply from the
remote target doesn't match the machine-id generated within GDB, then
GDB will assume that the target is truly remote, just as it does right
now.

This commit does NOT implement the two improvements listed above,
these will be added in follow on commits.  This commit just adds
support for the qMachineId packet.

Generating a suitable machine-id is, I think, always going to be
target specific.  As such, I've structured the code in a way that
allows different targets to provide their own implementations, but
I've only implemented a solution for the Linux targets.

The reply to a qMachineId packet looks like this:

  predicate;key=value[;key=value]*

the idea being that the reply consists of a number of key/value pairs,
each of which must match in order for GDB to consider the machine-id a
match.  I currently propose just two keys:

  linux-boot-id - this returns the value from the file
  /proc/sys/kernel/random/boot_id, which, if I understand correctly,
  should be unique(ish) for each boot of each machine, and

  cuserid - this returns the value of the cuserid call.

My thinking is that if we know we are on the same machine (thanks to
linux-boot-id), and we know we are the same effective user (thanks to
cuserid) then there's a pretty good chance that GDB and the remote can
access the same set of files.

As well as the 'predicate;' based reply, a remote can respond to a
qMachineId packet with one of these replies:

  local
  remote

The 'local' reply forces GDB to consider the remote as being local to
GDB.  I've documented this as something that should be used with
extreme care, obviously it would be easy for a user to run a remote
non-locally, in which case, if the remote claims to be local, then GDB
is going to try to access the files directly ... but maybe there will
be some use case where this is helpful.

The 'remote' reply is the opposite, it forces GDB to consider the
remote as being truly remote (which is the behaviour we have today).
It's always safe to return this reply, though this prevents GDB from
performing any of the improvements listed above.

For the GDB/gdbserver implementation, the code to generate the values
for the machine-id has been placed in gdb/nat/linux-machine-id.c, and
is shared between GDB and gdbserver.

There are no tests in this commit as there's no new commands, or user
visible behaviour changes (without turning on debug output), that can
be seen.  However, later commits will add new functionality, which
will rely on this packet working correctly.
---
 gdb/Makefile.in            |   3 +
 gdb/NEWS                   |   5 ++
 gdb/configure.nat          |   2 +-
 gdb/doc/gdb.texinfo        | 126 +++++++++++++++++++++++++++
 gdb/linux-nat.c            |  42 +++++++++
 gdb/nat/linux-machine-id.c |  85 +++++++++++++++++++
 gdb/nat/linux-machine-id.h |  75 ++++++++++++++++
 gdb/nat/linux-namespaces.c |  13 +++
 gdb/nat/linux-namespaces.h |  14 +++
 gdb/remote-machine-id.c    |  69 +++++++++++++++
 gdb/remote-machine-id.h    | 108 ++++++++++++++++++++++++
 gdb/remote.c               | 169 +++++++++++++++++++++++++++++++++++++
 gdbserver/Makefile.in      |   1 +
 gdbserver/configure.srv    |   2 +-
 gdbserver/linux-low.cc     |  24 ++++++
 gdbserver/linux-low.h      |   2 +
 gdbserver/server.cc        |  12 +++
 gdbserver/target.cc        |   8 ++
 gdbserver/target.h         |   9 ++
 19 files changed, 767 insertions(+), 2 deletions(-)
 create mode 100644 gdb/nat/linux-machine-id.c
 create mode 100644 gdb/nat/linux-machine-id.h
 create mode 100644 gdb/remote-machine-id.c
 create mode 100644 gdb/remote-machine-id.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9b992a3d8c0..8eda606f838 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1171,6 +1171,7 @@ COMMON_SFILES = \
 	reggroups.c \
 	remote.c \
 	remote-fileio.c \
+	remote-machine-id.c \
 	remote-notif.c \
 	reverse.c \
 	run-on-main-thread.c \
@@ -1447,6 +1448,7 @@ HFILES_NO_SRCDIR = \
 	regset.h \
 	remote.h \
 	remote-fileio.h \
+	remote-machine-id.h \
 	remote-notif.h \
 	riscv-fbsd-tdep.h \
 	riscv-ravenscar-thread.h \
@@ -1567,6 +1569,7 @@ HFILES_NO_SRCDIR = \
 	nat/gdb_thread_db.h \
 	nat/fork-inferior.h \
 	nat/linux-btrace.h \
+	nat/linux-machine-id.h \
 	nat/linux-namespaces.h \
 	nat/linux-nat.h \
 	nat/linux-osdata.h \
diff --git a/gdb/NEWS b/gdb/NEWS
index d78929c1398..2b1f265f5b8 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -293,6 +293,11 @@ qDefaultExecAndArgs
   which the server was started.  If no such information was given to
   the server then this is reflected in the reply.
 
+qMachineId
+  This packet returns an identifier that allows GDB to determine if
+  the remote server and GDB are running on the same host, and can see
+  the same filesystem.
+
 *** Changes in GDB 13
 
 * MI version 1 is deprecated, and will be removed in GDB 14.
diff --git a/gdb/configure.nat b/gdb/configure.nat
index aabcdeff989..92a8c4592bc 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -58,7 +58,7 @@ case ${gdb_host} in
 		proc-service.o \
 		linux-thread-db.o linux-nat.o nat/linux-osdata.o linux-fork.o \
 		nat/linux-procfs.o nat/linux-ptrace.o nat/linux-waitpid.o \
-		nat/linux-personality.o nat/linux-namespaces.o'
+		nat/linux-personality.o nat/linux-machine-id.o nat/linux-namespaces.o'
 	NAT_CDEPS='$(srcdir)/proc-service.list'
 	LOADLIBES='-ldl $(RDYNAMIC)'
 	;;
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index fa062e25bca..6b417ab85d8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -41730,6 +41730,7 @@
 * Traceframe Info Format::
 * Branch Trace Format::
 * Branch Trace Configuration Format::
+* Machine-Id Details::
 @end menu
 
 @node Overview
@@ -44812,6 +44813,49 @@
 Indicates an error was encountered.
 @end table
 
+@anchor{Machine-Id Packet}
+@cindex query remote machine-id, remote request
+@cindex @samp{qMachineId} packet
+@item qMachineId
+Access a remote @dfn{machine-id}.  The machine-id returned in response
+to this packet is compared to a machine-id created on the host the
+debugger is running on, if the two machine-ids match then the debugger
+will assume that the remote server and the debugger are running on the
+same machine, and can access the same files, the debugger will use
+this knowledge to avoid unnecessary copying of files from the remote
+(@pxref{File-I/O Remote Protocol Extension}).
+
+Reply:
+@table @samp
+@item predicate;@var{key}=@var{value}@r{[};@var{key}=@var{value}@r{]*}
+Returning a string starting with @samp{predicate;}, followed by one or
+more @var{key}=@var{value} pairs, defines a machine-id.  Each
+@var{key} and @var{value} is a non-empty string that must not contain
+the characters @samp{;} or @samp{=}.  Each @var{key} must be unique
+within a single reply.  See @ref{Machine-Id Details}, for details of
+valid @var{key}s and their @var{value}s.
+
+@item remote
+Returning the string @samp{remote} indicates that the remote server
+should always be considered truly remote, and files the debugger needs
+to access should be first copied from the remote.
+
+@item local
+Returning the string @samp{local} indicates that the remote server
+should always be treated as running on the same host as the debugger.
+The debugger will avoid copying files from the remote server, and will
+instead try to access files directly.
+
+Sending this reply will rarely be appropriate, as it implies certainty
+about where the remote server and debugger are running, however, in
+some tightly controlled environments this might be appropriate.  Using
+a @samp{predicate} based reply would be better if at all possible.
+
+@item E @var{NN}
+@itemx E.errtext
+Indicates an error was encountered.
+@end table
+
 @item Qbtrace:bts
 Enable branch tracing for the current thread using Branch Trace Store.
 
@@ -47519,6 +47563,88 @@
 <!ATTLIST pt	size	CDATA	#IMPLIED>
 @end smallexample
 
+@node Machine-Id Details
+@section Machine-Id Details
+@cindex machine-id key-value pair details
+
+This section describes the valid @var{key}s and @var{values}s that can
+be returned in response to the @samp{qMachineId} packet
+(@pxref{Machine-Id Packet}), specifically, when using a
+@samp{predicate;} based reply.  Other reply types for the
+@samp{qMachineId} packet don't include @var{key}s and @var{value}s.
+
+There are two types of @var{key}, master keys and secondary keys.  A
+reply should contain exactly one master key, and zero or more
+secondary keys.  The set of valid secondary keys will depend on which
+master key is used.
+
+No @var{key} of @var{value} can contain the characters @samp{;} or
+@samp{=}.
+
+The order of the @var{key}/@var{value} pairs in the reply does not
+matter.
+
+Currently supported master and secondary keys are described below:
+
+@table @samp
+@item linux-boot-id
+The value for this master key contains the contents of the first line
+of the file @file{/proc/sys/kernel/random/boot_id} with any @samp{-}
+characters filtered out.
+
+@table @samp
+@item cuserid
+The value for this secondary key contains a username string associated
+with the effective user ID of the remote server process.
+
+@item namespaces
+The value for this secondary key contains a set of namespace
+identifiers for the remote server process.  Currently the only two
+namespaces that are identified are @file{mnt} and @file{user}.  The
+format of this string is @samp{mnt:@var{mnt-id},user:@var{user-id}}.
+
+The @var{mnt-id} and @var{user-id} are either the character @samp{-}
+if the particular namespace is not support on this host, or is the
+inode of the underlying namespace (as obtained by a @code{stat()}
+call) formatted in hex without any @samp{0x} prefix.
+
+An example of this string is:
+
+@smallexample
+mnt:f0000000,user:effffffd
+@end smallexample
+@end table
+
+An example reply using @samp{linux-boot-id} and all secondary keys is
+below, due to length the line below had to be wrapped to fit on the
+page, but the actual format does not include any extra newlines or
+white space:
+
+@smallexample
+@c Not sure if wrapping this is the best solution.  If I don't wrap
+@c it, then in the pdf version of the manual this overflows the page
+@c and is truncated, but if I do wrap it, then maybe it's not clear to
+@c the reader what the actual format is?
+predicate;linux-boot-id=28d154b3b1518383b3b4efcbd221fa7d;
+       cuserid=username;
+       namespaces=mnt:f0000000,user:effffffd
+@end smallexample
+
+@end table
+
+When matching a machine-id @value{GDBN} first checks the reply for a
+master key that it understands.  If a suitable key is found
+@value{GDBN} checks that the value for the master key matches its
+value for the master key.  If the master key value matches, then
+@value{GDBN} checks all the remaining @var{key}/@var{value} pairs;
+each @var{key} must be known secondary key associated with the
+previously matched master key, and the secondary @var{value} must
+match @value{GDBN}'s computed value.
+
+If all @var{key}s are known, and their @var{value}s match, then
+@value{GDBN} considers the machine-id a match, otherwise, the
+machine-id is considered non-matching.
+
 @include agentexpr.texi
 
 @node Target Descriptions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index e58d67183b5..9f8db6bb7cc 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -69,6 +69,8 @@
 #include "gdbsupport/scope-exit.h"
 #include "gdbsupport/gdb-sigmask.h"
 #include "gdbsupport/common-debug.h"
+#include "remote-machine-id.h"
+#include "nat/linux-machine-id.h"
 #include <unordered_map>
 
 /* This comment documents high-level logic of this file.
@@ -4491,6 +4493,42 @@ current_lwp_ptid (void)
   return inferior_ptid;
 }
 
+struct linux_nat_machine_id_validation : public machine_id_validation
+{
+  linux_nat_machine_id_validation ()
+    : machine_id_validation ("linux-boot-id")
+  { /* Nothing.  */ }
+
+  bool check_master_key (const std::string &value) override
+  {
+    std::string boot_id = gdb_linux_machine_id_linux_boot_id ();
+    if (boot_id.empty ())
+      return false;
+    return boot_id == value;
+  }
+
+  bool check_secondary_key (const std::string &key,
+			    const std::string &value) override
+  {
+    if (key == "cuserid")
+      {
+	std::string username = gdb_linux_machine_id_cuserid ();
+	if (username.empty ())
+	  return false;
+	return username == value;
+      }
+    else if (key == "namespaces")
+      {
+	std::string namespaces = gdb_linux_machine_id_namespaces ();
+	if (namespaces.empty ())
+	  return false;
+	return namespaces == value;
+      }
+
+    return false;
+  }
+};
+
 void _initialize_linux_nat ();
 void
 _initialize_linux_nat ()
@@ -4528,6 +4566,10 @@ Enables printf debugging output."),
   sigemptyset (&blocked_mask);
 
   lwp_lwpid_htab_create ();
+
+  std::unique_ptr<linux_nat_machine_id_validation> validation
+    (new linux_nat_machine_id_validation);
+  register_machine_id_validation (std::move (validation));
 }
 \f
 
diff --git a/gdb/nat/linux-machine-id.c b/gdb/nat/linux-machine-id.c
new file mode 100644
index 00000000000..afa32177dbd
--- /dev/null
+++ b/gdb/nat/linux-machine-id.c
@@ -0,0 +1,85 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "gdbsupport/common-defs.h"
+
+#include "nat/linux-machine-id.h"
+#include "nat/linux-namespaces.h"
+
+#include "safe-ctype.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* See nat/linux-machine-id.h.  */
+
+std::string
+gdb_linux_machine_id_linux_boot_id ()
+{
+  int fd = open ("/proc/sys/kernel/random/boot_id", O_RDONLY);
+  if (fd < 0)
+    return "";
+
+  std::string boot_id;
+  char buf;
+  while (read (fd, &buf, sizeof (buf)) == sizeof (buf))
+    {
+      if (ISXDIGIT (buf))
+	boot_id += buf;
+    }
+
+  close (fd);
+
+  return boot_id;
+}
+
+/* See nat/linux-machine-id.h.  */
+
+std::string
+gdb_linux_machine_id_cuserid ()
+{
+  char cuserid_str[L_cuserid];
+  char *res = cuserid (cuserid_str);
+  if (res == nullptr)
+    return "";
+
+  return std::string (cuserid_str);
+}
+
+/* See nat/linux-machine-id.h.  */
+
+std::string
+gdb_linux_machine_id_namespaces ()
+{
+  std::vector<std::string> namespaces;
+  namespaces.emplace_back (linux_ns_id (LINUX_NS_MNT));
+  namespaces.emplace_back (linux_ns_id (LINUX_NS_USER));
+
+  /* Ensure namespaces are sorted alphabetically.  */
+  std::sort (namespaces.begin (), namespaces.end ());
+
+  std::string str;
+  for (const std::string &n : namespaces)
+    {
+      if (!str.empty ())
+	str += ",";
+      str += n;
+    }
+
+  return str;
+}
diff --git a/gdb/nat/linux-machine-id.h b/gdb/nat/linux-machine-id.h
new file mode 100644
index 00000000000..4167aca4bcb
--- /dev/null
+++ b/gdb/nat/linux-machine-id.h
@@ -0,0 +1,75 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef NAT_LINUX_MACHINE_ID_H
+#define NAT_LINUX_MACHINE_ID_H
+
+#include <string>
+
+/* Return a string that contains the Linux boot-id, formatted for use in
+   the qMachineId packet.  If anything goes wrong then an empty string is
+   returned, otherwise a non-empty string is returned.
+
+   This is used by gdbserver when sending the reply to a qMachineId packet,
+   and used by GDB to check the value returned in for a qMachineId
+   packet.  */
+
+extern std::string gdb_linux_machine_id_linux_boot_id ();
+
+/* Return a string that contains the result of calling cuserid, that is, a
+   username associated with the effective user-id of the current process.
+   If anything goes wrong then an empty string is returned, otherwise a
+   non-empty string is returned.
+
+   This is used by gdbserver when sending the reply to a qMachineId packet,
+   and used by GDB to check the value returned in for a qMachineId
+   packet.  */
+
+extern std::string gdb_linux_machine_id_cuserid ();
+
+/* Return a string describing various namespaces of the current process.
+   The format of the returned string is this:
+
+   <STRING> ::= <DESC-LIST>
+
+   <DESC-LIST> ::= <DESC-ITEM>
+                 | <DESC-ITEM> "," <DESC-LIST>
+
+   <DESC-ITEM> ::= <NAME> ":" <INODE-NUMBER>
+                 | <NAME> ":" "-"
+
+   The <DESC-ITEM>s in the <DESC-LIST> are sorted alphabetically in
+   ascending order.
+
+   Each <NAME> is the name of a namespace, as found in /proc/self/ns/,
+   e.g. 'mnt', 'pid', 'user', etc.
+
+   The <INODE-NUMBER> is the inode of the underlying namespace (as returned
+   by a stat call), formatted as hex with no '0x' prefix.  If the namespace
+   is not supported on the current host then the <INODE-NUMBER> is replaced
+   with the character "-".
+
+   If anything goes wrong building the namespace string then an empty
+   string is returned.
+
+   This is used by gdbserver when sending the reply to a qMachineId packet,
+   and used by GDB to check the value returned in for a qMachineId
+   packet.  */
+
+extern std::string gdb_linux_machine_id_namespaces ();
+
+#endif /* NAT_LINUX_MACHINE_ID_H */
diff --git a/gdb/nat/linux-namespaces.c b/gdb/nat/linux-namespaces.c
index 4b1fee18425..3bec724c9c4 100644
--- a/gdb/nat/linux-namespaces.c
+++ b/gdb/nat/linux-namespaces.c
@@ -1049,3 +1049,16 @@ linux_mntns_readlink (pid_t pid, const char *filename,
 
   return ret;
 }
+
+/* See nat/linux-namespaces.h.  */
+
+std::string
+linux_ns_id (enum linux_ns_type type)
+{
+  struct linux_ns *ns = linux_ns_get_namespace (type);
+  gdb_assert (ns->initialized);
+  if (!ns->supported)
+    return (std::string (ns->filename) + ":-");
+  return (std::string (ns->filename) + ":"
+	  + string_printf ("%llx", (unsigned long long) ns->id));
+}
diff --git a/gdb/nat/linux-namespaces.h b/gdb/nat/linux-namespaces.h
index a45b99cf650..523a6e709f1 100644
--- a/gdb/nat/linux-namespaces.h
+++ b/gdb/nat/linux-namespaces.h
@@ -73,4 +73,18 @@ extern int linux_mntns_unlink (pid_t pid, const char *filename);
 extern ssize_t linux_mntns_readlink (pid_t pid, const char *filename,
 				     char *buf, size_t bufsiz);
 
+/* Return an identification string representing namespace TYPE.  The
+   string has the format 'name:inode' or 'name:-'.  If something goes
+   wrong obtaining the information about namespace TYPE then an empty
+   string is returned.
+
+   The 'name' part of the returned string is the short name found in
+   /proc/self/ns/, e.g. 'mnt', 'user', etc.
+
+   The 'inode' part of the returned string is the inode of the underlying
+   namespace formatted as hex but with no '0x' prefix.  The character '-'
+   is returned if namespace TYPE is not supported on this host.  */
+
+extern std::string linux_ns_id (enum linux_ns_type type);
+
 #endif /* NAT_LINUX_NAMESPACES_H */
diff --git a/gdb/remote-machine-id.c b/gdb/remote-machine-id.c
new file mode 100644
index 00000000000..155bf5b3a0c
--- /dev/null
+++ b/gdb/remote-machine-id.c
@@ -0,0 +1,69 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "defs.h"
+#include "remote-machine-id.h"
+
+#include <string>
+#include <vector>
+
+/* List of all registered machine_id_validation objects.  */
+static std::vector<std::unique_ptr<machine_id_validation>> validation_list;
+
+/* See remote-machine-id.h.  */
+
+void
+register_machine_id_validation (std::unique_ptr<machine_id_validation> &&validation)
+{
+  validation_list.emplace_back (std::move (validation));
+}
+
+/* See remote-machine-id.  */
+
+bool
+validate_machine_id (const std::unordered_map<std::string, std::string> &kv_pairs)
+{
+  for (const auto &validator : validation_list)
+    {
+      const auto kv_master = kv_pairs.find (validator->master_key ());
+      if (kv_master == kv_pairs.end ())
+	continue;
+
+      if (!validator->check_master_key (kv_master->second))
+	continue;
+
+      /* Check all the secondary keys in KV_PAIRS.  */
+      bool match_failed = false;
+      for (const auto &kv : kv_pairs)
+	{
+	  if (kv.first == validator->master_key ())
+	    continue;
+
+	  if (!validator->check_secondary_key (kv.first, kv.second))
+	    {
+	      match_failed = true;
+	      break;
+	    }
+	}
+
+      if (!match_failed)
+	return true;
+    }
+
+  /* None of the machine_id_validation objects matched KV_PAIRS.  */
+  return false;
+}
diff --git a/gdb/remote-machine-id.h b/gdb/remote-machine-id.h
new file mode 100644
index 00000000000..8dd3b377591
--- /dev/null
+++ b/gdb/remote-machine-id.h
@@ -0,0 +1,108 @@
+/* Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef REMOTE_MACHINE_ID_H
+#define REMOTE_MACHINE_ID_H
+
+#include <memory>
+#include <unordered_map>
+
+/* A base class from which machine-id validation objects can be created.
+   A remote target can send GDB a machine-id, which can be used to check
+   if the remote target and GDB are running on the same machine, and have
+   a common view of the file-system.  Knowing this allows GDB to optimise
+   some of its interactions with the remote target.
+
+   A machine-id consists of a set of key-value pairs, where both keys and
+   values are std::string objects.  A machine-id has a single master key
+   and some number of secondary keys.
+
+   Within GDB the native target will register one or more of these objects
+   by calling register_machine_id_validation.  When GDB receives a
+   machine-id from a remote-target each registered machine_id_validation
+   object will be checked in turn to see if it matches the machined-id.
+   If any machine_id_validation matches then this indicates that GDB and
+   the remote target are on the same machine.  */
+struct machine_id_validation
+{
+  /* Constructor.  MASTER_KEY is the name of the master key that this
+     machine_id_validation object validates for.  */
+  machine_id_validation (std::string &&master_key)
+    : m_master_key (master_key)
+  { /* Nothing.  */ }
+
+  /* Destructor.  */
+  virtual ~machine_id_validation ()
+  { /* Nothing.  */ }
+
+  /* Return a reference to the master key.  */
+  const std::string &
+  master_key () const
+  {
+    return m_master_key;
+  }
+
+  /* VALUE is a string passed from the remote target corresponding to the
+     key for master_key().  If the remote target didn't pass a key
+     matching master_key() then this function should not be called.
+
+     Return true if VALUE matches the value calculated for the host on
+     which GDB is currently running.  */
+  virtual bool
+  check_master_key (const std::string &value) = 0;
+
+  /* This function will only be called for a machine-id which contains a
+     key matching master_key(), and for which check_master_key() returned
+     true.
+
+     KEY and VALUE are a key-value pair passed from the remote target.
+     This function should return true if KEY is known, and VALUE matches
+     the value calculated for the host on which GDB is running.  If KEY is
+     not known, or VALUE doesn't match, then this function should return
+     false.  */
+  virtual bool
+  check_secondary_key (const std::string &key, const std::string &value) = 0;
+
+private:
+  /* The master key for which this object validates machine-ids.  */
+  std::string m_master_key;
+};
+
+/* Register a new machine-id.  */
+
+extern void register_machine_id_validation
+  (std::unique_ptr<machine_id_validation> &&validation);
+
+/* KV_PAIRS contains all machine-id obtained from the remote target, the
+   keys are the index into the map, and the values are the values of the
+   map.  These pairs are checked against all of the registered
+   machine_id_validation objects.
+
+   If any machine_id_validation matches all the data in KV_PAIRS then this
+   function returns true, otherwise, this function returns false.
+
+   For KV_PAIRS to match against a machine_id_validation object, KV_PAIRS
+   must contain a key matching machine_id_validation::master_key(), and the
+   value for that key must return true when passed to the function
+   machine_id_validation::check_master_key().  Then, for every other
+   key/value pair machine_id_validation::check_secondary_key() must return
+   true.  */
+
+extern bool validate_machine_id
+  (const std::unordered_map<std::string, std::string> &kv_pairs);
+
+#endif /* REMOTE_MACHINE_ID_H */
diff --git a/gdb/remote.c b/gdb/remote.c
index 77dc9607997..1a1e963f832 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -80,6 +80,7 @@
 #include "async-event.h"
 #include "gdbsupport/selftest.h"
 #include "cli/cli-style.h"
+#include "remote-machine-id.h"
 
 /* The remote target.  */
 
@@ -306,6 +307,9 @@ enum {
   /* Support the qDefaultExecAndArgs packet.  */
   PACKET_qDefaultExecAndArgs,
 
+  /* Support the qMachineId packet.  */
+  PACKET_qMachineId,
+
   PACKET_MAX
 };
 
@@ -557,6 +561,15 @@ class remote_state
      this can go away.  */
   int wait_forever_enabled_p = 1;
 
+  /* Set to true if the remote target returned a machine-id (see
+     qMachineId packet) which matched one of the registered validation
+     objects.  This indicates that the remote target is running on the
+     same host as GDB (and can see the same filesystem as GDB.
+
+     Otherwise, this is false, which indicates the remote target should be
+     treated as truly remote.  */
+  bool remote_target_is_local_p = false;
+
 private:
   /* Mapping of remote protocol data for each gdbarch.  Usually there
      is only one entry here, though we may see more with stubs that
@@ -1341,6 +1354,12 @@ class remote_target : public process_stratum_target
   /* Fetch the executable filename and argument string from the remote.  */
   remote_exec_and_args_info fetch_default_executable_and_arguments ();
 
+  /* Send the qMachineId packet and process the reply.  Update the
+     remote_state::remote_target_is_local_p field based on the result.  We
+     assume that when this is called remote_target_is_local_p will be
+     false by default.  */
+  void fetch_remote_machine_id ();
+
   bool start_remote_1 (int from_tty, int extended_p);
 
   /* The remote state.  Don't reference this directly.  Use the
@@ -5055,6 +5074,151 @@ struct scoped_mark_target_starting
   scoped_restore_tmpl<bool> m_restore_starting_up;
 };
 
+/* Extract a machine-id key/value pair from the null-terminated string
+   **STRP, and update STRP to point to the first character after the parsed
+   key/value pair, including skipping any ';' that appears after the
+   key/value pair.
+
+   A key/value pair consists of two strings separated by an '=' character,
+   neither string will contain a '=' or ';' character.
+
+   Characters are read from *STRP until '=', ';' or the null character are
+   found, this forms the key string.  If ';' or null character were found
+   then the value string is empty.  Otherwise, '=' was found, the '=' is
+   skipped, and character are read until ';' or the null character are
+   found, this forms the value string.
+
+   This function will throw an error if the key string is found to be zero
+   length (e.g. '=abc' is invalid), or if the value string contains a '='
+   character (e.g. 'foo=def=ghi' is invalid).
+
+   The pair <key, value> is then returned.  */
+
+static
+std::pair<std::string, std::string> extract_kv_pair (const char **strp)
+{
+  gdb_assert (strp != nullptr);
+  gdb_assert (*strp != nullptr);
+  gdb_assert (**strp != '\0');
+
+  std::string key, value;
+  const char *str = *strp;
+  while (*str != '=' && *str != ';' && *str != '\0')
+    {
+      key += *str;
+      ++str;
+    }
+
+  if (key.empty ())
+    error (_("empty key while parsing '%s'"), *strp);
+
+  if (*str == '\0' || *str == ';')
+    {
+      if (*str == ';')
+	++str;
+      *strp = str;
+      return { key, "" };
+    }
+
+  gdb_assert (*str == '=');
+  ++str;
+
+  while (*str != ';' && *str != '\0')
+    {
+      if (*str == '=')
+	error (_("found '=' character in value string while parsing '%s'"),
+	       *strp);
+      value += *str;
+      ++str;
+    }
+
+  if (*str == ';')
+    ++str;
+  *strp = str;
+  return { key, value };
+}
+
+/* See declaration in class above.   */
+
+void
+remote_target::fetch_remote_machine_id ()
+{
+  struct remote_state *rs = get_remote_state ();
+
+  /* This should only be called for newly created remote_target objects, so
+     the remote_state::remote_target_is_local_p within the remote_target
+     should be false by default.  */
+  gdb_assert (!rs->remote_target_is_local_p);
+
+  if (m_features.packet_support (PACKET_qMachineId) == PACKET_DISABLE)
+    return;
+
+  putpkt ("qMachineId");
+  getpkt (&rs->buf, 0);
+
+  auto packet_result = m_features.packet_ok (rs->buf, PACKET_qMachineId);
+  if (packet_result == PACKET_UNKNOWN)
+    return;
+
+  if (packet_result == PACKET_ERROR)
+    {
+      warning (_("Remote error: %s"), rs->buf.data ());
+      return;
+    }
+
+  /* If the machine-id is the string 'remote' then we are done.  The
+     remote_target_is_local_p field is false by default.  */
+  const char *id = rs->buf.data ();
+  if (startswith (id, "remote") && (id[6] == ';' || id[6] == '\0'))
+    return;
+
+  /* If the machine-id is the string 'local' then the remote claims to
+     "know" that it is on the same machine as GDB.  Good luck with that.  */
+  if (startswith (id, "local") && (id[5] == ';' || id[5] == '\0'))
+    {
+      rs->remote_target_is_local_p = true;
+      return;
+    }
+
+  /* If the machine-id starts with the string 'predicate;', then
+     everything after that string is the part of the machine-id that we
+     need to match against to confirm we are on the same machine as the
+     remote target.  */
+  static const char *predicate_prefix = "predicate;";
+  if (!startswith (id, predicate_prefix))
+    return;
+  id += strlen (predicate_prefix);
+
+  /* Split the ID string into key/value pairs.  */
+  std::unordered_map<std::string, std::string> kv;
+  try
+    {
+      while (*id != '\0')
+	{
+	  auto kv_pair = extract_kv_pair (&id);
+	  kv.emplace (std::move (kv_pair.first), std::move (kv_pair.second));
+	}
+    }
+  catch (const gdb_exception &ex)
+    {
+      /* Let the user know something went wrong, and then return, treating
+	 the target as truly remote.  */
+      warning (_("Error parsing qMachineId packet: %s"), ex.what ());
+      return;
+    }
+
+  /* If there were no predicates, then this looks like a badly behaved
+     remote target, warn the user, and assume the target is remote.  */
+  if (kv.empty ())
+    {
+      warning (_("no machine-id predicates in qMachineId packet reply"));
+      return;
+    }
+
+  /* Check to see if the remote machine is actually local.  */
+  rs->remote_target_is_local_p = validate_machine_id (kv);
+}
+
 /* See declaration in class above.   */
 
 remote_exec_and_args_info
@@ -5188,6 +5352,8 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
 	rs->noack_mode = 1;
     }
 
+  fetch_remote_machine_id ();
+
   auto exec_and_args = fetch_default_executable_and_arguments ();
 
   /* Update the inferior with the executable and argument string from the
@@ -15640,6 +15806,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (PACKET_qDefaultExecAndArgs, "qDefaultExecAndArgs",
 			 "fetch-exec-and-args", 0);
 
+  add_packet_config_cmd (PACKET_qMachineId, "qMachineId",
+			 "fetch-machine-id", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 39cb9e7a151..c746a950bed 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -220,6 +220,7 @@ SFILES = \
 	$(srcdir)/../gdb/nat/aarch64-mte-linux-ptrace.c \
 	$(srcdir)/../gdb/nat/aarch64-sve-linux-ptrace.c \
 	$(srcdir)/../gdb/nat/linux-btrace.c \
+	$(srcdir)/../gdb/nat/linux-machine-id.c \
 	$(srcdir)/../gdb/nat/linux-namespaces.c \
 	$(srcdir)/../gdb/nat/linux-osdata.c \
 	$(srcdir)/../gdb/nat/linux-personality.c \
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index f0101994529..838d446d53e 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -26,7 +26,7 @@ ipa_ppc_linux_regobj="powerpc-32l-ipa.o powerpc-altivec32l-ipa.o powerpc-vsx32l-
 
 # Linux object files.  This is so we don't have to repeat
 # these files over and over again.
-srv_linux_obj="linux-low.o nat/linux-osdata.o nat/linux-procfs.o nat/linux-ptrace.o nat/linux-waitpid.o nat/linux-personality.o nat/linux-namespaces.o fork-child.o nat/fork-inferior.o"
+srv_linux_obj="linux-low.o nat/linux-osdata.o nat/linux-procfs.o nat/linux-ptrace.o nat/linux-waitpid.o nat/linux-personality.o nat/linux-machine-id.o nat/linux-namespaces.o fork-child.o nat/fork-inferior.o"
 
 # Input is taken from the "${host}" and "${target}" variables.
 
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index e1806ade82f..7d7ed36b5f9 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -61,6 +61,7 @@
 #include <elf.h>
 #endif
 #include "nat/linux-namespaces.h"
+#include "nat/linux-machine-id.h"
 
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
@@ -6940,6 +6941,29 @@ linux_process_target::thread_pending_child (thread_info *thread)
   return get_lwp_thread (child);
 }
 
+/* See target.h.  */
+
+std::string
+linux_process_target::get_machine_id () const
+{
+  std::string boot_id = gdb_linux_machine_id_linux_boot_id ();
+  if (boot_id.empty ())
+    return "";
+  boot_id = "linux-boot-id=" + boot_id;
+
+  std::string username = gdb_linux_machine_id_cuserid ();
+  if (username.empty ())
+    return "";
+  username = "cuserid=" + username;
+
+  std::string namespaces = gdb_linux_machine_id_namespaces ();
+  if (namespaces.empty ())
+    return "";
+  namespaces = "namespaces=" + namespaces;
+
+  return boot_id + ";" + username + ";" + namespaces;
+}
+
 /* Default implementation of linux_target_ops method "set_pc" for
    32-bit pc register which is literally named "pc".  */
 
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index 6dc93197f5c..1728370d1f2 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -317,6 +317,8 @@ class linux_process_target : public process_stratum_target
 
   bool supports_catch_syscall () override;
 
+  std::string get_machine_id () const override;
+
   /* Return the information to access registers.  This has public
      visibility because proc-service uses it.  */
   virtual const regs_info *get_regs_info () = 0;
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index e749194e039..dda6406854a 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -2730,6 +2730,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       return;
     }
 
+  if (strcmp ("qMachineId", own_buf) == 0)
+    {
+      std::string machine_id = the_target->get_machine_id ();
+      if (!machine_id.empty ())
+	machine_id = std::string ("predicate;") + machine_id;
+      else
+	machine_id = std::string ("remote");
+
+      strcpy (own_buf, machine_id.c_str ());
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index f8e592d20c3..2d43dfbe8de 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -442,6 +442,14 @@ process_stratum_target::store_memtags (CORE_ADDR address, size_t len,
   gdb_assert_not_reached ("target op store_memtags not supported");
 }
 
+/* See target.h.  */
+
+std::string
+process_stratum_target::get_machine_id () const
+{
+  return "";
+}
+
 int
 process_stratum_target::read_offsets (CORE_ADDR *text, CORE_ADDR *data)
 {
diff --git a/gdbserver/target.h b/gdbserver/target.h
index d993e361b76..092a6d9d3df 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -508,6 +508,15 @@ class process_stratum_target
      Returns true if successful and false otherwise.  */
   virtual bool store_memtags (CORE_ADDR address, size_t len,
 			      const gdb::byte_vector &tags, int type);
+
+  /* Return a string representing a machine-id suitable for returning
+     within a qMachineId packet response, but don't include the
+     'predicate;' prefix.
+
+     If the current target doesn't support machine-id, or if we fail to
+     build the machine-id for any reason, then return an empty string, the
+     server will send back a suitable reply to the debugger.  */
+  virtual std::string get_machine_id () const;
 };
 
 extern process_stratum_target *the_target;
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 08/10] gdb: remote filesystem can be local to GDB in some cases
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
                     ` (6 preceding siblings ...)
  2023-08-25 15:34   ` [PATCHv2 07/10] gdb: add qMachineId packet Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-26  6:49     ` Eli Zaretskii
  2023-08-25 15:34   ` [PATCHv2 09/10] gdb: use exec_file with remote targets when possible Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 10/10] gdb: remove the get_remote_exec_file function Andrew Burgess
  9 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Eli Zaretskii

This commit makes use of the qMachineId packet (added in the previous
commit) to detect when a remote target is actually running on the same
host as GDB.

If GDB detects that the remote target is running on the same host as
GDB then GDB is able to avoid fetching files from the remote, and can
instead, access the files directly, which is much quicker.

There is a new setting to control this behaviour:

  set remote local-filesystem auto|on|off
  show remote local-filesystem

By default this setting is 'auto', in which case GDB will use the
result of the qMachineId packet to choose between acting like 'on' or
'off', but the user can force this on (maybe for targets that don't
support qMachineId?), or can force it off if this feature is causing
problems.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                                     |  25 ++++
 gdb/doc/gdb.texinfo                          |  30 ++++
 gdb/remote.c                                 | 148 ++++++++++++++-----
 gdb/testsuite/gdb.server/server-local-fs.c   |  22 +++
 gdb/testsuite/gdb.server/server-local-fs.exp | 138 +++++++++++++++++
 gdb/testsuite/gdb.server/sysroot.exp         |  90 ++++++-----
 6 files changed, 371 insertions(+), 82 deletions(-)
 create mode 100644 gdb/testsuite/gdb.server/server-local-fs.c
 create mode 100644 gdb/testsuite/gdb.server/server-local-fs.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 2b1f265f5b8..36ca93398f6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -113,6 +113,13 @@
   subsequent runs of the inferior will use the same arguments as the
   first run.
 
+* For remote targets that support the qMachineId packet, if GDB
+  believes that the remote target is on the same host as GDB itself,
+  and that GDB can access the same files as the remote target, then
+  GDB will take advantage of this to access files directly, rather
+  than copying them from the remote target.  This behaviour can be
+  disable using the new command 'set remote local-filesystem off'.
+
 * New commands
 
 set debug breakpoint on|off
@@ -140,6 +147,24 @@ show always-read-ctf
 info main
   Get main symbol to identify entry point into program.
 
+set remote local-filesystem on|off|auto
+show remote local-filesystem
+  When 'on' GDB will assume that its local filesystem is the same
+  filesystem as the remote target, this effectively means GDB will
+  ignore any 'target:' prefix in the sysroot setting.  When 'off' GDB
+  will use the sysroot value to determine if a path is remote or not;
+  a sysroot starting 'target:' indicates that paths should be treated
+  as remote.
+
+  The default value for this setting is 'auto', in this mode GDB will
+  make use of the qMachineId packet to determine if the remote target
+  is on the same host as GDB or not.  For remote targets that don't
+  support qMachineId, or in cases where the qMachineId indicates that
+  the remote target is truly remote, GDB will behave as if this
+  setting is 'off'.  Only when qMachineId is supported, and qMachineId
+  indicates the remote target is on the same host as GDB, will GDB
+  treat this setting as 'on'.
+
 * New convenience function "$_shell", to execute a shell command and
   return the result.  This lets you run shell commands in expressions.
   Some examples:
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 6b417ab85d8..bc41d3dbedd 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21949,6 +21949,7 @@
 @cindex system root, alternate
 @kindex set solib-absolute-prefix
 @kindex set sysroot
+@anchor{set sysroot}
 @item set sysroot @var{path}
 Use @var{path} as the system root for the program being debugged.  Any
 absolute shared library paths will be prefixed with @var{path}; many
@@ -21977,6 +21978,15 @@
 named @file{target:} or @file{remote:}, you need to use some
 equivalent variant of the name like @file{./target:}.
 
+If @value{GDBN} is connected to a remote target, but the remote target
+is running on the same host as @value{GDBN}, then @value{GDBN} is able
+to avoid fetching files from the remote target, effectively ignoring
+the @file{target:} prefix in the sysroot.  @value{GDBN} uses the
+@code{qMachineId} packet (@pxref{Machine-Id Packet}) to determine if a
+remote target and @value{GDBN} are running on the same host or not.
+This behaviour can be controlled using @code{set remote
+local-filesystem} (@pxref{set remote local-filesystem}).
+
 For targets with an MS-DOS based filesystem, such as MS-Windows,
 @value{GDBN} tries prefixing a few variants of the target
 absolute file name with @var{path}.  But first, on Unix hosts,
@@ -24024,6 +24034,26 @@
 target system.  If it is not set, the target will use a default
 filename (e.g.@: the last program run).
 
+@anchor{set remote local-filesystem}
+@cindex filesystem, for remote target
+@item set remote local-filesystem @r{[}auto@r{|}on@r{|}off@r{]}
+@itemx show remote local-filesystem
+When set to @samp{on}, @value{GDBN} will ignore any @file{target:}
+prefix in the sysroot setting (@pxref{set sysroot,,Setting the
+sysroot}) when accessing files on a remote target.  The result of this
+is that @value{GDBN} will act as though @value{GDBN} and the remote
+target are sharing a filesystem.
+
+When set to @samp{off}, @value{GDBN} treats a @file{target:} prefix in
+the usual way; files will be copied from the remote target before
+being read.
+
+When set to @samp{auto}, which is the default, @value{GDBN} will use
+the @code{qMachineId} packet (@pxref{Machine-Id Packet}) to determine
+if the remote target and @value{GDBN} are running on the same host,
+and can access the same filesystem, in which case, this setting act as
+if @samp{on}.  Otherwise, this setting acts as if @samp{off}.
+
 @item set remote interrupt-sequence
 @cindex interrupt remote programs
 @cindex select Ctrl-C, BREAK or BREAK-g
diff --git a/gdb/remote.c b/gdb/remote.c
index 1a1e963f832..d5b758c888d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1444,6 +1444,52 @@ is_remote_target (process_stratum_target *target)
   return as_remote_target (target) != nullptr;
 }
 
+/* Get a pointer to the current remote target.  If not connected to a
+   remote target, return NULL.  */
+
+static remote_target *
+get_current_remote_target ()
+{
+  target_ops *proc_target = current_inferior ()->process_target ();
+  return dynamic_cast<remote_target *> (proc_target);
+}
+
+/* Should GDB assume that the remote target is on the same local
+   filesystem?  For example, when starting a target using the '|'
+   notation, the target will be on the local machine.
+
+   When this is set to auto GDB will try to figure this out itself, while
+   setting this to true forces GDB to assume the remote is actually
+   local.  */
+
+static enum auto_boolean remote_filesystem_is_local = AUTO_BOOLEAN_AUTO;
+
+/* Implement 'show remote local-filesystem'.  */
+
+static void
+show_remote_local_filesystem (struct ui_file *file, int from_tty,
+			      struct cmd_list_element *cmd,
+			      const char *value)
+{
+  if (remote_filesystem_is_local == AUTO_BOOLEAN_AUTO)
+    {
+      remote_target *remote = get_current_remote_target ();
+
+      if (remote == nullptr)
+	gdb_printf (file, _("Whether the remote filesystem is local to GDB "
+			    "is \"auto\" (current target is not remote).\n"));
+      else if (target_filesystem_is_local ())
+	gdb_printf (file, _("Whether the remote filesystem is local to GDB "
+			    "is \"auto\" (currently \"on\").\n"));
+      else
+	gdb_printf (file, _("Whether the remote filesystem is local to GDB "
+			    "is \"auto\" (currently \"off\").\n"));
+    }
+  else
+    gdb_printf (file, _("Whether the remote filesystem is local to GDB "
+			"is \"%s\".\n"), value);
+}
+
 /* An enum used to track where the per-program-space remote exec-file data
    came from.  This is useful when deciding which warnings to give to the
    user.  */
@@ -1973,16 +2019,6 @@ remote_arch_state::remote_arch_state (struct gdbarch *gdbarch)
     this->remote_packet_size = (this->sizeof_g_packet * 2 + 32);
 }
 
-/* Get a pointer to the current remote target.  If not connected to a
-   remote target, return NULL.  */
-
-static remote_target *
-get_current_remote_target ()
-{
-  target_ops *proc_target = current_inferior ()->process_target ();
-  return dynamic_cast<remote_target *> (proc_target);
-}
-
 /* Return the current allowed size of a remote packet.  This is
    inferred from the current architecture, and should be used to
    limit the length of outgoing packets.  */
@@ -13216,49 +13252,60 @@ remote_target::fileio_fstat (int fd, struct stat *st, fileio_error *remote_errno
 bool
 remote_target::filesystem_is_local ()
 {
-  /* Valgrind GDB presents itself as a remote target but works
-     on the local filesystem: it does not implement remote get
-     and users are not expected to set a sysroot.  To handle
-     this case we treat the remote filesystem as local if the
-     sysroot is exactly TARGET_SYSROOT_PREFIX and if the stub
-     does not support vFile:open.  */
-  if (gdb_sysroot == TARGET_SYSROOT_PREFIX)
+  if (remote_filesystem_is_local == AUTO_BOOLEAN_AUTO)
     {
-      packet_support ps = m_features.packet_support (PACKET_vFile_open);
+      /* If the remote appears to be on the same local machine then assume
+	 the filesystem is local.  */
+      struct remote_state *rs = get_remote_state ();
+      if (rs->remote_target_is_local_p)
+	return true;
 
-      if (ps == PACKET_SUPPORT_UNKNOWN)
+      /* Valgrind GDB presents itself as a remote target but works
+	 on the local filesystem: it does not implement remote get
+	 and users are not expected to set a sysroot.  To handle
+	 this case we treat the remote filesystem as local if the
+	 sysroot is exactly TARGET_SYSROOT_PREFIX and if the stub
+	 does not support vFile:open.  */
+      if (gdb_sysroot == TARGET_SYSROOT_PREFIX)
 	{
-	  int fd;
-	  fileio_error remote_errno;
+	  packet_support ps = m_features.packet_support (PACKET_vFile_open);
 
-	  /* Try opening a file to probe support.  The supplied
-	     filename is irrelevant, we only care about whether
-	     the stub recognizes the packet or not.  */
-	  fd = remote_hostio_open (NULL, "just probing",
-				   FILEIO_O_RDONLY, 0700, 0,
-				   &remote_errno);
+	  if (ps == PACKET_SUPPORT_UNKNOWN)
+	    {
+	      int fd;
+	      fileio_error remote_errno;
 
-	  if (fd >= 0)
-	    remote_hostio_close (fd, &remote_errno);
+	      /* Try opening a file to probe support.  The supplied
+		 filename is irrelevant, we only care about whether
+		 the stub recognizes the packet or not.  */
+	      fd = remote_hostio_open (NULL, "just probing",
+				       FILEIO_O_RDONLY, 0700, 0,
+				       &remote_errno);
 
-	  ps = m_features.packet_support (PACKET_vFile_open);
-	}
+	      if (fd >= 0)
+		remote_hostio_close (fd, &remote_errno);
 
-      if (ps == PACKET_DISABLE)
-	{
-	  static int warning_issued = 0;
+	      ps = m_features.packet_support (PACKET_vFile_open);
+	    }
 
-	  if (!warning_issued)
+	  if (ps == PACKET_DISABLE)
 	    {
-	      warning (_("remote target does not support file"
-			 " transfer, attempting to access files"
-			 " from local filesystem."));
-	      warning_issued = 1;
-	    }
+	      static int warning_issued = 0;
 
-	  return true;
+	      if (!warning_issued)
+		{
+		  warning (_("remote target does not support file"
+			     " transfer, attempting to access files"
+			     " from local filesystem."));
+		  warning_issued = 1;
+		}
+
+	      return true;
+	    }
 	}
     }
+  else if (remote_filesystem_is_local == AUTO_BOOLEAN_TRUE)
+    return true;
 
   return false;
 }
@@ -15891,6 +15938,25 @@ this setting is not used."),
 				   &remote_set_cmdlist,
 				   &remote_show_cmdlist);
 
+  add_setshow_auto_boolean_cmd ("local-filesystem", class_files,
+				&remote_filesystem_is_local, _("\
+Set whether the remote's filesystem is local to GDB."), _("\
+Show whether the remote's filesystem is local to GDB."), _("\
+When 'on', GDB assumes that the remote target's filesystem is the same\n\
+local filesystem as GDB sees.  GDB can avoid transferring files over\n\
+the remote protocol, and will instead access the files directly.\n\
+When 'off', GDB will always fetch files using the remote protocol,\n\
+e.g. when an inferior loads a library, GDB will read the libraries\n\
+debug information using the remote protocol, which is slower than\n\
+accessing the library directly.\n\
+\n\
+The default for the setting is 'auto', in which case GDB will try to\n\
+detect when the remote target is running on the same host as GDB."),
+				nullptr,
+				show_remote_local_filesystem,
+				&remote_set_cmdlist,
+				&remote_show_cmdlist);
+
   add_setshow_boolean_cmd ("range-stepping", class_run,
 			   &use_range_stepping, _("\
 Enable or disable range stepping."), _("\
diff --git a/gdb/testsuite/gdb.server/server-local-fs.c b/gdb/testsuite/gdb.server/server-local-fs.c
new file mode 100644
index 00000000000..439a0927f73
--- /dev/null
+++ b/gdb/testsuite/gdb.server/server-local-fs.c
@@ -0,0 +1,22 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2023 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
+main (void)
+{
+  return 0;	/* Break here.  */
+}
diff --git a/gdb/testsuite/gdb.server/server-local-fs.exp b/gdb/testsuite/gdb.server/server-local-fs.exp
new file mode 100644
index 00000000000..b830c06d033
--- /dev/null
+++ b/gdb/testsuite/gdb.server/server-local-fs.exp
@@ -0,0 +1,138 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2023 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/>.
+
+# Check that GDB can detect when the remote target and GDB running on
+# the same host and can automatically choose to ignore the 'target:'
+# prefix in the sysroot.
+
+load_lib gdbserver-support.exp
+
+standard_testfile
+
+require allow_gdbserver_tests
+
+# This test requires that GDB and the remote be on the same host.
+require {!is_remote host}
+
+if { [build_executable "failed to prepare" ${testfile}] } {
+    return -1
+}
+
+# Call after connecting to a remote target, return true if the
+# GDB/GDBserver combo supports the qMachineId packet, otherwise,
+# return false.
+
+proc supports_qMachineId {} {
+    gdb_test_multiple "show remote fetch-machine-id-packet" "" {
+	-re -wrap "Support for the 'qMachineId' packet on the current remote target is \"auto\", currently disabled\\." {
+	    return false
+	}
+	-re -wrap "Support for the 'qMachineId' packet on the current remote target is \"off\"\\." {
+	    return false
+	}
+	-re -wrap "Support for the 'qMachineId' packet on the current remote target is \"auto\", currently enabled\\." {
+	    return true
+	}
+	-re -wrap "Support for the 'qMachineId' packet on the current remote target is \"on\"\\." {
+	    return true
+	}
+    }
+}
+
+clean_restart $binfile
+
+# This test is all about ignoring the 'target:' prefix in the sysroot.
+# If the test is being run with some board that sets a specific
+# sysroot value then this test might now work, so just don't try.
+gdb_test_multiple "show sysroot" "" {
+    -re -wrap "The current system root is \"target:\"\\." {
+	pass $gdb_test_name
+    }
+    -re -wrap "The current system root is \"\"\\." {
+	gdb_test_no_output "set sysroot target:"
+	send_gdb "show sysroot\n"
+	exp_continue
+    }
+    -re -wrap "" {
+	# Any other sysroot setting is not going to work with this
+	# test.
+	unsupport "unsupported sysroot setting"
+	return
+    }
+}
+
+# Start a gdbserver and connect to it from GDB.  Check to see if GDB
+# downloads any files from the remote target.
+#
+# FS_MODE is used to set 'remote local-filesystem'.
+#
+# Depending on whether the target is remote or local, and the value of
+# FS_MODE, this test might, or might not, expect to see files being
+# downloaded.
+proc test_for_remote_read { fs_mode } {
+
+    clean_restart $::binfile
+
+    # We already checked that the sysroot setting either is already
+    # 'target:', or is '', but can be set to 'target:', so at this
+    # point we just force the sysroot setting.
+    gdb_test_no_output "set sysroot target:"
+
+    # Setup whether GDB thinks the remote filesystem is local or not.
+    gdb_test_no_output "set remote local-filesystem $fs_mode"
+
+    # Disconnect in case we are using extended-remote and are already
+    # connected.
+    gdb_test "disconnect" ".*"
+
+    # Start the gdbserver.
+    set res [gdbserver_spawn ""]
+    set gdbserver_protocol [lindex $res 0]
+    set gdbserver_gdbport [lindex $res 1]
+
+    # Connect to gdbserver.
+    gdb_test_multiple "target $gdbserver_protocol $gdbserver_gdbport" \
+	"connect to gdbserver" {
+	-re -wrap "Reading \[^\r\n\]+ from remote target\.\.\.\r\n.*" {
+	    if {$fs_mode eq "off"
+		|| [is_remote target]
+		|| ![supports_qMachineId]} {
+		pass $gdb_test_name
+	    } else {
+		fail $gdb_test_name
+	    }
+	}
+	-re -wrap "" {
+	    if {![is_remote target]
+		&& $fs_mode ne "off"} {
+		pass $gdb_test_name
+	    } else {
+		fail $gdb_test_name
+	    }
+	}
+    }
+}
+
+foreach_with_prefix fs_mode { auto on off } {
+    if {$fs_mode eq "on" && [is_remote target]} {
+	# It doesn't make sense to force GDB to try and use the local
+	# filesystem when the target is actually remote.
+	continue
+    }
+
+    test_for_remote_read $fs_mode
+}
diff --git a/gdb/testsuite/gdb.server/sysroot.exp b/gdb/testsuite/gdb.server/sysroot.exp
index 35f15d6c60e..7662787f705 100644
--- a/gdb/testsuite/gdb.server/sysroot.exp
+++ b/gdb/testsuite/gdb.server/sysroot.exp
@@ -42,53 +42,61 @@ if { ( ![is_remote host] && ![is_remote target ] )
 }
 lappend modes "remote"
 
-foreach_with_prefix sysroot $modes {
-    global srcdir
-    global subdir
-    global binfile
-
-    if { $sysroot == "local" } {
-	set sysroot_command "/"
-	set reading_symbols "Reading symbols from $host_binfile..."
-	set timeout_factor 1
-    } else {
-	set sysroot_command "target:"
-	set reading_symbols "Reading .*$target_binfile from remote target..."
-	set timeout_factor 5
-    }
+foreach_with_prefix fs_mode $modes {
+    foreach_with_prefix sysroot $modes {
+	global srcdir
+	global subdir
+	global binfile
 
-    # Reading debug info from the remote target can take a bit of time, so
-    # increase the timeout in that case.
-    with_timeout_factor $timeout_factor {
-	# Restart GDB.
-	clean_restart
+	if { $sysroot == "local" || $fs_mode == "local" } {
+	    set sysroot_command "/"
+	    set reading_symbols "Reading symbols from $host_binfile..."
+	    set timeout_factor 1
+	} else {
+	    set sysroot_command "target:"
+	    set reading_symbols "Reading .*$target_binfile from remote target..."
+	    set timeout_factor 5
+	}
 
-	# Make sure we're disconnected, in case we're testing with an
-	# extended-remote board, therefore already connected.
-	gdb_test "disconnect" ".*"
+	# Reading debug info from the remote target can take a bit of time, so
+	# increase the timeout in that case.
+	with_timeout_factor $timeout_factor {
+	    # Restart GDB.
+	    clean_restart
 
-	# Start GDBserver.
-	set res [gdbserver_start "" $target_binfile]
-	set gdbserver_protocol [lindex $res 0]
-	set gdbserver_gdbport [lindex $res 1]
+	    if { $fs_mode == "local" } {
+		gdb_test_no_output "set remote local-filesystem on"
+	    } else {
+		gdb_test_no_output "set remote local-filesystem off"
+	    }
 
-	# Set the sysroot.
-	gdb_test_no_output "set sysroot $sysroot_command"
+	    # Make sure we're disconnected, in case we're testing with an
+	    # extended-remote board, therefore already connected.
+	    gdb_test "disconnect" ".*"
 
-	# Connect to gdbserver, making sure GDB reads in the binary correctly.
-	set test "connect to remote and read binary"
-	if {[gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport $reading_symbols] == 0} {
-	    pass $test
-	} else {
-	    fail $test
-	}
+	    # Start GDBserver.
+	    set res [gdbserver_start "" $target_binfile]
+	    set gdbserver_protocol [lindex $res 0]
+	    set gdbserver_gdbport [lindex $res 1]
+
+	    # Set the sysroot.
+	    gdb_test_no_output "set sysroot $sysroot_command"
 
-	gdb_breakpoint main
-	gdb_test "continue" "Breakpoint $decimal.* main.*" "continue to main"
+	    # Connect to gdbserver, making sure GDB reads in the binary correctly.
+	    set test "connect to remote and read binary"
+	    if {[gdb_target_cmd $gdbserver_protocol $gdbserver_gdbport $reading_symbols] == 0} {
+		pass $test
+	    } else {
+		fail $test
+	    }
 
-	# Test that we can stop inside a library.
-	gdb_breakpoint printf
-	gdb_test "continue" "Breakpoint $decimal.* (__)?printf.*" \
-	    "continue to printf"
+	    gdb_breakpoint main
+	    gdb_test "continue" "Breakpoint $decimal.* main.*" "continue to main"
+
+	    # Test that we can stop inside a library.
+	    gdb_breakpoint printf
+	    gdb_test "continue" "Breakpoint $decimal.* (__)?printf.*" \
+		"continue to printf"
+	}
     }
 }
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 09/10] gdb: use exec_file with remote targets when possible
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
                     ` (7 preceding siblings ...)
  2023-08-25 15:34   ` [PATCHv2 08/10] gdb: remote filesystem can be local to GDB in some cases Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  2023-08-25 15:34   ` [PATCHv2 10/10] gdb: remove the get_remote_exec_file function Andrew Burgess
  9 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess

This commit allows GDB to make use of the file set with the 'file'
command when starting a new inferior on an extended-remote target.
There are however some restrictions.

If the user has used 'set remote exec-file', then this is always used
in preference to the file set with the 'file' command.

Similarly, if the qDefaultExecAndArgs packet has succeeded, and GDB
knows that the remote target has a default executable set, then this
will be used in preference to the file set with the 'file' command;
this preserves GDB's existing behaviour.

And, GDB can only use the file set with the 'file' command if it
believes that both GDB and the remote target will both be able to
access this file.  This means that either, the sysroot has been
updated by the user and no longer contains a 'target:' prefix, or, the
the remote_target::filesystem_is_local function returns true (see the
implementation of that function for details of when this can happen).

If all of these conditions are met, then GDB will use the file set
with the 'file' command when starting a new inferior, in all other
cases, GDB will use the file set with 'set remote exec-file'.
---
 gdb/remote.c                         |  52 ++++++++++-
 gdb/testsuite/gdb.server/ext-run.exp | 131 +++++++++++++++++++++------
 2 files changed, 154 insertions(+), 29 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index d5b758c888d..87ec0562bfb 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1396,6 +1396,9 @@ class extended_remote_target final : public remote_target
 
   void post_attach (int) override;
   bool supports_disable_randomization () override;
+
+private:
+  std::string get_exec_file_for_create_inferior (const char *exec_file);
 };
 
 struct stop_reply : public notif_event
@@ -11031,6 +11034,51 @@ directory: %s"),
     }
 }
 
+/* Return the path for the executable that GDB should ask the remote target
+   to use when starting an inferior.  EXEC_FILE is GDB's idea of the
+   current inferior (e.g. set with the 'file' command), however, this is
+   often not the right thing for a remote target to use, which is why GDB
+   also has the 'set remote exec-file' command.
+
+   If the user has 'set remote exec-file', then this function returns the
+   path the user set.  Otherwise, if it is possible that GDB and the remote
+   target can see the same filesystem, this function will return
+   EXEC_FILE.  */
+
+std::string
+extended_remote_target::get_exec_file_for_create_inferior
+  (const char *exec_file)
+{
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+
+  /* If INFO.SECOND is remote_exec_source::DEFAULT_VALUE, then this
+     indicates that the remote target has failed to inform us if it has a
+     default-executable set or not, maybe the remote doesn't support the
+     qDefaultExecAndArgs packet?  In this case, we pass the empty string to
+     the remote and expect it to use the default executable (if one is
+     set).  */
+  if (exec_file != nullptr
+      && info.second == remote_exec_source::UNSET_VALUE)
+    {
+      /* If the sysroot has been set to something that does not have a
+	 'target:' prefix then GDB will not be trying to fetch files from
+	 the remote anyway, so we can assume that the executable is visible
+	 to both the remote and GDB.
+
+	 Otherwise, if GDB is able to determine that the remote filesystem
+	 is actually local, then we are still OK to use the local
+	 executable.  */
+      if (!is_target_filename (gdb_sysroot)
+	  || target_filesystem_is_local ())
+	return exec_file;
+    }
+
+  /* The user has set the remote exec-file, or GDB doesn't think the remote
+     target and GDB can see the same filesystem.  */
+  return info.first;
+}
+
 /* In the extended protocol we want to be able to do things like
    "run" and have them basically work as expected.  So we need
    a special create_inferior function.  We support changing the
@@ -11045,7 +11093,9 @@ extended_remote_target::create_inferior (const char *exec_file,
   int run_worked;
   char *stop_reply;
   struct remote_state *rs = get_remote_state ();
-  const std::string &remote_exec_file = get_remote_exec_file ();
+
+  std::string remote_exec_file
+    = get_exec_file_for_create_inferior (exec_file);
 
   /* If running asynchronously, register the target file descriptor
      with the event loop.  */
diff --git a/gdb/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp
index 59ead666d7b..3a4bf3e7eb1 100644
--- a/gdb/testsuite/gdb.server/ext-run.exp
+++ b/gdb/testsuite/gdb.server/ext-run.exp
@@ -30,43 +30,118 @@ if {[build_executable $testfile.exp $testfile $srcfile debug] == -1} {
 # allow_xml_test must be called while gdb is not running.
 set do_xml_test [allow_xml_test]
 
-save_vars { GDBFLAGS } {
-    # If GDB and GDBserver are both running locally, set the sysroot to avoid
-    # reading files via the remote protocol.
-    if { ![is_remote host] && ![is_remote target] } {
-	set GDBFLAGS "$GDBFLAGS -ex \"set sysroot\""
+# This is used as an override function.
+proc do_nothing {} { return 0 }
+
+# Start an exetended-remote gdbserver, connect to it, and then use
+# 'run' to start an inferior.
+#
+# If CLEAR_SYSROOT is true then the 'set sysroot' command is issued,
+# clearing the sysroot, otherwise the sysroot is left unchanged.
+#
+# If SET_REMOTE_EXEC is true then the 'set remote-exec ...' command is
+# issued to point GDB at the executable on the target (after copying
+# the executable over).  Otherwise, we rely on GDB and gdbserver being
+# able to see the same filesystem, remote exec-file is not set, and
+# GDB will just use the path to the executable.
+proc do_test { clear_sysroot set_remote_exec fetch_exec_and_args } {
+    save_vars { ::GDBFLAGS } {
+	if { $clear_sysroot } {
+	    set ::GDBFLAGS "$::GDBFLAGS -ex \"set sysroot\""
+	}
+
+	clean_restart $::binfile
     }
 
-    clean_restart $binfile
-}
+    # Disable, or enable, use of the qDefaultExecAndArgs packet.
+    gdb_test "set remote fetch-exec-and-args-packet ${fetch_exec_and_args}" \
+	".*"
 
-# Make sure we're disconnected, in case we're testing with an
-# extended-remote board, therefore already connected.
-gdb_test "disconnect" ".*"
+    # Make sure we're disconnected, in case we're testing with an
+    # extended-remote board, therefore already connected.
+    gdb_test "disconnect" ".*"
 
-set target_exec [gdbserver_download_current_prog]
-gdbserver_start_extended
+    gdbserver_start_extended
 
-gdb_test_no_output "set remote exec-file $target_exec" "set remote exec-file"
+    gdb_test "show remote exec-file" \
+	"The remote exec-file is \"\"\[^\r\n\]*" \
+	"check remote exec-file is unset"
 
-gdb_breakpoint main
-gdb_test "run" "Breakpoint.* main .*" "continue to main"
+    if { $set_remote_exec } {
+	set target_exec [gdbserver_download_current_prog]
+	gdb_test_no_output "set remote exec-file $target_exec" \
+	    "set remote exec-file"
+    }
 
-if { [istarget *-*-linux*] } {
-    # On Linux, gdbserver can also report the list of processes.
-    # But only if xml support is compiled in.
-    if { $do_xml_test } {
-	# This is done in a way to avoid the timeout that can occur from
-	# applying .* regexp to large output.
-	gdb_test_sequence "info os processes" "get process list" \
-	    { "pid +user +command" "1 +root +\[/a-z\]*(init|systemd)" }
+    gdb_breakpoint main
+    gdb_test_multiple "run" "continue to main" {
+	-re -wrap "Breakpoint.* main .*" {
+	    pass $gdb_test_name
+	}
+	-re -wrap "Running the default executable on the remote target failed; try \"set remote exec-file\"." {
+
+	    # If 'set remote exec-file' has been used then we should
+	    # not get here.
+	    gdb_assert {!$set_remote_exec} \
+		"confirm remote exec-file is not set"
+
+	    if {!$fetch_exec_and_args} {
+		# We deliberately disabled GDB's ability to know that
+		# the remote doesn't have a default executable set (by
+		# disabling the qDefaultExecAndArgs packet).  We got
+		# the result we expected, but the inferior is not
+		# running, so we're done with this phase of testing.
+		pass $gdb_test_name
+		return
+	    } else {
+		# This means that the qMachineId packet is not supported,
+		# and GDB could not tell if it is running on the same host
+		# as gdbserver.  Confirm that qMachineId is showing as
+		# disabled, and then move onto the next testing mode.
+		gdb_test "show remote fetch-machine-id-packet" \
+		    "Support for the 'qMachineId' packet on the current remote target is \"auto\", currently disabled\\." \
+		    "confirm qMachineId packet is not supported"
+		return
+	    }
+	}
+    }
+
+    if { [istarget *-*-linux*] } {
+	# On Linux, gdbserver can also report the list of processes.
+	# But only if xml support is compiled in.
+	if { $::do_xml_test } {
+	    # This is done in a way to avoid the timeout that can occur from
+	    # applying .* regexp to large output.
+	    gdb_test_sequence "info os processes" "get process list" \
+		{ "pid +user +command" "1 +root +\[/a-z\]*(init|systemd)" }
+	}
     }
-}
 
-gdb_test "kill" "" "kill" "Kill the program being debugged. .y or n. " "y"
+    gdb_test "kill" "" "kill" "Kill the program being debugged. .y or n. " "y"
 
-gdb_load $binfile
-gdb_test "monitor help" "The following monitor commands.*" \
+    gdb_load $::binfile
+    gdb_test "monitor help" "The following monitor commands.*" \
         "load new file without any gdbserver inferior"
 
-gdb_test_no_output "monitor exit"
+    gdb_test_no_output "monitor exit"
+}
+
+set clear_sysroot_modes { false }
+set set_remote_exec_modes { true }
+if {![is_remote target] && ![is_remote host]} {
+    lappend set_remote_exec_modes false
+    lappend clear_sysroot_modes true
+}
+
+# This override prevents GDB from automatically setting the 'remote
+# exec-file' when using the extended-remote protocol.  If we want the
+# exec-file set, then this test takes care of it.
+with_override extended_gdbserver_load_last_file do_nothing {
+    foreach_with_prefix clear_sysroot $clear_sysroot_modes {
+	foreach_with_prefix set_remote_exec $set_remote_exec_modes {
+	    foreach_with_prefix fetch_exec_and_args { on off } {
+		do_test $clear_sysroot $set_remote_exec $fetch_exec_and_args
+	    }
+	}
+    }
+}
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCHv2 10/10] gdb: remove the get_remote_exec_file function
  2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
                     ` (8 preceding siblings ...)
  2023-08-25 15:34   ` [PATCHv2 09/10] gdb: use exec_file with remote targets when possible Andrew Burgess
@ 2023-08-25 15:34   ` Andrew Burgess
  9 siblings, 0 replies; 46+ messages in thread
From: Andrew Burgess @ 2023-08-25 15:34 UTC (permalink / raw)
  To: gdb-patches; +Cc: Andrew Burgess, Mark Wielaard

After the previous commit, the get_remote_exec_file function is only
used in one place, so inline it and delete get_remote_exec_file.

There should be no user visible changes after this commit.

Reviewed-by: Mark Wielaard <mark@klomp.org>
---
 gdb/remote.c | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 87ec0562bfb..c483e060966 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1864,16 +1864,6 @@ remote_target::get_remote_state ()
   return &m_remote_state;
 }
 
-/* Fetch the remote exec-file from the current program space.  */
-
-static const std::string &
-get_remote_exec_file ()
-{
-  const remote_exec_file_info &info
-    = get_remote_exec_file_info (current_program_space);
-  return info.first;
-}
-
 /* Set the remote exec file for PSPACE.  */
 
 static void
@@ -1900,7 +1890,9 @@ set_remote_exec_file_cb (const std::string &filename)
 static const std::string &
 get_remote_exec_file_cb ()
 {
-  return get_remote_exec_file ();
+  const remote_exec_file_info &info
+    = get_remote_exec_file_info (current_program_space);
+  return info.first;
 }
 
 /* Implement the "show remote exec-file" command.  */
-- 
2.25.4


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCHv2 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet
  2023-08-25 15:34   ` [PATCHv2 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
@ 2023-08-26  6:46     ` Eli Zaretskii
  0 siblings, 0 replies; 46+ messages in thread
From: Eli Zaretskii @ 2023-08-26  6:46 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> From: Andrew Burgess <aburgess@redhat.com>
> Cc: Andrew Burgess <aburgess@redhat.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Fri, 25 Aug 2023 16:34:37 +0100
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index c4b1f7a7e3b..d78929c1398 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -105,6 +105,14 @@
>    'inferior' keyword with either the 'thread' or 'task' keywords when
>    creating a breakpoint.
>  
> +* When connecting to a remote server, if the server supports the
> +  qDefaultExecAndArgs packet, then GDB will copy the argument string
> +  from the server and update the 'args' setting, as if 'set args ...'
> +  had been used.  This means that the arguments are visible from GDB
> +  using 'show args', and that, if using the extended-remote protocol,
> +  subsequent runs of the inferior will use the same arguments as the
> +  first run.
> +
>  * New commands
>  
>  set debug breakpoint on|off
> @@ -278,6 +286,13 @@ info main
>       inferior specific, then this field contains None.  This field can
>       be written too.
>  
> +* New remote packets
> +
> +qDefaultExecAndArgs
> +  This packet returns the executable filename and argument string with
> +  which the server was started.  If no such information was given to
> +  the server then this is reflected in the reply.
> +
>  *** Changes in GDB 13
>  

This part is OK.

> +@item S;@var{PP@dots{}};@var{AA@dots{}}
> +The program filename provided to the remote server when it started was
> +@var{PP@dots{}}, which is a hex encoded string, and the argument
> +string passed to the program when started by the server was
> +@var{AA@dots{}}, which is also a hex encoded string.

Here, PP and AA don't stand for literal "PP" and "AA" strings, they
stand for 2 or more hex-encoded characters, right?  In that case, PP
and AA should be in lower-case.  Moreover, I question the need to use
such cryptic names; why not say something like this instead:

  @item S;@var{prog};@var{args}
  The program filename provided to the remote server when it started was
  @var{prog}, which is a hex encoded string of zero or more characters,
  and the argument string passed to the program when started by the
  server was @var{args}, which is also a hex encoded string.

IOW, what is the significance of using two characters in PP and AA?

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCHv2 08/10] gdb: remote filesystem can be local to GDB in some cases
  2023-08-25 15:34   ` [PATCHv2 08/10] gdb: remote filesystem can be local to GDB in some cases Andrew Burgess
@ 2023-08-26  6:49     ` Eli Zaretskii
  0 siblings, 0 replies; 46+ messages in thread
From: Eli Zaretskii @ 2023-08-26  6:49 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> From: Andrew Burgess <aburgess@redhat.com>
> Cc: Andrew Burgess <aburgess@redhat.com>,
> 	Eli Zaretskii <eliz@gnu.org>
> Date: Fri, 25 Aug 2023 16:34:41 +0100
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 2b1f265f5b8..36ca93398f6 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -113,6 +113,13 @@
>    subsequent runs of the inferior will use the same arguments as the
>    first run.
>  
> +* For remote targets that support the qMachineId packet, if GDB
> +  believes that the remote target is on the same host as GDB itself,
> +  and that GDB can access the same files as the remote target, then
> +  GDB will take advantage of this to access files directly, rather
> +  than copying them from the remote target.  This behaviour can be
> +  disable using the new command 'set remote local-filesystem off'.
     ^^^^^^^
"disabled"

> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 6b417ab85d8..bc41d3dbedd 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo

This part is okay.

Thanks.

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCHv2 07/10] gdb: add qMachineId packet
  2023-08-25 15:34   ` [PATCHv2 07/10] gdb: add qMachineId packet Andrew Burgess
@ 2023-08-26  6:54     ` Eli Zaretskii
  0 siblings, 0 replies; 46+ messages in thread
From: Eli Zaretskii @ 2023-08-26  6:54 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> Cc: Andrew Burgess <aburgess@redhat.com>
> Date: Fri, 25 Aug 2023 16:34:40 +0100
> From: Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org>
> 
>  gdb/Makefile.in            |   3 +
>  gdb/NEWS                   |   5 ++
>  gdb/configure.nat          |   2 +-
>  gdb/doc/gdb.texinfo        | 126 +++++++++++++++++++++++++++
>  gdb/linux-nat.c            |  42 +++++++++
>  gdb/nat/linux-machine-id.c |  85 +++++++++++++++++++
>  gdb/nat/linux-machine-id.h |  75 ++++++++++++++++
>  gdb/nat/linux-namespaces.c |  13 +++
>  gdb/nat/linux-namespaces.h |  14 +++
>  gdb/remote-machine-id.c    |  69 +++++++++++++++
>  gdb/remote-machine-id.h    | 108 ++++++++++++++++++++++++
>  gdb/remote.c               | 169 +++++++++++++++++++++++++++++++++++++
>  gdbserver/Makefile.in      |   1 +
>  gdbserver/configure.srv    |   2 +-
>  gdbserver/linux-low.cc     |  24 ++++++
>  gdbserver/linux-low.h      |   2 +
>  gdbserver/server.cc        |  12 +++
>  gdbserver/target.cc        |   8 ++
>  gdbserver/target.h         |   9 ++
>  19 files changed, 767 insertions(+), 2 deletions(-)
>  create mode 100644 gdb/nat/linux-machine-id.c
>  create mode 100644 gdb/nat/linux-machine-id.h
>  create mode 100644 gdb/remote-machine-id.c
>  create mode 100644 gdb/remote-machine-id.h

Thanks.

> diff --git a/gdb/NEWS b/gdb/NEWS
> index d78929c1398..2b1f265f5b8 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -293,6 +293,11 @@ qDefaultExecAndArgs
>    which the server was started.  If no such information was given to
>    the server then this is reflected in the reply.
>  
> +qMachineId
> +  This packet returns an identifier that allows GDB to determine if
> +  the remote server and GDB are running on the same host, and can see
> +  the same filesystem.
> +
>  *** Changes in GDB 13

This part is OK.

> +The @var{mnt-id} and @var{user-id} are either the character @samp{-}
> +if the particular namespace is not support on this host, or is the
                                      ^^^^^^^
"supported"

> +When matching a machine-id @value{GDBN} first checks the reply for a
> +master key that it understands.  If a suitable key is found
> +@value{GDBN} checks that the value for the master key matches its
> +value for the master key.  If the master key value matches, then
> +@value{GDBN} checks all the remaining @var{key}/@var{value} pairs;
> +each @var{key} must be known secondary key associated with the
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
"must be a known secondary key"

Reviewed-By: Eli Zaretskii <eliz@gnu.org>

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet
  2023-08-16 15:55 ` [PATCH 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
  2023-08-16 16:36   ` Eli Zaretskii
@ 2023-08-28 15:35   ` Tom Tromey
  1 sibling, 0 replies; 46+ messages in thread
From: Tom Tromey @ 2023-08-28 15:35 UTC (permalink / raw)
  To: Andrew Burgess via Gdb-patches; +Cc: Andrew Burgess

>>>>> "Andrew" == Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org> writes:

Andrew> +@item qDefaultExecAndArgs
...
Andrew> +@item S;@var{PP@dots{}};@var{AA@dots{}}
Andrew> +The program filename provided to the remote server when it started was
Andrew> +@var{PP@dots{}}, which is a hex encoded string, and the argument
Andrew> +string passed to the program when started by the server was
Andrew> +@var{AA@dots{}}, which is also a hex encoded string.

This should probably discuss any quoting that should or should not be
applied to the strings.  That is, how are multiple arguments handled?
Is it spaces?  If so then how is a space passed in an argument?

It would maybe be better if the arguments were a vector of strings, each
to be taken literally by gdb.  Then gdb can apply the quoting it likes.

Tom

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-08-16 15:55 ` [PATCH 07/10] gdb: add qMachineId packet Andrew Burgess
                     ` (2 preceding siblings ...)
  2023-08-23  9:24   ` Mark Wielaard
@ 2023-08-28 16:06   ` Tom Tromey
  3 siblings, 0 replies; 46+ messages in thread
From: Tom Tromey @ 2023-08-28 16:06 UTC (permalink / raw)
  To: Andrew Burgess via Gdb-patches; +Cc: Andrew Burgess

>>>>> "Andrew" == Andrew Burgess via Gdb-patches <gdb-patches@sourceware.org> writes:

Andrew>   linux-boot-id - this returns the value from the file
Andrew>   /proc/sys/kernel/random/boot_id, which, if I understand correctly,
Andrew>   should be unique(ish) for each boot of each machine, and

At one point I thought I found docs saying this should be kept
confidential and in particular not sent over the network.  I can't find
those any more but I did find them for /etc/machine-id.

When we discussed this on irc, Pedro had a different idea, based on
using the existing remote file operations: write a random number /
identifying string to a local file (say, something in /tmp or maybe
gdb's cache directory), then ask the remote to read it.  If the read
succeeds and the result is identical, assume the machines are the same.

Tom

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-08-25 15:01       ` Eli Zaretskii
@ 2023-09-26 14:42         ` Andrew Burgess
  2023-09-29  7:45           ` Eli Zaretskii
  0 siblings, 1 reply; 46+ messages in thread
From: Andrew Burgess @ 2023-09-26 14:42 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

Eli Zaretskii <eliz@gnu.org> writes:

>> From: Andrew Burgess <aburgess@redhat.com>
>> Cc: gdb-patches@sourceware.org
>> Date: Fri, 25 Aug 2023 15:49:00 +0100
>> 
>> Eli Zaretskii <eliz@gnu.org> writes:
>> 
>> > Btw, when a sentence starts with "See @ref", you could simplify by
>> > using @xref, as that is its main purpose.
>> 
>> When generating the 'info' docs @xref doesn't add the 'See' prefix, but
>> when generating the pdf 'See' is added.  I guess this explains why 'See
>> @ref' is common throughout the GDB manual.
>
> No, @xref always produces "See".  Where did you see it without "See"?

Sorry Eli, I missed your reply here.

I am using GNU texinfo 6.7, and I don't see @xref adding "See" when
generating 'info' output format.  When producing 'pdf' output I do see
the "See" prefix.

This is visible to me in the current GDB info page(s).

For example, this paragraph in gdb.texinfo:

  If the architecture supports memory tagging, the @code{print} command will
  display pointer/memory tag mismatches if what is being printed is a pointer
  or reference type. @xref{Memory Tagging}.

Is rendered like this in 'info' format:

     If the architecture supports memory tagging, the 'print' command will
  display pointer/memory tag mismatches if what is being printed is a
  pointer or reference type.  *Note Memory Tagging::.

I guess it has the '*Note ' prefix instead, but in contrast, this
partial paragraph from gdb.texinfo:

  This command allows to control the information printed when
  the debugger prints a frame.  See @ref{Frames}, @ref{Backtrace},
  for a general explanation about frames and frame information.

Is rendered like this in 'info' format:

  This command allows to control the information printed when the
  debugger prints a frame.  See *note Frames::, *note Backtrace::,
  for a general explanation about frames and frame information.

Which I think is nicer, for me starting the sentence with 'Note' doesn't
seem as friendly.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH 07/10] gdb: add qMachineId packet
  2023-09-26 14:42         ` Andrew Burgess
@ 2023-09-29  7:45           ` Eli Zaretskii
  0 siblings, 0 replies; 46+ messages in thread
From: Eli Zaretskii @ 2023-09-29  7:45 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: gdb-patches

> From: Andrew Burgess <aburgess@redhat.com>
> Cc: gdb-patches@sourceware.org
> Date: Tue, 26 Sep 2023 15:42:26 +0100
> 
> Eli Zaretskii <eliz@gnu.org> writes:
> 
> > No, @xref always produces "See".  Where did you see it without "See"?
> 
> Sorry Eli, I missed your reply here.
> 
> I am using GNU texinfo 6.7, and I don't see @xref adding "See" when
> generating 'info' output format.  When producing 'pdf' output I do see
> the "See" prefix.
> 
> This is visible to me in the current GDB info page(s).
> 
> For example, this paragraph in gdb.texinfo:
> 
>   If the architecture supports memory tagging, the @code{print} command will
>   display pointer/memory tag mismatches if what is being printed is a pointer
>   or reference type. @xref{Memory Tagging}.
> 
> Is rendered like this in 'info' format:
> 
>      If the architecture supports memory tagging, the 'print' command will
>   display pointer/memory tag mismatches if what is being printed is a
>   pointer or reference type.  *Note Memory Tagging::.
> 
> I guess it has the '*Note ' prefix instead, but in contrast

Yes, in Info format, @xref produces a capitalized "Note".  (Emacs
replaces that on display with "See", so I tend to forget about the
actual conversion, sorry about that).  And a capitalize "Note" is also
inappropriate in the middle of a sentence.

>   This command allows to control the information printed when
>   the debugger prints a frame.  See @ref{Frames}, @ref{Backtrace},
>   for a general explanation about frames and frame information.
> 
> Is rendered like this in 'info' format:
> 
>   This command allows to control the information printed when the
>   debugger prints a frame.  See *note Frames::, *note Backtrace::,
>   for a general explanation about frames and frame information.
> 
> Which I think is nicer, for me starting the sentence with 'Note' doesn't
> seem as friendly.

It's your personal preference, so I don't want to argue.  It is
suboptimal from my POV, which is why you will almost never see that in
the Emacs manuals.

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2023-09-29  7:46 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-16 15:54 [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Andrew Burgess
2023-08-16 15:54 ` [PATCH 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
2023-08-23  9:30   ` Alexandra Petlanova Hajkova
2023-08-16 15:54 ` [PATCH 02/10] gdb: improve how 'remote exec-file' is stored and accessed Andrew Burgess
2023-08-23  8:44   ` Alexandra Petlanova Hajkova
2023-08-16 15:54 ` [PATCH 03/10] gdb: improve show text and help text for 'remote exec-file' Andrew Burgess
2023-08-23 11:36   ` Mark Wielaard
2023-08-24  8:56   ` Alexandra Petlanova Hajkova
2023-08-16 15:55 ` [PATCH 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
2023-08-16 16:36   ` Eli Zaretskii
2023-08-28 15:35   ` Tom Tromey
2023-08-16 15:55 ` [PATCH 05/10] gdb: detect when gdbserver has no default executable set Andrew Burgess
2023-08-16 15:55 ` [PATCH 06/10] gdb: make use of is_target_filename Andrew Burgess
2023-08-23 13:35   ` Mark Wielaard
2023-08-16 15:55 ` [PATCH 07/10] gdb: add qMachineId packet Andrew Burgess
2023-08-16 16:34   ` Eli Zaretskii
2023-08-25 14:49     ` Andrew Burgess
2023-08-25 15:01       ` Eli Zaretskii
2023-09-26 14:42         ` Andrew Burgess
2023-09-29  7:45           ` Eli Zaretskii
2023-08-22  2:39   ` Thiago Jung Bauermann
2023-08-23  9:24   ` Mark Wielaard
2023-08-23 11:36     ` Andrew Burgess
2023-08-28 16:06   ` Tom Tromey
2023-08-16 15:55 ` [PATCH 08/10] gdb: remote filesystem can be local to GDB in some cases Andrew Burgess
2023-08-16 16:40   ` Eli Zaretskii
2023-08-16 15:55 ` [PATCH 09/10] gdb: use exec_file with remote targets when possible Andrew Burgess
2023-08-16 15:55 ` [PATCH 10/10] gdb: remote the get_remote_exec_file function Andrew Burgess
2023-08-23 13:42   ` Mark Wielaard
2023-08-22 10:41 ` [PATCH 00/10] Improve GDB/gdbserver experience when using a local gdbserver Alexandra Petlanova Hajkova
2023-08-23 14:32 ` Mark Wielaard
2023-08-23 15:26   ` Andrew Burgess
2023-08-25 15:34 ` [PATCHv2 " Andrew Burgess
2023-08-25 15:34   ` [PATCHv2 01/10] gdb: have remote_target::extended_remote_run take the exec filename Andrew Burgess
2023-08-25 15:34   ` [PATCHv2 02/10] gdb: improve how 'remote exec-file' is stored and accessed Andrew Burgess
2023-08-25 15:34   ` [PATCHv2 03/10] gdb: improve show text and help text for 'remote exec-file' Andrew Burgess
2023-08-25 15:34   ` [PATCHv2 04/10] gdb/gdbserver: add new qDefaultExecAndArgs packet Andrew Burgess
2023-08-26  6:46     ` Eli Zaretskii
2023-08-25 15:34   ` [PATCHv2 05/10] gdb: detect when gdbserver has no default executable set Andrew Burgess
2023-08-25 15:34   ` [PATCHv2 06/10] gdb: make use of is_target_filename Andrew Burgess
2023-08-25 15:34   ` [PATCHv2 07/10] gdb: add qMachineId packet Andrew Burgess
2023-08-26  6:54     ` Eli Zaretskii
2023-08-25 15:34   ` [PATCHv2 08/10] gdb: remote filesystem can be local to GDB in some cases Andrew Burgess
2023-08-26  6:49     ` Eli Zaretskii
2023-08-25 15:34   ` [PATCHv2 09/10] gdb: use exec_file with remote targets when possible Andrew Burgess
2023-08-25 15:34   ` [PATCHv2 10/10] gdb: remove the get_remote_exec_file function Andrew Burgess

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).