[PATCH 09/11] Ensure EXIT is last event, gdb/linux
Pedro Alves
pedro@palves.net
Thu Mar 3 14:40:18 GMT 2022
From: Lancelot SIX <lancelot.six@amd.com>
When all threads of a multi-threaded process terminate, we can end up
with multiple LWPs having a pending exit event (lets say that the
events arrive faster than GDB processes them, which can be simulated
by introducing an artificial delay in GDB). When we have multiple
pending events to report to the core, linux_nat_wait_1 uses
select_event_lwp to select randomly one of the pending events to
report.
If we have multiple pending exit events, and the randomization picks
the leader's exit to report, filter_exit_event sees that this is the
leader exiting, thus it is the exit of the whole process and thus
reports an EXITED event to the core, while leaving the other threads'
exit statuses pending.
This is problematic for infrun.c:stop_all_threads, which asks the
target to report all thread exit events to infrun. For example, in
stop_all_threads, core GDB counts 2 threads that needs to be stopped.
It then asks the target to stop those 2 threads (with
target_stop(ptid)), and waits for 2 events to come back from the
target. Unfortunately, when waiting for events, the linux-nat target,
due to the random event selecting mentioned above, reports the
whole-process EXIT event even though the other thread has exited and
its exit hasn't been reported yet. As a consequence, stop_all_threads
receives one event, but waits indefinitely for the second one to come.
Effectively, GDB hangs forever.
To fix this, this commit makes sure that a leader's exit event is not
considered for selection by select_event_lwp as long as there is at
least one other thread with a pending event remaining in the process.
Considering that the leader thread's exit event can only be generated
by the kernel once it has reaped all the non-leader threads, we are
guaranteed that all other threads do have an exit event which is ready
to be processed. Once all other exit events are processed,
select_event_lwp will consider the leader's exit status.
Tested on Linux-x86_64 with no regression observed.
Co-Authored-By: Pedro Alves <pedro@palves.net>
Change-Id: Id17ad5de76518925a968c0902860646820679dfa
---
gdb/linux-nat.c | 46 +++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 41 insertions(+), 5 deletions(-)
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index d97a770bf83..5d8887b11f6 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2482,15 +2482,50 @@ status_callback (struct lwp_info *lp)
return 1;
}
-/* Count the LWP's that have had events. */
+/* Return true if another thread of the same process as LP has a
+ pending status ready to be processed. LP is assumed to be the
+ leader of its process. */
+
+static bool
+non_leader_lwp_in_process_with_pending_event (lwp_info *lp)
+{
+ gdb_assert (is_leader (lp));
+
+ for (lwp_info *other : all_lwps ())
+ {
+ if (other->ptid.pid () == lp->ptid.pid ()
+ && !is_leader (other)
+ && other->resumed
+ && lwp_status_pending_p (other))
+ return true;
+ }
+
+ return false;
+}
+
+/* Indicate whether LP has a pending event which should be considered
+ for immediate processing. Does not consider a leader thread's exit
+ event before the non-leader threads have reported their exits. */
+
+static bool
+has_reportable_pending_event (struct lwp_info *lp)
+{
+ return (lp->resumed && lwp_status_pending_p (lp)
+ && !(!WIFSTOPPED (lp->status)
+ && is_leader (lp)
+ && non_leader_lwp_in_process_with_pending_event (lp)));
+}
+
+/* Count the LWP's that have had reportable events. */
static int
count_events_callback (struct lwp_info *lp, int *count)
{
gdb_assert (count != NULL);
- /* Select only resumed LWPs that have an event pending. */
- if (lp->resumed && lwp_status_pending_p (lp))
+ /* Select only resumed LWPs that have a reportable event
+ pending. */
+ if (has_reportable_pending_event (lp))
(*count)++;
return 0;
@@ -2526,8 +2561,9 @@ select_event_lwp_callback (struct lwp_info *lp, int *selector)
{
gdb_assert (selector != NULL);
- /* Select only resumed LWPs that have an event pending. */
- if (lp->resumed && lwp_status_pending_p (lp))
+ /* Select only resumed LWPs that have a reportable event
+ pending. */
+ if (has_reportable_pending_event (lp))
if ((*selector)-- == 0)
return 1;
--
2.26.2
More information about the Gdb-patches
mailing list