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 65706a29ba (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  <palves@redhat.com>

	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.
This commit is contained in:
Pedro Alves 2016-05-24 14:47:56 +01:00
parent 44d3da2338
commit aa01bd3689
3 changed files with 73 additions and 17 deletions

View File

@ -1,3 +1,17 @@
2016-05-24 Pedro Alves <palves@redhat.com>
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 <currygt52@gmail.com>
* MAINTAINERS (Write After Approval): Add "Yan-Ting Lin".

View File

@ -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;

View File

@ -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)
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));