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:
parent
8607ea632c
commit
2c5c2a3321
|
@ -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>
|
2016-07-01 John Baldwin <jhb@FreeBSD.org>
|
||||||
|
|
||||||
* fbsd-nat.c (super_resume): Move earlier next to "super_wait".
|
* fbsd-nat.c (super_resume): Move earlier next to "super_wait".
|
||||||
|
|
112
gdb/fbsd-nat.c
112
gdb/fbsd-nat.c
|
@ -530,13 +530,13 @@ fbsd_update_thread_list (struct target_ops *ops)
|
||||||
sake. FreeBSD versions newer than 9.1 contain both fixes.
|
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;
|
struct fbsd_fork_info *next;
|
||||||
ptid_t child; /* Pid of new child. */
|
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
|
/* Record a new child process event that is reported before the
|
||||||
corresponding fork event in the parent. */
|
corresponding fork event in the parent. */
|
||||||
|
@ -544,9 +544,9 @@ static struct fbsd_fork_child_info *fbsd_pending_children;
|
||||||
static void
|
static void
|
||||||
fbsd_remember_child (ptid_t pid)
|
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;
|
info->next = fbsd_pending_children;
|
||||||
fbsd_pending_children = info;
|
fbsd_pending_children = info;
|
||||||
}
|
}
|
||||||
|
@ -557,25 +557,74 @@ fbsd_remember_child (ptid_t pid)
|
||||||
static ptid_t
|
static ptid_t
|
||||||
fbsd_is_child_pending (pid_t pid)
|
fbsd_is_child_pending (pid_t pid)
|
||||||
{
|
{
|
||||||
struct fbsd_fork_child_info *info, *prev;
|
struct fbsd_fork_info *info, *prev;
|
||||||
ptid_t ptid;
|
ptid_t ptid;
|
||||||
|
|
||||||
prev = NULL;
|
prev = NULL;
|
||||||
for (info = fbsd_pending_children; info; prev = info, info = info->next)
|
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)
|
if (prev == NULL)
|
||||||
fbsd_pending_children = info->next;
|
fbsd_pending_children = info->next;
|
||||||
else
|
else
|
||||||
prev->next = info->next;
|
prev->next = info->next;
|
||||||
ptid = info->child;
|
ptid = info->ptid;
|
||||||
xfree (info);
|
xfree (info);
|
||||||
return ptid;
|
return ptid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null_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
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -616,6 +665,17 @@ static void
|
||||||
fbsd_resume (struct target_ops *ops,
|
fbsd_resume (struct target_ops *ops,
|
||||||
ptid_t ptid, int step, enum gdb_signal signo)
|
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)
|
if (debug_fbsd_lwp)
|
||||||
fprintf_unfiltered (gdb_stdlog,
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
@ -650,6 +710,12 @@ fbsd_wait (struct target_ops *ops,
|
||||||
|
|
||||||
while (1)
|
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);
|
wptid = super_wait (ops, ptid, ourstatus, target_options);
|
||||||
if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
|
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)
|
if (!follow_child && detach_fork)
|
||||||
{
|
{
|
||||||
struct thread_info *tp = inferior_thread ();
|
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);
|
pid_t child_pid = ptid_get_pid (tp->pending_follow.value.related_pid);
|
||||||
|
|
||||||
/* Breakpoints have already been detached from the child by
|
/* 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)
|
if (ptrace (PT_DETACH, child_pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
|
||||||
perror_with_name (("ptrace"));
|
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;
|
return 0;
|
||||||
|
|
Loading…
Reference in New Issue