public inbox for gdb@sourceware.org
 help / color / mirror / Atom feed
* Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode
@ 2022-03-09  7:25 Adrian Oltean
  2022-03-09 19:49 ` Tom Tromey
  0 siblings, 1 reply; 8+ messages in thread
From: Adrian Oltean @ 2022-03-09  7:25 UTC (permalink / raw)
  To: Tom Tromey, Adrian Oltean via Gdb

Hi Tom,

Unfortunately, I could not find ways to adapt all the other components I have around GDB to 
work with target-async mode enabled. It is what it is...

Regarding the 'H' packet, I had a second look in the RSP specs where it's stated that '0' indicates
an arbitrary process/thread. So, as you mentioned, I changed the GDB server code to simply
ensure there's a valid thread stopped marked as 'current'. The original implementation incorrectly
assimilated '0' with 'first thread' (always).

Thank you,
Adrian

-----Original Message-----
From: Tom Tromey <tom@tromey.com> 
Sent: Wednesday, March 9, 2022 12:10 AM
To: Adrian Oltean via Gdb <gdb@sourceware.org>
Cc: Adrian Oltean <adrian.oltean@nxp.com>
Subject: [EXT] Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode

Caution: EXT Email

>>>>> Adrian Oltean via Gdb <gdb@sourceware.org> writes:

> 1. I need to run GDB client in all-stop mode with target-async set to off.
> This seems to be a must in order to make some python scripts 
> (containing
> gdb.post_event('continue')) interact correctly with Eclipse CDT and 
> with some automation I have around the python scripts.

I think it would be better on the whole to figure out a way to make this work with target-async.  In the long run I'd prefer that gdb remove sync target wait entirely -- that the default be target-async everywhere.

> but I'd like to know if there's any chance I'll break any use case 
> with these changes.

I don't have the answer to that I'm afraid.

> Is the described behavior a known issue inside GDB? Or do I have to 
> change something in the server to correct the described behavior?

>   [remote] Sending packet: $Hg0#df

I think this request means to switch to any thread for subsequent non-continue/step requests.  So, ignoring it is almost the right thing to do, I think the idea is just to ensure that some stopped thread is selected.

Tom

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

* Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode
  2022-03-09  7:25 Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode Adrian Oltean
@ 2022-03-09 19:49 ` Tom Tromey
  2022-03-10  9:47   ` Pedro Alves
  0 siblings, 1 reply; 8+ messages in thread
From: Tom Tromey @ 2022-03-09 19:49 UTC (permalink / raw)
  To: Adrian Oltean; +Cc: Tom Tromey, Adrian Oltean via Gdb

Adrian> Unfortunately, I could not find ways to adapt all the other components I have around GDB to 
Adrian> work with target-async mode enabled. It is what it is...

I'm a little lost about this, because my mental model is that target
async shouldn't really change anything.  I think it is just an
implementation detail about where gdb waits for events -- is it in
wait() or in select()?

So, it would be interesting to learn what the differences are.  Perhaps
this can be patched up.

Adrian> Regarding the 'H' packet, I had a second look in the RSP specs
Adrian> where it's stated that '0' indicates an arbitrary
Adrian> process/thread. So, as you mentioned, I changed the GDB server
Adrian> code to simply ensure there's a valid thread stopped marked as
Adrian> 'current'. The original implementation incorrectly assimilated
Adrian> '0' with 'first thread' (always).

I'm a little surprised this matters as well, but anyway I think it's
fine to submit a patch like this.  Maybe others have some idea of what's
going on.

Tom

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

* Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode
  2022-03-09 19:49 ` Tom Tromey
@ 2022-03-10  9:47   ` Pedro Alves
  2022-03-10 10:02     ` [EXT] " Adrian Oltean
  0 siblings, 1 reply; 8+ messages in thread
From: Pedro Alves @ 2022-03-10  9:47 UTC (permalink / raw)
  To: Tom Tromey, Adrian Oltean; +Cc: Adrian Oltean via Gdb

On 2022-03-09 19:49, Tom Tromey wrote:
> Adrian> Unfortunately, I could not find ways to adapt all the other components I have around GDB to 
> Adrian> work with target-async mode enabled. It is what it is...
> 
> I'm a little lost about this, because my mental model is that target
> async shouldn't really change anything.  I think it is just an
> implementation detail about where gdb waits for events -- is it in
> wait() or in select()?
> 
> So, it would be interesting to learn what the differences are.  Perhaps
> this can be patched up.
> 
> Adrian> Regarding the 'H' packet, I had a second look in the RSP specs
> Adrian> where it's stated that '0' indicates an arbitrary
> Adrian> process/thread. So, as you mentioned, I changed the GDB server
> Adrian> code to simply ensure there's a valid thread stopped marked as
> Adrian> 'current'. The original implementation incorrectly assimilated
> Adrian> '0' with 'first thread' (always).
> 
> I'm a little surprised this matters as well, but anyway I think it's
> fine to submit a patch like this.  Maybe others have some idea of what's
> going on.

I'm not sure that I get what Adrian is callilng "first thread", but I take
it to mean that the server was numbering threads starting at "0".
But, in the RSP, you can't use '0' as thread id, as it has a special meaning.
I.e., if the server is numbering threads like, 0,1,2,3,... then it will
run into problems, because when GDB says "Hg0", it doesn't mean "select
the first thread, the one with id "0".  The typical fix is to just start
numbering threads at 1 instead of 0, for example.

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

