[PATCH v2 18/47] gdb, gdbserver, ze: add U stop reply

Tankut Baris Aktemur
2024-12-13T16:23:03Z

From: Markus Metzger <markus.t.metzger@intel.com>

Add a new stop reply U for unavailable saying that we tried to stop a
process or thread but it would not respond and we cannot afford waiting.

This may occur when modeling threads as hardware threads on GPUs, where
threads that are currently idle cannot be interacted with. We cannot
afford waiting for threads to become available again as that may require
submitting new work from the host process. Or it may never happen for
some devices that simply are not used (anymore).

Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 6 ++++++
gdb/doc/gdb.texinfo | 24 ++++++++++++++++++++++++
gdb/remote.c | 18 +++++++++++++++++-
gdbserver/remote-utils.cc | 5 +++++
gdbserver/server.cc | 28 ++++++++++++++++++++++++++++
5 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 698ea9d589870388c56782a3ec535899b768fe72..01b9a359538bcfa7f767f51f2dc59eb252ee8074 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -187,6 +187,12 @@ vAck:in-memory-library
after the initial processing by GDB such as loading symbols and placing
breakpoints.

+U stop reply
+
+ Indicates that threads are currently unavailable. We tried stopping them but
+ they did not respond. The remote stub reports support for this stop reply to
+ GDB's qSupported query.
+
* Changed remote packets

qXfer:features:read:target.xml
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 03fcbee94198692b0e0bb1651add94d815e287f0..1cf31d022843b41dfdc4014125438d5455dc0bb8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -24683,6 +24683,10 @@ future connections is shown. The available settings are:
@tab @code{no resumed thread left stop reply}
@tab Tracking thread lifetime.

+@item @code{unavailable-stop-reply}
+@tab @code{thread unavailable stop reply}
+@tab Tracking thread lifetime.
+
@end multitable

@cindex packet size, remote, configuring
@@ -43904,6 +43908,17 @@ reply packet from the target. The latest @samp{C}, @samp{c}, @samp{S}
or @samp{s} action is expected to be continued. @xref{File-I/O Remote
Protocol Extension}, for more details.

+@item U @var{thread-id}
+The program is currently unavailable. The remote target tried to stop
+it but it would not respond. The thread designator @var{thread-id}
+has the format and interpretation described in @ref{thread-id syntax}.
+
+This packet should not be sent by default; older @value{GDBN} versions
+did not support it. @value{GDBN} requests it, by supplying an
+appropriate @samp{qSupported} feature (@pxref{qSupported}). The
+remote stub must also supply the appropriate @samp{qSupported} feature
+indicating support.
+
@end table

@node General Query Packets
@@ -45128,6 +45143,11 @@ These are the currently defined stub features and their properties:
@tab @samp{+}
@tab No

+@item @samp{unavailable}
+@tab No
+@tab @samp{-}
+@tab No
+
@end multitable

These are the currently defined stub features, in more detail:
@@ -45374,6 +45394,10 @@ The remote stub supports replying with an error in a
send this feature back to @value{GDBN} in the @samp{qSupported} reply,
@value{GDBN} will always support @samp{E.@var{errtext}} format replies
if it sent the @samp{error-message} feature.
+
+@item unavailable
+The remote stub reports the @samp{U} stop reply.
+
@end table

@item qSymbol::
diff --git a/gdb/remote.c b/gdb/remote.c
index 22c747de555e6a823973e17bbdb7c321364b547a..e277370ab4f1783d781c9b3a9edd4f459397839e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -404,6 +404,9 @@ enum {
errors, and so they should not need to check for this feature. */
PACKET_accept_error_message,

+ /* Support TARGET_WAITKIND_UNAVAILABLE. */
+ PACKET_unavailable,
+
PACKET_MAX
};

@@ -5858,6 +5861,8 @@ static const struct protocol_feature remote_protocol_features[] = {
PACKET_memory_tagging_feature },
{ "error-message", PACKET_ENABLE, remote_supported_packet,
PACKET_accept_error_message },
+ { "unavailable", PACKET_DISABLE, remote_supported_packet,
+ PACKET_unavailable },
{ "vAck:library", PACKET_DISABLE, remote_supported_packet,
PACKET_vAck_library },
{ "vAck:in-memory-library", PACKET_DISABLE, remote_supported_packet,
@@ -5973,6 +5978,10 @@ remote_target::remote_query_supported ()
!= AUTO_BOOLEAN_FALSE)
remote_query_supported_append (&q, "memory-tagging+");

