Fake VFORK_DONE events when following only the parent after a vfork.

FreeBSD does not currently report a ptrace event for a parent process
after it resumes due to the child exiting the shared memory region after
a vfork.  Take the same approach used in linux-nat.c in this case of
sleeping for a while and then reporting a fake VFORK_DONE event.

gdb/ChangeLog:

	* fbsd-nat.c (struct fbsd_fork_child_info): Rename to ...
	(struct fbsd_fork_info): ... this.
	(struct fbsd_fork_info) <child>: Rename to ...
	(struct fbsd_fork_info) <ptid>: ... this.
	(fbsd_pending_children): Update type.
	(fbsd_remember_child): Update type and field name.
	(fbsd_is_child_pending): Likewise.
	(fbsd_pending_vfork_done): New variable.
	(fbsd_is_vfork_done_pending): New function.
	(fbsd_next_vfork_done): New function.
	(fbsd_resume): Don't resume processes with a pending vfork done
	event.
	(fbsd_wait): Report pending vfork done events.
	(fbsd_follow_fork): Delay and record a pending vfork done event
	for a vfork parent when detaching the child.
This commit is contained in:
John Baldwin 2016-06-24 21:00:04 -07:00
parent 8607ea632c
commit 2c5c2a3321
2 changed files with 121 additions and 9 deletions

View File

@ -1,3 +1,21 @@
2016-07-01 John Baldwin <jhb@FreeBSD.org>
* fbsd-nat.c (struct fbsd_fork_child_info): Rename to ...
(struct fbsd_fork_info): ... this.
(struct fbsd_fork_info) <child>: Rename to ...
(struct fbsd_fork_info) <ptid>: ... this.
(fbsd_pending_children): Update type.
(fbsd_remember_child): Update type and field name.
(fbsd_is_child_pending): Likewise.
(fbsd_pending_vfork_done): New variable.
(fbsd_is_vfork_done_pending): New function.
(fbsd_next_vfork_done): New function.
(fbsd_resume): Don't resume processes with a pending vfork done
event.
(fbsd_wait): Report pending vfork done events.
(fbsd_follow_fork): Delay and record a pending vfork done event
for a vfork parent when detaching the child.
2016-07-01 John Baldwin <jhb@FreeBSD.org>
* fbsd-nat.c (super_resume): Move earlier next to "super_wait".

View File

