aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <pedro@palves.net>2021-01-06 02:19:38 +0000
committerPedro Alves <pedro@palves.net>2021-02-03 01:14:46 +0000
commitb0083dd72fcff6ac3cd8ae10d104cf714e5e32aa (patch)
tree834d4ebbe8d24c43f4abbb26367500e0a0c5a751
parent92234eb192f11b1981acb46a3a5e725360b89d6f (diff)
downloadfsf-binutils-gdb-b0083dd72fcff6ac3cd8ae10d104cf714e5e32aa.zip
fsf-binutils-gdb-b0083dd72fcff6ac3cd8ae10d104cf714e5e32aa.tar.gz
fsf-binutils-gdb-b0083dd72fcff6ac3cd8ae10d104cf714e5e32aa.tar.bz2
Fix a couple vStopped pending ack bugs
A following patch will add a testcase that has two processes with threads stepping over a breakpoint continuously, and then detaches from one of the processes while threads are running. The other process continues stepping over its breakpoint. And then the testcase sends a SIGUSR1, expecting that GDB reports it. That would sometimes hang against gdbserver, due to the bugs fixed here. Both bugs are related, in that they're about remote protocol asynchronous Stop notifications. There's a bug in GDB, and another in GDBserver. The GDB bug: - when we detach from a process, the remote target discards any pending RSP notification related to that process, including the in-flight, yet-unacked notification. Discarding the in-flight notification is the problem. Until the in-flight notification is acked with a vStopped packet, the server won't send another %Stop notification. As a result, the debug session gets messed up. In the new testcase's case, GDB would hang inside stop_all_threads, waiting for a stop for one of the process'es threads, which never arrived -- its stop reply was permanently stuck in the stop reply queue, waiting for a vStopped packet that never arrived. In summary: 1. GDBserver sends stop notification about thread X, the remote target receives it and stores it 2. At the same time, GDB detaches thread X's inferior 3. The remote target discards the received stop notification 4. GDBserver waits forever for the ack The GDBserver bug: GDBserver has the opposite bug. It also discards notifications for the process being detached. If that discards the head of the notification queue, when gdb sends an ack, it ends up acking the _next_ notification. Meaning, gdb loses one notification. In the testcase, this results in a similar hang in stop_all_threads. So we have two very similar bugs in GDB and GDBserver, both resulting in a similar symptom. That's why I'm fixing them both at the same time. gdb/ChangeLog: * remote.c (remote_notif_stop_ack): Don't error out on TARGET_WAITKIND_IGNORE; instead, just ignore the notification. (remote_target::discard_pending_stop_replies): Don't delete in-flight notification; instead, clear its contents. gdbserver/ChangeLog: * server.cc (discard_queued_stop_replies): Don't ever discard the notification at the head of the list.
-rw-r--r--gdb/ChangeLog7
-rw-r--r--gdb/remote.c22
-rw-r--r--gdbserver/ChangeLog5
-rw-r--r--gdbserver/server.cc9
4 files changed, 34 insertions, 9 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index b53cd2b..f11092d 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,12 @@
2021-02-03 Pedro Alves <pedro@palves.net>
+ * remote.c (remote_notif_stop_ack): Don't error out on
+ TARGET_WAITKIND_IGNORE; instead, just ignore the notification.
+ (remote_target::discard_pending_stop_replies): Don't delete
+ in-flight notification; instead, clear its contents.
+
+2021-02-03 Pedro Alves <pedro@palves.net>
+
* remote.c (extended_remote_target::attach): Set target async in
the target-non-stop path too.
diff --git a/gdb/remote.c b/gdb/remote.c
index f183b4f..512bd94 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -7006,13 +7006,11 @@ remote_notif_stop_ack (remote_target *remote,
/* acknowledge */
putpkt (remote, self->ack_command);
- if (stop_reply->ws.kind == TARGET_WAITKIND_IGNORE)
- {
- /* We got an unknown stop reply. */
- error (_("Unknown stop reply"));
- }
-
- remote->push_stop_reply (stop_reply);
+ /* Kind can be TARGET_WAITKIND_IGNORE if we have meanwhile discarded
+ the notification. It was left in the queue because we need to
+ acknowledge it and pull the rest of the notifications out. */
+ if (stop_reply->ws.kind != TARGET_WAITKIND_IGNORE)
+ remote->push_stop_reply (stop_reply);
}
static int
@@ -7181,8 +7179,14 @@ remote_target::discard_pending_stop_replies (struct inferior *inf)
/* Discard the in-flight notification. */
if (reply != NULL && reply->ptid.pid () == inf->pid)
{
- delete reply;
- rns->pending_event[notif_client_stop.id] = NULL;
+ /* Leave the notification pending, since the server expects that
+ we acknowledge it with vStopped. But clear its contents, so
+ that later on when we acknowledge it, we also discard it. */
+ reply->ws.kind = TARGET_WAITKIND_IGNORE;
+
+ if (remote_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "discarded in-flight notification\n");
}
/* Discard the stop replies we have already pulled with
diff --git a/gdbserver/ChangeLog b/gdbserver/ChangeLog
index 2f883e0..41154d9 100644
--- a/gdbserver/ChangeLog
+++ b/gdbserver/ChangeLog
@@ -1,3 +1,8 @@
+2021-02-03 Pedro Alves <pedro@palves.net>
+
+ * server.cc (discard_queued_stop_replies): Don't ever discard the
+ notification at the head of the list.
+
2021-01-20 Simon Marchi <simon.marchi@polymtl.ca>
* ax.cc (bytecode_address_table): Make static.
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 77e89fe..a5497e9 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -203,6 +203,15 @@ discard_queued_stop_replies (ptid_t ptid)
next = iter;
++next;
+ if (iter == notif_stop.queue.begin ())
+ {
+ /* The head of the list contains the notification that was
+ already sent to GDB. So we can't remove it, otherwise
+ when GDB sends the vStopped, it would ack the _next_
+ notification, which hadn't been sent yet! */
+ continue;
+ }
+
if (remove_all_on_match_ptid (*iter, ptid))
{
delete *iter;