summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdbserver/linux-low.cc75
-rw-r--r--gdbserver/linux-low.h5
2 files changed, 68 insertions, 12 deletions
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 44d0fe38030..f9001e2fa17 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -279,7 +279,8 @@ int using_threads = 1;
static int stabilizing_threads;
static void unsuspend_all_lwps (struct lwp_info *except);
-static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static void mark_lwp_dead (struct lwp_info *lwp, int wstat,
+ bool thread_event);
static int lwp_is_marked_dead (struct lwp_info *lwp);
static int kill_lwp (unsigned long lwpid, int signo);
static void enqueue_pending_signal (struct lwp_info *lwp, int signal, siginfo_t *info);
@@ -1803,10 +1804,12 @@ iterate_over_lwps (ptid_t filter,
return get_thread_lwp (thread);
}
-void
+bool
linux_process_target::check_zombie_leaders ()
{
- for_each_process ([this] (process_info *proc)
+ bool new_pending_event = false;
+
+ for_each_process ([&] (process_info *proc)
{
pid_t leader_pid = pid_of (proc);
lwp_info *leader_lp = find_lwp_pid (ptid_t (leader_pid));
@@ -1875,9 +1878,19 @@ linux_process_target::check_zombie_leaders ()
"(it exited, or another thread execd), "
"deleting it.",
leader_pid);
- delete_lwp (leader_lp);
+
+ thread_info *leader_thread = get_lwp_thread (leader_lp);
+ if (report_exit_events_for (leader_thread))
+ {
+ mark_lwp_dead (leader_lp, W_EXITCODE (0, 0), true);
+ new_pending_event = true;
+ }
+ else
+ delete_lwp (leader_lp);
}
});
+
+ return new_pending_event;
}
/* Callback for `find_thread'. Returns the first LWP that is not
@@ -2336,7 +2349,7 @@ linux_process_target::filter_event (int lwpid, int wstat)
/* Since events are serialized to GDB core, and we can't
report this one right now. Leave the status pending for
the next time we're able to report it. */
- mark_lwp_dead (child, wstat);
+ mark_lwp_dead (child, wstat, false);
return;
}
else
@@ -2655,7 +2668,8 @@ linux_process_target::wait_for_event_filtered (ptid_t wait_ptid,
/* Check for zombie thread group leaders. Those can't be reaped
until all other threads in the thread group are. */
- check_zombie_leaders ();
+ if (check_zombie_leaders ())
+ goto retry;
auto not_stopped = [&] (thread_info *thread)
{
@@ -2902,6 +2916,17 @@ linux_process_target::filter_exit_event (lwp_info *event_child,
struct thread_info *thread = get_lwp_thread (event_child);
ptid_t ptid = ptid_of (thread);
+ if (ourstatus->kind () == TARGET_WAITKIND_THREAD_EXITED)
+ {
+ /* We're reporting a thread exit for the leader. The exit was
+ detected by check_zombie_leaders. */
+ gdb_assert (is_leader (thread));
+ gdb_assert (report_exit_events_for (thread));
+
+ delete_lwp (event_child);
+ return ptid;
+ }
+
/* Note we must filter TARGET_WAITKIND_SIGNALLED as well, otherwise
if a non-leader thread exits with a signal, we'd report it to the
core which would interpret it as the whole-process exiting.
@@ -3021,7 +3046,20 @@ linux_process_target::wait_1 (ptid_t ptid, target_waitstatus *ourstatus,
{
if (WIFEXITED (w))
{
- ourstatus->set_exited (WEXITSTATUS (w));
+ /* If we already have the exit recorded in waitstatus, use
+ it. This will happen when we detect a zombie leader,
+ when we had GDB_THREAD_OPTION_EXIT enabled for it. We
+ want to report its exit as TARGET_WAITKIND_THREAD_EXITED,
+ as the whole process hasn't exited yet. */
+ const target_waitstatus &ws = event_child->waitstatus;
+ if (ws.kind () != TARGET_WAITKIND_IGNORE)
+ {
+ gdb_assert (ws.kind () == TARGET_WAITKIND_EXITED
+ || ws.kind () == TARGET_WAITKIND_THREAD_EXITED);
+ *ourstatus = ws;
+ }
+ else
+ ourstatus->set_exited (WEXITSTATUS (w));
threads_debug_printf
("ret = %s, exited with retcode %d",
@@ -3727,8 +3765,15 @@ suspend_and_send_sigstop (thread_info *thread, lwp_info *except)
send_sigstop (thread, except);
}
+/* Mark LWP dead, with WSTAT as exit status pending to report later.
+ If THREAD_EVENT is true, interpret WSTAT as a thread exit event
+ instead of a process exit event. This is meaningful for the leader
+ thread, as we normally report a process-wide exit event when we see
+ the leader exit, and a thread exit event when we see any other
+ thread exit. */
+
static void
-mark_lwp_dead (struct lwp_info *lwp, int wstat)
+mark_lwp_dead (struct lwp_info *lwp, int wstat, bool thread_event)
{
/* Store the exit status for later. */
lwp->status_pending_p = 1;
@@ -3737,9 +3782,19 @@ mark_lwp_dead (struct lwp_info *lwp, int wstat)
/* Store in waitstatus as well, as there's nothing else to process
for this event. */
if (WIFEXITED (wstat))
- lwp->waitstatus.set_exited (WEXITSTATUS (wstat));
+ {
+ if (thread_event)
+ lwp->waitstatus.set_thread_exited (WEXITSTATUS (wstat));
+ else
+ lwp->waitstatus.set_exited (WEXITSTATUS (wstat));
+ }
else if (WIFSIGNALED (wstat))
- lwp->waitstatus.set_signalled (gdb_signal_from_host (WTERMSIG (wstat)));
+ {
+ gdb_assert (!thread_event);
+ lwp->waitstatus.set_signalled (gdb_signal_from_host (WTERMSIG (wstat)));
+ }
+ else
+ gdb_assert_not_reached ("unknown status kind");
/* Prevent trying to stop it. */
lwp->stopped = 1;
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index d46ea5aa3ec..51d1899893a 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -574,8 +574,9 @@ private: /* Back to private. */
/* Detect zombie thread group leaders, and "exit" them. We can't
reap their exits until all other threads in the group have
- exited. */
- void check_zombie_leaders ();
+ exited. Returns true if we left any new event pending, false
+ otherwise. */
+ bool check_zombie_leaders ();
/* Convenience function that is called when we're about to return an
event to the core. If the event is an exit or signalled event,