* RE: [EXT] Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode
  2022-03-10  9:47   ` Pedro Alves
@ 2022-03-10 10:02     ` Adrian Oltean
  2022-03-10 11:30       ` Pedro Alves
  0 siblings, 1 reply; 8+ messages in thread
From: Adrian Oltean @ 2022-03-10 10:02 UTC (permalink / raw)
  To: Pedro Alves, Tom Tromey; +Cc: Adrian Oltean via Gdb

Hi guys,

Unfortunately, my knowledge on GDB's internals is quite limited... I'm leaving below two old mail threads, [1] and [2],
that describe the GDB's behavior in a custom use case and what makes it difficult to handle when target-async
is enabled. GDB 8.3 is referred in those threads but behavior is mostly the same in GDB 11.1. The key is the execution
of post_event("cont").

As far as 'H' packet handling is concerned, note that the GDB server I'm referring is a proprietary implementation
that is used for JTAG debugging. Threads are numbered from 1. However, in the old implementation "Hg0" was always
switching to thread 1 ("first thread") internally, even though some other core/thread was suspended. The original
implementation assumed "Hg0" must *always* switch to "first thread" (thread 1 in my case) but this doesn't seem
to be enforced in the RSP specs. I updated the code and assimilate "arbitrary" (from spec) as "if there's no suspended
thread/core internally selected, find a valid one".

Thank you,
Adrian

[1] https://sourceware.org/pipermail/gdb/2020-February/048395.html
[2] https://www.eclipse.org/lists/cdt-dev/msg35130.html

-----Original Message-----
From: Pedro Alves <pedro@palves.net> 
Sent: Thursday, March 10, 2022 11:48 AM
To: Tom Tromey <tom@tromey.com>; Adrian Oltean <adrian.oltean@nxp.com>
Cc: Adrian Oltean via Gdb <gdb@sourceware.org>
Subject: [EXT] Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode

Caution: EXT Email

On 2022-03-09 19:49, Tom Tromey wrote:
> Adrian> Unfortunately, I could not find ways to adapt all the other 
> Adrian> components I have around GDB to work with target-async mode enabled. It is what it is...
>
> I'm a little lost about this, because my mental model is that target 
> async shouldn't really change anything.  I think it is just an 
> implementation detail about where gdb waits for events -- is it in
> wait() or in select()?
>
> So, it would be interesting to learn what the differences are.  
> Perhaps this can be patched up.
>
> Adrian> Regarding the 'H' packet, I had a second look in the RSP specs 
> Adrian> where it's stated that '0' indicates an arbitrary 
> Adrian> process/thread. So, as you mentioned, I changed the GDB server 
> Adrian> code to simply ensure there's a valid thread stopped marked as 
> Adrian> 'current'. The original implementation incorrectly assimilated 
> Adrian> '0' with 'first thread' (always).
>
> I'm a little surprised this matters as well, but anyway I think it's 
> fine to submit a patch like this.  Maybe others have some idea of 
> what's going on.

I'm not sure that I get what Adrian is callilng "first thread", but I take it to mean that the server was numbering threads starting at "0".
But, in the RSP, you can't use '0' as thread id, as it has a special meaning.
I.e., if the server is numbering threads like, 0,1,2,3,... then it will run into problems, because when GDB says "Hg0", it doesn't mean "select the first thread, the one with id "0".  The typical fix is to just start numbering threads at 1 instead of 0, for example.

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

* Re: [EXT] Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode
  2022-03-10 10:02     ` [EXT] " Adrian Oltean
@ 2022-03-10 11:30       ` Pedro Alves
  2022-03-10 12:32         ` Pedro Alves
  0 siblings, 1 reply; 8+ messages in thread
From: Pedro Alves @ 2022-03-10 11:30 UTC (permalink / raw)
  To: Adrian Oltean, Tom Tromey; +Cc: Adrian Oltean via Gdb

On 2022-03-10 10:02, Adrian Oltean wrote:

> As far as 'H' packet handling is concerned, note that the GDB server I'm referring is a proprietary implementation
> that is used for JTAG debugging. Threads are numbered from 1. However, in the old implementation "Hg0" was always
> switching to thread 1 ("first thread") internally, even though some other core/thread was suspended. The original
> implementation assumed "Hg0" must *always* switch to "first thread" (thread 1 in my case) but this doesn't seem
> to be enforced in the RSP specs. I updated the code and assimilate "arbitrary" (from spec) as "if there's no suspended
> thread/core internally selected, find a valid one".

So going back to your original problem description, you said:

~~~~
2. File I/O (semihosting) when a multicore (ARMv8) target is involved seems
to exhibit an issue. My multicore debugging model assumes that each core is a
separate thread. If I have 'printf' calls executed on separate cores, I noticed
that the messages are duplicated on secondary cores (other than core 0). The
remote log snippet that highlights the root cause is pasted below. Long story
short, the 'H' packet that is sent to the server and instructs it to use
thread 0 for some further operations, actually causes troubles because breakpoint
handling and run control gets broken afterwards. Note that the 'Fwrite' is the
result of a 'printf' on a secondary core, thus instructing the server to use
thread 0 breaks lots of things inside the GDB server. The 'H' packet seems to
be sent as a result of the 'continue' action and it translates into a call to
'switch_to_no_thread'. If I ignore the 'H' packet, semihosting breakpoints and run
control are correctly handled, so the second 'Fwrite' RSP sequence would not
exist (this is the expected behavior). Is the described behavior a known issue
inside GDB? Or do I have to change something in the server to correct the
described behavior?

