From patchwork Tue Jun 28 23:00:11 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Baldwin X-Patchwork-Id: 13459 Received: (qmail 101671 invoked by alias); 28 Jun 2016 23:00:44 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 100939 invoked by uid 89); 28 Jun 2016 23:00:37 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-0.5 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, SPF_SOFTFAIL autolearn=no version=3.3.2 spammy=8016, resumed, 91, HX-Greylist:EDT X-HELO: bigwig.baldwin.cx Received: from bigwig.baldwin.cx (HELO bigwig.baldwin.cx) (96.47.65.170) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (CAMELLIA256-SHA encrypted) ESMTPS; Tue, 28 Jun 2016 23:00:34 +0000 Received: from ralph.com (c-73-231-226-104.hsd1.ca.comcast.net [73.231.226.104]) by bigwig.baldwin.cx (Postfix) with ESMTPSA id 73E87B9A8 for ; Tue, 28 Jun 2016 19:00:32 -0400 (EDT) From: John Baldwin To: gdb-patches@sourceware.org Subject: [PATCH 3/3] Fake VFORK_DONE events when following only the parent after a vfork. Date: Tue, 28 Jun 2016 16:00:11 -0700 Message-Id: <20160628230011.80849-4-jhb@FreeBSD.org> In-Reply-To: <20160628230011.80849-1-jhb@FreeBSD.org> References: <20160628230011.80849-1-jhb@FreeBSD.org> X-IsSubscribed: yes 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) : Rename to ... (struct fbsd_fork_info) : ... 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. --- gdb/ChangeLog | 18 ++++++++++ gdb/fbsd-nat.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 09b75a5..0ea329a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,23 @@ 2016-06-24 John Baldwin + * fbsd-nat.c (struct fbsd_fork_child_info): Rename to ... + (struct fbsd_fork_info): ... this. + (struct fbsd_fork_info) : Rename to ... + (struct fbsd_fork_info) : ... 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-06-24 John Baldwin + * fbsd-nat.c (super_resume): Move earlier next to "super_wait". (resume_one_thread_cb): Move below fork following helper code. (resume_all_threads_cb): Likewise. diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c index 4b2b41a..f1dc743 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) { @@ -794,6 +860,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 @@ -801,6 +868,31 @@ 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). + + Take the same approach used in linux-nat.c of just + sleeping and hoping the sleep is long enough. */ + + usleep (10000); + + /* Schedule a fake VFORK_DONE event to report on the next + wait. */ + fbsd_add_vfork_done (inferior_ptid); + } } return 0;