From aa01bd3689d204ce3d657cf7eb17b8343d79a080 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Tue, 24 May 2016 14:47:56 +0100 Subject: [PATCH] Linux native thread create/exit events support A following patch (fix for gdb/19828) makes linux-nat.c add threads to GDB's thread list earlier in the "attach" sequence, and that causes a surprising regression on gdb.threads/attach-many-short-lived-threads.exp on my machine. The extra "thread x exited" handling and traffic slows down that test enough that GDB core has trouble keeping up with new threads that are spawned while trying to stop existing ones. I saw the exact same issue with remote/gdbserver a while ago and fixed it in 65706a29bac5 (Remote thread create/exit events) so part of the fix here is the exact same -- add support for thread created events to gdb/linux-nat.c. infrun.c:stop_all_threads enables those events when it tries to stop threads, which ensures that new threads never get a chance to themselves start new threads, thus fixing the race. gdb/ 2016-05-24 Pedro Alves PR gdb/19828 * linux-nat.c (report_thread_events): New global. (linux_handle_extended_wait): Report TARGET_WAITKIND_THREAD_CREATED if thread event reporting is enabled. (wait_lwp, linux_nat_filter_event): Report all thread exits if thread event reporting is enabled. Remove comment. (filter_exit_event): New function. (linux_nat_wait_1): Use it. (linux_nat_thread_events): New function. (linux_nat_add_target): Install it as target_thread_events method. --- gdb/ChangeLog | 14 ++++++++++ gdb/linux-nat.c | 62 +++++++++++++++++++++++++++++++++++-------- gdb/linux-thread-db.c | 14 +++++----- 3 files changed, 73 insertions(+), 17 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 46f5b424313..5b79c485348 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,17 @@ +2016-05-24 Pedro Alves + + PR gdb/19828 + * linux-nat.c (report_thread_events): New global. + (linux_handle_extended_wait): Report + TARGET_WAITKIND_THREAD_CREATED if thread event reporting is + enabled. + (wait_lwp, linux_nat_filter_event): Report all thread exits if + thread event reporting is enabled. Remove comment. + (filter_exit_event): New function. + (linux_nat_wait_1): Use it. + (linux_nat_thread_events): New function. + (linux_nat_add_target): Install it as target_thread_events method. + 2016-05-24 Yan-Ting Lin * MAINTAINERS (Write After Approval): Add "Yan-Ting Lin". diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index edde88df8ab..19fe12f80a3 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -239,6 +239,9 @@ struct simple_pid_list }; struct simple_pid_list *stopped_pids; +/* Whether target_thread_events is in effect. */ +static int report_thread_events; + /* Async mode support. */ /* The read/write ends of the pipe registered as waitable file in the @@ -1952,6 +1955,11 @@ linux_handle_extended_wait (struct lwp_info *lp, int status) status_to_str (status)); new_lp->status = status; } + else if (report_thread_events) + { + new_lp->waitstatus.kind = TARGET_WAITKIND_THREAD_CREATED; + new_lp->status = status; + } return 1; } @@ -2091,13 +2099,14 @@ wait_lwp (struct lwp_info *lp) /* Check if the thread has exited. */ if (WIFEXITED (status) || WIFSIGNALED (status)) { - if (ptid_get_pid (lp->ptid) == ptid_get_lwp (lp->ptid)) + if (report_thread_events + || ptid_get_pid (lp->ptid) == ptid_get_lwp (lp->ptid)) { if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, "WL: Process %d exited.\n", + fprintf_unfiltered (gdb_stdlog, "WL: LWP %d exited.\n", ptid_get_pid (lp->ptid)); - /* This is the leader exiting, it means the whole + /* If this is the leader exiting, it means the whole process is gone. Store the status to report to the core. Store it in lp->waitstatus, because lp->status would be ambiguous (W_EXITCODE(0,0) == 0). */ @@ -2902,7 +2911,8 @@ linux_nat_filter_event (int lwpid, int status) /* Check if the thread has exited. */ if (WIFEXITED (status) || WIFSIGNALED (status)) { - if (num_lwps (ptid_get_pid (lp->ptid)) > 1) + if (!report_thread_events + && num_lwps (ptid_get_pid (lp->ptid)) > 1) { if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, @@ -2922,15 +2932,9 @@ linux_nat_filter_event (int lwpid, int status) resumed. */ if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, - "Process %ld exited (resumed=%d)\n", + "LWP %ld exited (resumed=%d)\n", ptid_get_lwp (lp->ptid), lp->resumed); - /* This was the last lwp in the process. Since events are - serialized to GDB core, we may not be able report this one - right now, but GDB core and the other target layers will want - to be notified about the exit code/signal, leave the status - pending for the next time we're able to report it. */ - /* Dead LWP's aren't expected to reported a pending sigstop. */ lp->signalled = 0; @@ -3110,6 +3114,30 @@ check_zombie_leaders (void) } } +/* Convenience function that is called when the kernel reports an exit + event. This decides whether to report the event to GDB as a + process exit event, a thread exit event, or to suppress the + event. */ + +static ptid_t +filter_exit_event (struct lwp_info *event_child, + struct target_waitstatus *ourstatus) +{ + ptid_t ptid = event_child->ptid; + + if (num_lwps (ptid_get_pid (ptid)) > 1) + { + if (report_thread_events) + ourstatus->kind = TARGET_WAITKIND_THREAD_EXITED; + else + ourstatus->kind = TARGET_WAITKIND_IGNORE; + + exit_lwp (event_child); + } + + return ptid; +} + static ptid_t linux_nat_wait_1 (struct target_ops *ops, ptid_t ptid, struct target_waitstatus *ourstatus, @@ -3339,6 +3367,9 @@ linux_nat_wait_1 (struct target_ops *ops, else lp->core = linux_common_core_of_thread (lp->ptid); + if (ourstatus->kind == TARGET_WAITKIND_EXITED) + return filter_exit_event (lp, ourstatus); + return lp->ptid; } @@ -4614,6 +4645,14 @@ linux_nat_fileio_unlink (struct target_ops *self, return ret; } +/* Implementation of the to_thread_events method. */ + +static void +linux_nat_thread_events (struct target_ops *ops, int enable) +{ + report_thread_events = enable; +} + void linux_nat_add_target (struct target_ops *t) { @@ -4646,6 +4685,7 @@ linux_nat_add_target (struct target_ops *t) t->to_supports_stopped_by_sw_breakpoint = linux_nat_supports_stopped_by_sw_breakpoint; t->to_stopped_by_hw_breakpoint = linux_nat_stopped_by_hw_breakpoint; t->to_supports_stopped_by_hw_breakpoint = linux_nat_supports_stopped_by_hw_breakpoint; + t->to_thread_events = linux_nat_thread_events; t->to_can_async_p = linux_nat_can_async_p; t->to_is_async_p = linux_nat_is_async_p; diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index 284e331258b..2300c810a74 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -1116,12 +1116,14 @@ thread_db_wait (struct target_ops *ops, ptid = beneath->to_wait (beneath, ptid, ourstatus, options); - if (ourstatus->kind == TARGET_WAITKIND_IGNORE) - return ptid; - - if (ourstatus->kind == TARGET_WAITKIND_EXITED - || ourstatus->kind == TARGET_WAITKIND_SIGNALLED) - return ptid; + switch (ourstatus->kind) + { + case TARGET_WAITKIND_IGNORE: + case TARGET_WAITKIND_EXITED: + case TARGET_WAITKIND_THREAD_EXITED: + case TARGET_WAITKIND_SIGNALLED: + return ptid; + } info = get_thread_db_info (ptid_get_pid (ptid)); -- 2.30.2