[remote] Sending packet: $vCont;c#a8
[remote] Received Ack
[remote] wait: enter
  [remote] Packet received: Fwrite,1,80026300,12
  [remote] Sending packet: $Hg0#df
  [remote] Received Ack
  [remote] Packet received: OK
  [remote] Sending packet: $m80026300,12#8f
  [remote] Received Ack
  [remote] Packet received: 48656c6c6f2066726f6d20436f726523370a
  [remote] Sending packet: $F12#a9
  [remote] Received Ack
  [remote] Packet received: Fwrite,1,80026300,12
  [remote] Sending packet: $m80026300,12#8f
  [remote] Received Ack
  [remote] Packet received: 48656c6c6f2066726f6d20436f726523370a
  [remote] Sending packet: $F12#a9
  [remote] Received Ack
  [remote] Packet received: T05thread:p1.8;core:7;
[remote] wait: exit
~~~~

The printf calls end up calling write under the hood, of course, and those
syscalls are intercepted and translated to File I/O calls into GDB.  That's the
"Fwrite,1,80026300,12" packets.  Now, GDB processes those and writes to its
own stdout.  What is written to its stdout is 12 bytes found at 0x80026300 in
the inferior memory, as per the Fwrite packet's arguments.

Depending on the kind of target object is being accessed, GDB makes sure
to select the proper remote thread, or at least any thread of the right remote process.
This is important for when the remote side understands the multi-process extensions,
for example, lest GDB reads something of the wrong process.

So in remote.c, you'll see calls to set_general_process, like this:

  /* Make sure the remote is pointing at the right process.  */
  set_general_process ();

... which translate to "HgPID.0" when multi-process extensions are supported by
the server, and "Hg0" when they're not.

