aboutsummaryrefslogtreecommitdiff
path: root/gdb/fbsd-nat.c
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2016-06-24 21:00:04 -0700
committerJohn Baldwin <jhb@FreeBSD.org>2016-07-01 08:35:33 -0700
commit2c5c2a3321706c28cbf1b85a970a2e32912eb0c8 (patch)
tree8bf821a89de943c5fb9718649ce00efbfcc6b5be /gdb/fbsd-nat.c
parent8607ea632c806235554aa2336cf01bf3758c1264 (diff)
downloadgdb-2c5c2a3321706c28cbf1b85a970a2e32912eb0c8.zip
gdb-2c5c2a3321706c28cbf1b85a970a2e32912eb0c8.tar.gz
gdb-2c5c2a3321706c28cbf1b85a970a2e32912eb0c8.tar.bz2
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.
Diffstat (limited to 'gdb/fbsd-nat.c')
-rw-r--r--gdb/fbsd-nat.c112
1 files changed, 103 insertions, 9 deletions
diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
index daf4580..fcb7ff5 100644
--- a/gdb/fbsd-nat.c
+++ b/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.
*/
-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;