+ if (m_features.packet_set_cmd_state (PACKET_unavailable)
+ != AUTO_BOOLEAN_FALSE)
+ remote_query_supported_append (&q, "unavailable+");
+
remote_query_supported_append
(&q, "qXfer:libraries:read:in-memory-library+");

@@ -8359,6 +8368,10 @@ Packet: '%s'\n"),
event->ws.set_no_resumed ();
event->ptid = minus_one_ptid;
break;
+ case 'U':
+ event->ws.set_unavailable ();
+ event->ptid = read_ptid (&buf[1], NULL);
+ break;
}
}

@@ -8772,7 +8785,7 @@ remote_target::wait_as (ptid_t ptid, target_waitstatus *status,
again. Keep waiting for events. */
rs->waiting_for_stop_reply = 1;
break;
- case 'N': case 'T': case 'S': case 'X': case 'W': case 'w':
+ case 'N': case 'T': case 'S': case 'X': case 'W': case 'w': case 'U':
{
/* There is a stop reply to handle. */
rs->waiting_for_stop_reply = 0;
@@ -16545,6 +16558,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (PACKET_accept_error_message,
"error-message", "error-message", 0);

+ add_packet_config_cmd (PACKET_unavailable,
+ "U stop reply", "unavailable-stop-reply", 0);
+
/* Assert that we've registered "set remote foo-packet" commands
for all packet configs. */
{
diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index 42252bad78f0dec91f887102d8a3baebb952c5ae..bbf72146ee8d301a9540211ef636dc0f6c223ef6 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1284,6 +1284,11 @@ prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status)
case TARGET_WAITKIND_NO_RESUMED:
sprintf (buf, "N");
break;
+ case TARGET_WAITKIND_UNAVAILABLE:
+ sprintf (buf, "U");
+ buf += strlen (buf);
+ buf = write_ptid (buf, ptid);
+ break;
default:
error ("unhandled waitkind");
break;
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 68ee52a7615b51c24e3c609bd78b525416b71420..eb541a9917f0faf6b4805700216e589054dc19bd 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -86,6 +86,9 @@ bool run_once;
/* Whether to report TARGET_WAITKIND_NO_RESUMED events. */
static bool report_no_resumed;

+/* Whether to report TARGET_WAITKIND_UNAVAILABLE events. */
+static bool report_unavailable;
+
/* The event loop checks this to decide whether to continue accepting
events. */
static bool keep_processing_events = true;
@@ -2872,6 +2875,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
}
else if (feature == "error-message+")
cs.error_message_supported = true;
+ else if (feature == "unavailable+")
+ {
+ /* GDB supports and wants TARGET_WAITKIND_UNAVAILABLE
+ events. */
+ report_unavailable = true;
+ }
else if (feature == "qXfer:libraries:read:in-memory-library+")
cs.in_memory_library_supported = true;
else if (feature == "vAck:library+")
@@ -3501,6 +3510,16 @@ resume (struct thread_resume *actions, size_t num_actions)
return;
}

+ if (cs.last_status.kind () == TARGET_WAITKIND_UNAVAILABLE
+ && !report_unavailable)
+ {
+ /* The client does not support this stop reply. At least
+ return error. */
+ sprintf (cs.own_buf, "E.Unavailable.");
+ disable_async_io ();
+ return;
+ }
+
if (cs.last_status.kind () != TARGET_WAITKIND_EXITED
&& cs.last_status.kind () != TARGET_WAITKIND_SIGNALLED
&& cs.last_status.kind () != TARGET_WAITKIND_THREAD_EXITED
@@ -5188,6 +5207,15 @@ handle_target_event (int err, gdb_client_data client_data)
if (gdb_connected () && report_no_resumed)
push_stop_notification (null_ptid, cs.last_status);
}
+ else if (cs.last_status.kind () == TARGET_WAITKIND_UNAVAILABLE)
+ {
+ /* Update the thread state but otherwise silently ignore this.
+
+ We do need to report thread unavailability on resume or stop
+ requests, but not as async target events. */
+ if (current_thread != nullptr)
+ current_thread->last_status = cs.last_status;
+ }
else if (cs.last_status.kind () != TARGET_WAITKIND_IGNORE)
{
int pid = cs.last_ptid.pid ();