@ -530,13 +530,13 @@ fbsd_update_thread_list (struct target_ops *ops)
sake. FreeBSD versions newer than 9.1 contain both fixes.
*/
struct fbsd_fork_child_info
struct fbsd_fork_info
{
struct fbsd_fork_child_info *next;
ptid_t child; /* Pid of new child. */
struct fbsd_fork_info *next;
ptid_t ptid;
};
static struct fbsd_fork_child_info *fbsd_pending_children;
static struct fbsd_fork_info *fbsd_pending_children;
/* Record a new child process event that is reported before the
corresponding fork event in the parent. */
@ -544,9 +544,9 @@ static struct fbsd_fork_child_info *fbsd_pending_children;
static void
fbsd_remember_child (ptid_t pid)
{
struct fbsd_fork_child_info *info = XCNEW (struct fbsd_fork_child_info);
struct fbsd_fork_info *info = XCNEW (struct fbsd_fork_info);
info->child = pid;
info->ptid = pid;
info->next = fbsd_pending_children;
fbsd_pending_children = info;
}
@ -557,25 +557,74 @@ fbsd_remember_child (ptid_t pid)
static ptid_t
fbsd_is_child_pending (pid_t pid)
{
struct fbsd_fork_child_info *info, *prev;
struct fbsd_fork_info *info, *prev;
ptid_t ptid;
prev = NULL;
for (info = fbsd_pending_children; info; prev = info, info = info->next)
{
if (ptid_get_pid (info->child) == pid)
if (ptid_get_pid (info->ptid) == pid)
{
if (prev == NULL)
fbsd_pending_children = info->next;
else
prev->next = info->next;
ptid = info->child;
ptid = info->ptid;
xfree (info);
return ptid;
}
}
return null_ptid;
}
static struct fbsd_fork_info *fbsd_pending_vfork_done;
/* Record a pending vfork done event. */
static void
fbsd_add_vfork_done (ptid_t pid)
{
struct fbsd_fork_info *info = XCNEW (struct fbsd_fork_info);
info->ptid = pid;
info->next = fbsd_pending_vfork_done;
fbsd_pending_vfork_done = info;
}
/* Check for a pending vfork done event for a specific PID. */
static int
fbsd_is_vfork_done_pending (pid_t pid)
{
struct fbsd_fork_info *info;
for (info = fbsd_pending_vfork_done; info != NULL; info = info->next)
{
if (ptid_get_pid (info->ptid) == pid)
return 1;
}
return 0;
}
/* Check for a pending vfork done event. If one is found, remove it
from the list and return the PTID. */
static ptid
fbsd_next_vfork_done (void)
{
struct fbsd_fork_info *info;
ptid_t ptid;
if (fbsd_pending_vfork_done != NULL)
{
info = fbsd_pending_vfork_done;
fbsd_pending_vfork_done = info->next;
ptid = info->ptid;
xfree (info);
return ptid;
}
return null_ptid;
}
#endif
static int
@ -616,6 +665,17 @@ static void
fbsd_resume (struct target_ops *ops,
ptid_t ptid, int step, enum gdb_signal signo)
{
#ifdef TDP_RFPPWAIT
pid_t pid;
/* Don't PT_CONTINUE a process which has a pending vfork done event. */
if (ptid_equal (minus_one_ptid, ptid))
pid = ptid_get_pid (inferior_ptid);
else
pid = ptid_get_pid (ptid);
if (fbsd_is_vfork_done_pending (pid))
return;
#endif
if (debug_fbsd_lwp)
fprintf_unfiltered (gdb_stdlog,
@ -650,6 +710,12 @@ fbsd_wait (struct target_ops *ops,
while (1)
{
wptid = fbsd_next_vfork_done ();
if (!ptid_equal (wptid, null_ptid))
{
ourstatus->kind = TARGET_WAITKIND_VFORK_DONE;
return wptid;
}
wptid = super_wait (ops, ptid, ourstatus, target_options);
if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
{
@ -828,6 +894,7 @@ fbsd_follow_fork (struct target_ops *ops, int follow_child,
if (!follow_child && detach_fork)
{
struct thread_info *tp = inferior_thread ();
int has_vforked = tp->pending_follow.kind == TARGET_WAITKIND_VFORKED;
pid_t child_pid = ptid_get_pid (tp->pending_follow.value.related_pid);
/* Breakpoints have already been detached from the child by
@ -835,6 +902,33 @@ fbsd_follow_fork (struct target_ops *ops, int follow_child,
if (ptrace (PT_DETACH, child_pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
perror_with_name (("ptrace"));
if (has_vforked)
{
/* We can't insert breakpoints until the child process has
finished with the shared memory region. The parent
process doesn't wait for the child process to exit or
exec until after it has been resumed from the ptrace stop
to report the fork. Once it has been resumed it doesn't
stop again before returning to userland, so there is no
reliable way to wait on the parent.
We can't stay attached to the child to wait for an exec
or exit because it may invoke ptrace(PT_TRACE_ME)
(e.g. if the parent process is a debugger forking a new
child process).
In the end, the best we can do is to make sure it runs
for a little while. Hopefully it will be out of range of
any breakpoints we reinsert. Usually this is only the
single-step breakpoint at vfork's return point. */
usleep (10000);
/* Schedule a fake VFORK_DONE event to report on the next
wait. */
fbsd_add_vfork_done (inferior_ptid);
}
}
return 0;