For memory reads in particular, GDB instead makes sure that GDB's selected thread
is selected on the server side as well, not just any thread of the gdb-selected process.
This is done here:

 enum target_xfer_status
 remote_target::xfer_partial (enum target_object object,
 			     const char *annex, gdb_byte *readbuf,
 			     const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
 			     ULONGEST *xfered_len)
 {
 ...
   set_general_thread (inferior_ptid);


So what I think is happening is that inferior_ptid is null_ptid when you get here.
We always switch to null_ptid when waiting for events out of the target, exactly
to better uncover spots that would be relying on some stale non-null inferior_ptid
by accident.  This would be such a case.  The inferior is running, and we're waiting
for events, so whatever was the gdb-selected thread (per inferior_ptid) has no bearing
on what thread is reporting some event.

If the server supports multi-process extensions, then that set_general_thread call above
with inferior_ptid == null_ptid will end up sending "Hg0.0", meaning, select any thread of any process,
which means the subsequent memory read (m packet) will potentially read memory off of
the wrong process.  This is obviously bad.

HOWEVER.  Note that the File I/O packets, like "Fwrite,1,80026300,12" don't say
at all which thread is doing the File I/O access!  IOW, this packet is not useable
in multi-process scenarios as is...  It would need to be extended to also pass down
an extra thread id field, like "Fwrite,1,80026300,12,pPID.TID".  FSF GDBserver doesn't
support File I/O, so these File I/O packets didn't get attention when the multi-process
extensions were devised.

So alright, the current "Hg0" packet you see in your logs is a bit spurious, but, it should
be harmless, and you should be able to select any valid thread, as per the Hg0 packet's
description, as all threads share the same address space, in GDB's view.

  [remote] Packet received: Fwrite,1,80026300,12
  [remote] Sending packet: $Hg0#df
  [remote] Received Ack
  [remote] Packet received: OK
  [remote] Sending packet: $m80026300,12#8f
  [remote] Received Ack
  [remote] Packet received: 48656c6c6f2066726f6d20436f726523370a
  [remote] Sending packet: $F12#a9

What I think you should do is, when you get the reply to Fwrite -- the "$F12#a9"
packet -- your stub should make sure that that resumes the same thread that initiated
the Fwrite, not whatever Hg selected.  That $F12 reply is linked with the last Fwrite,
there can only be one File I/O request ongoing at a given time.

Pedro Alves

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

* Re: [EXT] Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode
  2022-03-10 11:30       ` Pedro Alves
@ 2022-03-10 12:32         ` Pedro Alves
  2022-03-14  7:33           ` Adrian Oltean
  0 siblings, 1 reply; 8+ messages in thread
From: Pedro Alves @ 2022-03-10 12:32 UTC (permalink / raw)
  To: Adrian Oltean, Tom Tromey; +Cc: Adrian Oltean via Gdb

On 2022-03-10 11:30, Pedro Alves wrote:
> 
> HOWEVER.  Note that the File I/O packets, like "Fwrite,1,80026300,12" don't say
> at all which thread is doing the File I/O access!  IOW, this packet is not useable
> in multi-process scenarios as is...  It would need to be extended to also pass down
> an extra thread id field, like "Fwrite,1,80026300,12,pPID.TID".  FSF GDBserver doesn't
> support File I/O, so these File I/O packets didn't get attention when the multi-process
> extensions were devised.
> 
> So alright, the current "Hg0" packet you see in your logs is a bit spurious, but, it should
> be harmless, and you should be able to select any valid thread, as per the Hg0 packet's
> description, as all threads share the same address space, in GDB's view.
> 
>   [remote] Packet received: Fwrite,1,80026300,12
>   [remote] Sending packet: $Hg0#df
>   [remote] Received Ack
>   [remote] Packet received: OK
>   [remote] Sending packet: $m80026300,12#8f
>   [remote] Received Ack
>   [remote] Packet received: 48656c6c6f2066726f6d20436f726523370a
>   [remote] Sending packet: $F12#a9
> 
> What I think you should do is, when you get the reply to Fwrite -- the "$F12#a9"
> packet -- your stub should make sure that that resumes the same thread that initiated
> the Fwrite, not whatever Hg selected.  That $F12 reply is linked with the last Fwrite,
> there can only be one File I/O request ongoing at a given time.

Thinking a bit more about this, I guess GDB could just avoid changing the selected thread
when handling a File I/O request.  That seems quite easy to do, so I gave it a try.

However, a realized that that runs into a difficulty.  With multi-process, if the server
doesn't say which process the request is for, GDB's target_read_memory/target_write_memory
can end up reading/writing from the wrong dcache.  A workaround is to flush the dcache
around memory accesses that come from File I/O.  Not great, but at least it turns
a correctness issue into an optimization issue...

I don't have a target that does File I/O, so I can't really test this.

From c3093d0fa3ff527b542a141e93c0a8a580e7cce8 Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Thu, 10 Mar 2022 11:59:52 +0000
Subject: [PATCH] File I/O - avoid changing the server's current thread

Change-Id: I3a69e36988cb1fba746be5a4d8dd65002c8b7711
---
 gdb/remote-fileio.c | 52 +++++++++++++++++++++++++++++++++++----------
 gdb/remote.c        | 23 ++++++++++++++++----
 gdb/target-dcache.c | 23 ++++++++++++++++----
 gdb/target-dcache.h |  3 +++
 4 files changed, 82 insertions(+), 19 deletions(-)

diff --git a/gdb/remote-fileio.c b/gdb/remote-fileio.c
index 9963f1ebc01..3fbf5ec4090 100644
--- a/gdb/remote-fileio.c
+++ b/gdb/remote-fileio.c
@@ -29,6 +29,7 @@
 #include "target.h"
 #include "filenames.h"
 #include "gdbsupport/filestuff.h"
+#include "target-dcache.h"
 
 #include <fcntl.h>
 #include "gdbsupport/gdb_sys_time.h"
@@ -37,6 +38,35 @@
 #endif
 #include <signal.h>
 
+/* Wrappers around target_read_memory/target_write_memory that
+   invalidate the dcache on entry and exit.  This is needed because
+   File I/O packets don't tell us which process initiated the request,
+   so we don't know whether the cache for the current process is the
+   right one.  */
+
+static int
+fileio_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len)
+{
+  target_dcache_invalidate ();
+  SCOPE_EXIT { target_dcache_invalidate (); };
+
+  return target_read_memory (memaddr, myaddr, len);
+}
+
+static int
+fileio_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t len)
+{
+  target_dcache_invalidate ();
+  SCOPE_EXIT
+    {
+      /* After writing, the cache of some other inferior may be stale,
+	 so we need to invalidate all.  */
+      target_dcache_invalidate_all ();
+    };
+
+  return target_write_memory (memaddr, myaddr, len);
+}
+
 static struct {
   int *fd_map;
   int fd_map_size;
@@ -401,7 +431,7 @@ remote_fileio_func_open (remote_target *remote, char *buf)
 
   /* Request pathname.  */
   pathname = (char *) alloca (length);
-  if (target_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
+  if (fileio_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -574,7 +604,7 @@ remote_fileio_func_read (remote_target *remote, char *buf)
 
   if (ret > 0)
     {
-      errno = target_write_memory (ptrval, buffer, ret);
+      errno = fileio_write_memory (ptrval, buffer, ret);
       if (errno != 0)
 	ret = -1;
     }
@@ -625,7 +655,7 @@ remote_fileio_func_write (remote_target *remote, char *buf)
   length = (size_t) num;
     
   buffer = (gdb_byte *) xmalloc (length);
-  if (target_read_memory (ptrval, buffer, length) != 0)
+  if (fileio_read_memory (ptrval, buffer, length) != 0)
     {
       xfree (buffer);
       remote_fileio_ioerror (remote);
@@ -740,7 +770,7 @@ remote_fileio_func_rename (remote_target *remote, char *buf)
   
   /* Request oldpath using 'm' packet */
   oldpath = (char *) alloca (old_len);
-  if (target_read_memory (old_ptr, (gdb_byte *) oldpath, old_len) != 0)
+  if (fileio_read_memory (old_ptr, (gdb_byte *) oldpath, old_len) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -748,7 +778,7 @@ remote_fileio_func_rename (remote_target *remote, char *buf)
   
   /* Request newpath using 'm' packet */
   newpath = (char *) alloca (new_len);
-  if (target_read_memory (new_ptr, (gdb_byte *) newpath, new_len) != 0)
+  if (fileio_read_memory (new_ptr, (gdb_byte *) newpath, new_len) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -825,7 +855,7 @@ remote_fileio_func_unlink (remote_target *remote, char *buf)
     }
   /* Request pathname using 'm' packet */
   pathname = (char *) alloca (length);
-  if (target_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
+  if (fileio_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -874,7 +904,7 @@ remote_fileio_func_stat (remote_target *remote, char *buf)
   
   /* Request pathname using 'm' packet */
   pathname = (char *) alloca (namelength);
-  if (target_read_memory (nameptr, (gdb_byte *) pathname, namelength) != 0)
+  if (fileio_read_memory (nameptr, (gdb_byte *) pathname, namelength) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -898,7 +928,7 @@ remote_fileio_func_stat (remote_target *remote, char *buf)
       host_to_fileio_stat (&st, &fst);
       host_to_fileio_uint (0, fst.fst_dev);
 
-      errno = target_write_memory (statptr, (gdb_byte *) &fst, sizeof fst);
+      errno = fileio_write_memory (statptr, (gdb_byte *) &fst, sizeof fst);
       if (errno != 0)
 	{
 	  remote_fileio_return_errno (remote, -1);
@@ -975,7 +1005,7 @@ remote_fileio_func_fstat (remote_target *remote, char *buf)
     {
       host_to_fileio_stat (&st, &fst);
 
-      errno = target_write_memory (ptrval, (gdb_byte *) &fst, sizeof fst);
+      errno = fileio_write_memory (ptrval, (gdb_byte *) &fst, sizeof fst);
       if (errno != 0)
 	{
 	  remote_fileio_return_errno (remote, -1);
@@ -1026,7 +1056,7 @@ remote_fileio_func_gettimeofday (remote_target *remote, char *buf)
     {
       remote_fileio_to_fio_timeval (&tv, &ftv);
 
-      errno = target_write_memory (ptrval, (gdb_byte *) &ftv, sizeof ftv);
+      errno = fileio_write_memory (ptrval, (gdb_byte *) &ftv, sizeof ftv);
       if (errno != 0)
 	{
 	  remote_fileio_return_errno (remote, -1);
@@ -1071,7 +1101,7 @@ remote_fileio_func_system (remote_target *remote, char *buf)
     {
       /* Request commandline using 'm' packet */
       cmdline = (char *) alloca (length);
-      if (target_read_memory (ptrval, (gdb_byte *) cmdline, length) != 0)
+      if (fileio_read_memory (ptrval, (gdb_byte *) cmdline, length) != 0)
 	{
 	  remote_fileio_ioerror (remote);
 	  return;
diff --git a/gdb/remote.c b/gdb/remote.c
index aa6a67a96e0..e9230060927 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -274,6 +274,13 @@ class remote_state
      because we allow GDB commands while the target is running.  */
   bool waiting_for_stop_reply = false;
 
+  /* True if handling a File I/O request.  When this is true, we make
+     sure to skip telling the server to change its selected thread to
+     match the GDB-selected thread, which has no relation to the
+     thread that initiated the File I/O request.  Note the File I/O
+     packet doesn't tell us which thread or process that was.  */
+  bool handling_fileio_request = false;
+
   /* The status of the stub support for the various vCont actions.  */
   vCont_action_support supports_vCont;
   /* Whether vCont support was probed already.  This is a workaround
@@ -8232,11 +8239,13 @@ remote_target::wait_as (ptid_t ptid, target_waitstatus *status,
 	     for a stop reply.  See the comments in putpkt_binary.  Set
 	     waiting_for_stop_reply to 0 temporarily.  */
 	  rs->waiting_for_stop_reply = 0;
+	  rs->handling_fileio_request = true;
 	  remote_fileio_request (this, buf, rs->ctrlc_pending_p);
 	  rs->ctrlc_pending_p = 0;
 	  /* GDB handled the File-I/O request, and the target is running
 	     again.  Keep waiting for events.  */
 	  rs->waiting_for_stop_reply = 1;
+	  rs->handling_fileio_request = false;
 	  break;
 	case 'N': case 'T': case 'S': case 'X': case 'W':
 	  {
@@ -11208,16 +11217,22 @@ remote_target::xfer_partial (enum target_object object,
 			     const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
 			     ULONGEST *xfered_len)
 {
-  struct remote_state *rs;
   int i;
   char *p2;
   char query_type;
   int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ());
 
-  set_remote_traceframe ();
-  set_general_thread (inferior_ptid);
+  remote_state *rs = get_remote_state ();
 
-  rs = get_remote_state ();
+  /* If we're handling a File I/O read/write request, we'll want to
+     access memory off of the thread that initiated the File I/O
+     request, not from whatever thread GDB had selected.  We don't
+     know what thread that is, but we don't need to know.  */
+  if (!rs->handling_fileio_request)
+    {
+      set_remote_traceframe ();
+      set_general_thread (inferior_ptid);
+    }
 
   /* Handle memory using the standard memory routines.  */
   if (object == TARGET_OBJECT_MEMORY)
diff --git a/gdb/target-dcache.c b/gdb/target-dcache.c
index 96d4611ee02..0e10e42218f 100644
--- a/gdb/target-dcache.c
+++ b/gdb/target-dcache.c
@@ -38,18 +38,33 @@ target_dcache_init_p (void)
   return (dcache != NULL);
 }
 
-/* Invalidate the target dcache.  */
+/* Invalidate the target dcache of PSPACE.  */
 
-void
-target_dcache_invalidate (void)
+static void
+target_dcache_invalidate_pspace (program_space *pspace)
 {
   DCACHE *dcache
-    = target_dcache_aspace_key.get (current_program_space->aspace);
+    = target_dcache_aspace_key.get (pspace->aspace);
 
   if (dcache != NULL)
     dcache_invalidate (dcache);
 }
 
+/* Invalidate the target dcache.  */
+
+void
+target_dcache_invalidate (void)
+{
+  target_dcache_invalidate_pspace (current_program_space);
+}
+
+void
+target_dcache_invalidate_all ()
+{
+  for (program_space *pspace : program_spaces)
+    target_dcache_invalidate_pspace (pspace);
+}
+
 /* Return the target dcache.  Return NULL if target dcache is not
    initialized yet.  */
 
diff --git a/gdb/target-dcache.h b/gdb/target-dcache.h
index 6f31a3d4482..2def8a2ee30 100644
--- a/gdb/target-dcache.h
+++ b/gdb/target-dcache.h
@@ -22,6 +22,9 @@
 
 extern void target_dcache_invalidate (void);
 
+/* Invalidate the dcache of all program spaces.  */
+extern void target_dcache_invalidate_all ();
+
 extern DCACHE *target_dcache_get (void);
 
 extern DCACHE *target_dcache_get_or_init (void);

base-commit: c6479f8b2ab32c8a1659054104f2d0176a466cb3
-- 
2.26.2


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

* RE: [EXT] Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode
  2022-03-10 12:32         ` Pedro Alves
@ 2022-03-14  7:33           ` Adrian Oltean
  2022-03-17 17:48             ` Pedro Alves
  0 siblings, 1 reply; 8+ messages in thread
From: Adrian Oltean @ 2022-03-14  7:33 UTC (permalink / raw)
  To: Pedro Alves, Tom Tromey; +Cc: Adrian Oltean via Gdb

Hi Pedro,

Thanks a lot for the extra details provided. I did not have a chance to test yet the patch you sent below. At first sight, the 
changes are straight-forward and make sense (even with my knowledge in GDB's internals). I also changed the 'H' packet
handling in the stub per previous emails.

Are you planning to commit the patch you proposed? I'm asking this because I have a GDB fork that I use for a custom
build but I'd rather not maintain a patch that won't be on the mainline.

Thank you,
Adrian

-----Original Message-----
From: Pedro Alves <pedro@palves.net> 
Sent: Thursday, March 10, 2022 2:33 PM
To: Adrian Oltean <adrian.oltean@nxp.com>; Tom Tromey <tom@tromey.com>
Cc: Adrian Oltean via Gdb <gdb@sourceware.org>
Subject: Re: [EXT] Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode

Caution: EXT Email

On 2022-03-10 11:30, Pedro Alves wrote:
>
> HOWEVER.  Note that the File I/O packets, like "Fwrite,1,80026300,12" 
> don't say at all which thread is doing the File I/O access!  IOW, this 
> packet is not useable in multi-process scenarios as is...  It would 
> need to be extended to also pass down an extra thread id field, like 
> "Fwrite,1,80026300,12,pPID.TID".  FSF GDBserver doesn't support File 
> I/O, so these File I/O packets didn't get attention when the multi-process extensions were devised.
>
> So alright, the current "Hg0" packet you see in your logs is a bit 
> spurious, but, it should be harmless, and you should be able to select 
> any valid thread, as per the Hg0 packet's description, as all threads share the same address space, in GDB's view.
>
>   [remote] Packet received: Fwrite,1,80026300,12
>   [remote] Sending packet: $Hg0#df
>   [remote] Received Ack
>   [remote] Packet received: OK
>   [remote] Sending packet: $m80026300,12#8f
>   [remote] Received Ack
>   [remote] Packet received: 48656c6c6f2066726f6d20436f726523370a
>   [remote] Sending packet: $F12#a9
>
> What I think you should do is, when you get the reply to Fwrite -- the "$F12#a9"
> packet -- your stub should make sure that that resumes the same thread 
> that initiated the Fwrite, not whatever Hg selected.  That $F12 reply 
> is linked with the last Fwrite, there can only be one File I/O request ongoing at a given time.

Thinking a bit more about this, I guess GDB could just avoid changing the selected thread when handling a File I/O request.  That seems quite easy to do, so I gave it a try.

However, a realized that that runs into a difficulty.  With multi-process, if the server doesn't say which process the request is for, GDB's target_read_memory/target_write_memory
can end up reading/writing from the wrong dcache.  A workaround is to flush the dcache around memory accesses that come from File I/O.  Not great, but at least it turns a correctness issue into an optimization issue...

I don't have a target that does File I/O, so I can't really test this.

From c3093d0fa3ff527b542a141e93c0a8a580e7cce8 Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Thu, 10 Mar 2022 11:59:52 +0000
Subject: [PATCH] File I/O - avoid changing the server's current thread

Change-Id: I3a69e36988cb1fba746be5a4d8dd65002c8b7711
---
 gdb/remote-fileio.c | 52 +++++++++++++++++++++++++++++++++++----------
 gdb/remote.c        | 23 ++++++++++++++++----
 gdb/target-dcache.c | 23 ++++++++++++++++----  gdb/target-dcache.h |  3 +++
 4 files changed, 82 insertions(+), 19 deletions(-)

diff --git a/gdb/remote-fileio.c b/gdb/remote-fileio.c index 9963f1ebc01..3fbf5ec4090 100644
--- a/gdb/remote-fileio.c
+++ b/gdb/remote-fileio.c
@@ -29,6 +29,7 @@
 #include "target.h"
 #include "filenames.h"
 #include "gdbsupport/filestuff.h"
+#include "target-dcache.h"

 #include <fcntl.h>
 #include "gdbsupport/gdb_sys_time.h"
@@ -37,6 +38,35 @@
 #endif
 #include <signal.h>

+/* Wrappers around target_read_memory/target_write_memory that
+   invalidate the dcache on entry and exit.  This is needed because
+   File I/O packets don't tell us which process initiated the request,
+   so we don't know whether the cache for the current process is the
+   right one.  */
+
+static int
+fileio_read_memory (CORE_ADDR memaddr, gdb_byte *myaddr, ssize_t len) {
+  target_dcache_invalidate ();
+  SCOPE_EXIT { target_dcache_invalidate (); };
+
+  return target_read_memory (memaddr, myaddr, len); }
+
+static int
+fileio_write_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, ssize_t 
+len) {
+  target_dcache_invalidate ();
+  SCOPE_EXIT
+    {
+      /* After writing, the cache of some other inferior may be stale,
+        so we need to invalidate all.  */
+      target_dcache_invalidate_all ();
+    };
+
+  return target_write_memory (memaddr, myaddr, len); }
+
 static struct {
   int *fd_map;
   int fd_map_size;
@@ -401,7 +431,7 @@ remote_fileio_func_open (remote_target *remote, char *buf)

   /* Request pathname.  */
   pathname = (char *) alloca (length);
-  if (target_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
+  if (fileio_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -574,7 +604,7 @@ remote_fileio_func_read (remote_target *remote, char *buf)

   if (ret > 0)
     {
-      errno = target_write_memory (ptrval, buffer, ret);
+      errno = fileio_write_memory (ptrval, buffer, ret);
       if (errno != 0)
        ret = -1;
     }
@@ -625,7 +655,7 @@ remote_fileio_func_write (remote_target *remote, char *buf)
   length = (size_t) num;

   buffer = (gdb_byte *) xmalloc (length);
-  if (target_read_memory (ptrval, buffer, length) != 0)
+  if (fileio_read_memory (ptrval, buffer, length) != 0)
     {
       xfree (buffer);
       remote_fileio_ioerror (remote);
@@ -740,7 +770,7 @@ remote_fileio_func_rename (remote_target *remote, char *buf)

   /* Request oldpath using 'm' packet */
   oldpath = (char *) alloca (old_len);
-  if (target_read_memory (old_ptr, (gdb_byte *) oldpath, old_len) != 0)
+  if (fileio_read_memory (old_ptr, (gdb_byte *) oldpath, old_len) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -748,7 +778,7 @@ remote_fileio_func_rename (remote_target *remote, char *buf)

   /* Request newpath using 'm' packet */
   newpath = (char *) alloca (new_len);
-  if (target_read_memory (new_ptr, (gdb_byte *) newpath, new_len) != 0)
+  if (fileio_read_memory (new_ptr, (gdb_byte *) newpath, new_len) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -825,7 +855,7 @@ remote_fileio_func_unlink (remote_target *remote, char *buf)
     }
   /* Request pathname using 'm' packet */
   pathname = (char *) alloca (length);
-  if (target_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
+  if (fileio_read_memory (ptrval, (gdb_byte *) pathname, length) != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -874,7 +904,7 @@ remote_fileio_func_stat (remote_target *remote, char *buf)

   /* Request pathname using 'm' packet */
   pathname = (char *) alloca (namelength);
-  if (target_read_memory (nameptr, (gdb_byte *) pathname, namelength) != 0)
+  if (fileio_read_memory (nameptr, (gdb_byte *) pathname, namelength) 
+ != 0)
     {
       remote_fileio_ioerror (remote);
       return;
@@ -898,7 +928,7 @@ remote_fileio_func_stat (remote_target *remote, char *buf)
       host_to_fileio_stat (&st, &fst);
       host_to_fileio_uint (0, fst.fst_dev);

-      errno = target_write_memory (statptr, (gdb_byte *) &fst, sizeof fst);
+      errno = fileio_write_memory (statptr, (gdb_byte *) &fst, sizeof 
+ fst);
       if (errno != 0)
        {
          remote_fileio_return_errno (remote, -1); @@ -975,7 +1005,7 @@ remote_fileio_func_fstat (remote_target *remote, char *buf)
     {
       host_to_fileio_stat (&st, &fst);

-      errno = target_write_memory (ptrval, (gdb_byte *) &fst, sizeof fst);
+      errno = fileio_write_memory (ptrval, (gdb_byte *) &fst, sizeof 
+ fst);
       if (errno != 0)
        {
          remote_fileio_return_errno (remote, -1); @@ -1026,7 +1056,7 @@ remote_fileio_func_gettimeofday (remote_target *remote, char *buf)
     {
       remote_fileio_to_fio_timeval (&tv, &ftv);

-      errno = target_write_memory (ptrval, (gdb_byte *) &ftv, sizeof ftv);
+      errno = fileio_write_memory (ptrval, (gdb_byte *) &ftv, sizeof 
+ ftv);
       if (errno != 0)
        {
          remote_fileio_return_errno (remote, -1); @@ -1071,7 +1101,7 @@ remote_fileio_func_system (remote_target *remote, char *buf)
     {
       /* Request commandline using 'm' packet */
       cmdline = (char *) alloca (length);
-      if (target_read_memory (ptrval, (gdb_byte *) cmdline, length) != 0)
+      if (fileio_read_memory (ptrval, (gdb_byte *) cmdline, length) != 
+ 0)
        {
          remote_fileio_ioerror (remote);
          return;
diff --git a/gdb/remote.c b/gdb/remote.c index aa6a67a96e0..e9230060927 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -274,6 +274,13 @@ class remote_state
      because we allow GDB commands while the target is running.  */
   bool waiting_for_stop_reply = false;

+  /* True if handling a File I/O request.  When this is true, we make
+     sure to skip telling the server to change its selected thread to
+     match the GDB-selected thread, which has no relation to the
+     thread that initiated the File I/O request.  Note the File I/O
+     packet doesn't tell us which thread or process that was.  */  bool 
+ handling_fileio_request = false;
+
   /* The status of the stub support for the various vCont actions.  */
   vCont_action_support supports_vCont;
   /* Whether vCont support was probed already.  This is a workaround @@ -8232,11 +8239,13 @@ remote_target::wait_as (ptid_t ptid, target_waitstatus *status,
             for a stop reply.  See the comments in putpkt_binary.  Set
             waiting_for_stop_reply to 0 temporarily.  */
          rs->waiting_for_stop_reply = 0;
+         rs->handling_fileio_request = true;
          remote_fileio_request (this, buf, rs->ctrlc_pending_p);
          rs->ctrlc_pending_p = 0;
          /* GDB handled the File-I/O request, and the target is running
             again.  Keep waiting for events.  */
          rs->waiting_for_stop_reply = 1;
+         rs->handling_fileio_request = false;
          break;
        case 'N': case 'T': case 'S': case 'X': case 'W':
          {
@@ -11208,16 +11217,22 @@ remote_target::xfer_partial (enum target_object object,
                             const gdb_byte *writebuf, ULONGEST offset, ULONGEST len,
                             ULONGEST *xfered_len)  {
-  struct remote_state *rs;
   int i;
   char *p2;
   char query_type;
   int unit_size = gdbarch_addressable_memory_unit_size (target_gdbarch ());

-  set_remote_traceframe ();
-  set_general_thread (inferior_ptid);
+  remote_state *rs = get_remote_state ();

-  rs = get_remote_state ();
+  /* If we're handling a File I/O read/write request, we'll want to
+     access memory off of the thread that initiated the File I/O
+     request, not from whatever thread GDB had selected.  We don't
+     know what thread that is, but we don't need to know.  */  if 
+ (!rs->handling_fileio_request)
+    {
+      set_remote_traceframe ();
+      set_general_thread (inferior_ptid);
+    }

   /* Handle memory using the standard memory routines.  */
   if (object == TARGET_OBJECT_MEMORY)
diff --git a/gdb/target-dcache.c b/gdb/target-dcache.c index 96d4611ee02..0e10e42218f 100644
--- a/gdb/target-dcache.c
+++ b/gdb/target-dcache.c
@@ -38,18 +38,33 @@ target_dcache_init_p (void)
   return (dcache != NULL);
 }

-/* Invalidate the target dcache.  */
+/* Invalidate the target dcache of PSPACE.  */

-void
-target_dcache_invalidate (void)
+static void
+target_dcache_invalidate_pspace (program_space *pspace)
 {
   DCACHE *dcache
-    = target_dcache_aspace_key.get (current_program_space->aspace);
+    = target_dcache_aspace_key.get (pspace->aspace);

   if (dcache != NULL)
     dcache_invalidate (dcache);
 }

+/* Invalidate the target dcache.  */
+
+void
+target_dcache_invalidate (void)
+{
+  target_dcache_invalidate_pspace (current_program_space); }
+
+void
+target_dcache_invalidate_all ()
+{
+  for (program_space *pspace : program_spaces)
+    target_dcache_invalidate_pspace (pspace); }
+
 /* Return the target dcache.  Return NULL if target dcache is not
    initialized yet.  */

diff --git a/gdb/target-dcache.h b/gdb/target-dcache.h index 6f31a3d4482..2def8a2ee30 100644
--- a/gdb/target-dcache.h
+++ b/gdb/target-dcache.h
@@ -22,6 +22,9 @@

 extern void target_dcache_invalidate (void);

+/* Invalidate the dcache of all program spaces.  */ extern void 
+target_dcache_invalidate_all ();
+
 extern DCACHE *target_dcache_get (void);

 extern DCACHE *target_dcache_get_or_init (void);

base-commit: c6479f8b2ab32c8a1659054104f2d0176a466cb3
--
2.26.2


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

* Re: [EXT] Re: Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode
  2022-03-14  7:33           ` Adrian Oltean
@ 2022-03-17 17:48             ` Pedro Alves
  0 siblings, 0 replies; 8+ messages in thread
From: Pedro Alves @ 2022-03-17 17:48 UTC (permalink / raw)
  To: Adrian Oltean, Tom Tromey; +Cc: Adrian Oltean via Gdb

Hi Adrian,

On 2022-03-14 07:33, Adrian Oltean wrote:
> Hi Pedro,
> 
> Thanks a lot for the extra details provided. I did not have a chance to test yet the patch you sent below. At first sight, the 
> changes are straight-forward and make sense (even with my knowledge in GDB's internals). I also changed the 'H' packet
> handling in the stub per previous emails.
> 
> Are you planning to commit the patch you proposed? I'm asking this because I have a GDB fork that I use for a custom
> build but I'd rather not maintain a patch that won't be on the mainline.

I think we can consider the patch for inclusion, yes, but I'd like to hear whether it actually works for
you as intended before sending it to the gdb-patches list for proper review.

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

end of thread, other threads:[~2022-03-17 17:48 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-09  7:25 Semihosting in GDB 11.1 | Proposed patch for interrupting in sync mode Adrian Oltean
2022-03-09 19:49 ` Tom Tromey
2022-03-10  9:47   ` Pedro Alves
2022-03-10 10:02     ` [EXT] " Adrian Oltean
2022-03-10 11:30       ` Pedro Alves
2022-03-10 12:32         ` Pedro Alves
2022-03-14  7:33           ` Adrian Oltean
2022-03-17 17:48             ` Pedro Alves

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).