From patchwork Mon Sep 29 18:07:52 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Don Breazeal X-Patchwork-Id: 3023 Received: (qmail 31930 invoked by alias); 29 Sep 2014 18:08:04 -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 31917 invoked by uid 89); 29 Sep 2014 18:08:02 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.8 required=5.0 tests=AWL, BAYES_00 autolearn=ham version=3.3.2 X-HELO: relay1.mentorg.com Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Mon, 29 Sep 2014 18:07:58 +0000 Received: from svr-orw-exc-10.mgc.mentorg.com ([147.34.98.58]) by relay1.mentorg.com with esmtp id 1XYfMY-0004qV-GV from Don_Breazeal@mentor.com ; Mon, 29 Sep 2014 11:07:54 -0700 Received: from [127.0.0.1] ([172.30.12.49]) by SVR-ORW-EXC-10.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Mon, 29 Sep 2014 11:07:53 -0700 Message-ID: <54299FF8.7000205@codesourcery.com> Date: Mon, 29 Sep 2014 11:07:52 -0700 From: "Breazeal, Don" User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.1.1 MIME-Version: 1.0 To: Pedro Alves , gdb-patches@sourceware.org Subject: Re: [PATCH 01/16 v2] Refactor native follow-fork References: <1407434395-19089-1-git-send-email-donb@codesourcery.com> <1408580964-27916-2-git-send-email-donb@codesourcery.com> <5409C69F.8030906@redhat.com> <540E41C5.2000600@codesourcery.com> <540EDFFE.4090703@redhat.com> <54132443.5060602@codesourcery.com> <5425ACBF.7080800@redhat.com> In-Reply-To: <5425ACBF.7080800@redhat.com> X-IsSubscribed: yes On 9/26/2014 11:13 AM, Pedro Alves wrote: > On 09/12/2014 05:50 PM, Breazeal, Don wrote: >> On 9/9/2014 4:09 AM, Pedro Alves wrote: >>> On 09/09/2014 12:54 AM, Breazeal, Don wrote: > >>> I'd rather not. That'll just add more technical debt. :-) E.g., >>> if we can't model this on the few native targets we have, then that'd >>> indicate that remote debugging against one of those targets (when >>> we get to gdb/gdbserver parity), wouldn't be correctly modelled, as >>> you'd be installing those methods in remote.c too. Plus, if we have >>> target_follow_fork_inferior as an extra method in addition >>> to target_follow_fork_inferior, and both are always called in >>> succession, then we might as well _not_ introduce a new target hook, >>> and just call infrun_follow_fork_inferior from the >>> top of linux-nat.c:linux_child_follow_fork, and the top of whatever >>> other target's to_follow_fork method that wants to use it. >> >> I've made modifications to inf_ttrace_follow_fork, inf_ttrace_wait, >> and inf_ptrace_follow_fork in the included patch that I think should >> allow them to work with follow_fork_inferior. Some other adjustments >> were necessary in inf-ttrace.c. Some of the changes weren't strictly >> necessary to get things working with follow_fork_inferior, but it >> seemed appropriate to make all the implementations more consistent. > > Thanks. > >>>> The changes to inf_ptrace_follow_fork to get it to work with >>>> follow_fork_inferior are straightforward. Making those changes to >>>> inf_ttrace_follow_fork is problematic, primarily because it handles the >>>> moral equivalent of the vfork-done event differently. >>> >>> Can you summarize the differences ? >> >> HP-UX apparently delivers a TTEVT_VFORK event for the parent inferior >> where Linux would report PTRACE_EVENT_VFORK_DONE -- when the child >> process has either execd or exited. inf_ttrace_follow_fork was handling >> the parent VFORK event in-line, where the Linux implementation expects >> a TARGET_WAITKIND_VFORK_DONE event to be reported so it can be handled >> in the generic event code. >> > > Right. Linux also used to handle that in-line, before 6c95b8df. > >> I've included annotated versions of the original implementations of >> inf_ttrace_follow_fork and inf-ptrace_follow_fork below for reference, >> to explain how I made decisions on the changes. Each annotation is >> a comment beginning with "BLOCK n". If you want to skip past all of >> that you can just search for "Don". :-) > > Excellent. That sure made it easier. > > >>> >> >> My assumptions about how HP-UX ttrace events work: >> - FORK: >> - TTEVT_FORK is reported for both the parent and child processes. >> The event can be reported for the parent or child in any order. >> >> - VFORK: >> - TTEVT_VFORK is reported for both the parent and child >> processes. >> - TTEVT_VFORK is reported for the child first. It is reported >> for the parent when the vfork is "done" as with the Linux >> PTRACE_EVENT_VFORK_DONE event, meaning that the parent has >> called exec or exited. See this comment in inf_ttrace_follow_fork: >> >> /* Wait till we get the TTEVT_VFORK event in the parent. >> This indicates that the child has called exec(3) or has >> exited and that the parent is ready to be traced again. */ >> >> The online HP-UX ttrace documentation doesn't really make this >> ordering explicit, but it doesn't contradict the implementation. > > Yeah, I can't imagine any way a TTEVT_VFORK could ever be reported to > the parent first. When the parent vforks, it blocks until the child > execs or exits. And the child won't do that until GDB resumes > the child after handling the TTEVT_VFORK that is reported for the > child. So the kernel must always report the child's TTEVT_VFORK first. > > >> if (follow_child) >> { >> struct thread_info *ti; >> @@ -533,17 +423,22 @@ inf_ttrace_follow_fork (struct target_ops *ops, >> int follow_child, >> inf_ttrace_num_lwps = 1; >> inf_ttrace_num_lwps_in_syscall = 0; >> >> - /* Delete parent. */ >> - delete_thread_silent (ptid_build (pid, lwpid, 0)); >> - detach_inferior (pid); >> - >> - /* Add child thread. inferior_ptid was already set above. */ >> - ti = add_thread_silent (inferior_ptid); >> + ti = find_thread_ptid (inferior_ptid); >> + gdb_assert (ti != NULL); > > Replace these with: > > ti = inferior_thread (); Done. > >> ti->private = >> xmalloc (sizeof (struct inf_ttrace_private_thread_info)); >> memset (ti->private, 0, >> sizeof (struct inf_ttrace_private_thread_info)); >> } >> + else >> + { >> + pid_t child_pid; >> + >> + /* Following parent. Detach child now. */ >> + child_pid = ptid_get_pid (tp->pending_follow.value.related_pid); >> + if (ttrace (TT_PROC_DETACH, child_pid, 0, 0, 0, 0) == -1) >> + perror_with_name (("ttrace")); >> + } >> >> return 0; >> } >> @@ -661,7 +556,6 @@ inf_ttrace_create_inferior (struct target_ops *ops, >> char *exec_file, >> gdb_assert (inf_ttrace_num_lwps_in_syscall == 0); >> gdb_assert (inf_ttrace_page_dict.count == 0); >> gdb_assert (inf_ttrace_reenable_page_protections == 0); >> - gdb_assert (inf_ttrace_vfork_ppid == -1); >> >> pid = fork_inferior (exec_file, allargs, env, inf_ttrace_me, NULL, >> inf_ttrace_prepare, NULL, NULL); >> @@ -772,7 +666,6 @@ inf_ttrace_attach (struct target_ops *ops, const >> char *args, int from_tty) >> >> gdb_assert (inf_ttrace_num_lwps == 0); >> gdb_assert (inf_ttrace_num_lwps_in_syscall == 0); >> - gdb_assert (inf_ttrace_vfork_ppid == -1); >> >> if (ttrace (TT_PROC_ATTACH, pid, 0, TT_KILL_ON_EXIT, TT_VERSION, 0) >> == -1) >> perror_with_name (("ttrace")); >> @@ -822,11 +715,12 @@ inf_ttrace_detach (struct target_ops *ops, const >> char *args, int from_tty) >> if (ttrace (TT_PROC_DETACH, pid, 0, 0, sig, 0) == -1) >> perror_with_name (("ttrace")); >> >> - if (inf_ttrace_vfork_ppid != -1) >> + if (current_inferior ()->vfork_parent != NULL) >> { >> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1) >> + pid_t ppid = current_inferior ()->vfork_parent->pid; >> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1) >> perror_with_name (("ttrace")); >> - inf_ttrace_vfork_ppid = -1; >> + detach_inferior (ppid); >> } > > I think we should just delete this block instead. > We shouldn't be blindly detaching from the parent here. > The user may well still want to debug it. The case of the > user doing "detach" on the child when the parent is waiting > for the the vfork-done should be handled by common code. > (and we should be going through the whole target_detach). > Done. >> >> inf_ttrace_num_lwps = 0; >> @@ -850,11 +744,12 @@ inf_ttrace_kill (struct target_ops *ops) >> perror_with_name (("ttrace")); >> /* ??? Is it necessary to call ttrace_wait() here? */ >> >> - if (inf_ttrace_vfork_ppid != -1) >> + if (current_inferior ()->vfork_parent != NULL) >> { >> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1) >> + pid_t ppid = current_inferior ()->vfork_parent->pid; >> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1) >> perror_with_name (("ttrace")); >> - inf_ttrace_vfork_ppid = -1; >> + detach_inferior (ppid); >> } > > This too. > Done. >> >> target_mourn_inferior (); >> @@ -967,20 +862,6 @@ inf_ttrace_wait (struct target_ops *ops, >> if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1) >> perror_with_name (("ttrace_wait")); >> >> - if (tts.tts_event == TTEVT_VFORK && tts.tts_u.tts_fork.tts_isparent) >> - { >> - if (inf_ttrace_vfork_ppid != -1) >> - { >> - gdb_assert (inf_ttrace_vfork_ppid == tts.tts_pid); >> - >> - if (ttrace (TT_PROC_DETACH, tts.tts_pid, 0, 0, 0, 0) == -1) >> - perror_with_name (("ttrace")); >> - inf_ttrace_vfork_ppid = -1; >> - } >> - >> - tts.tts_event = TTEVT_NONE; >> - } >> - >> clear_sigint_trap (); >> } >> while (tts.tts_event == TTEVT_NONE); >> @@ -1075,17 +956,23 @@ inf_ttrace_wait (struct target_ops *ops, >> break; >> >> case TTEVT_VFORK: >> - gdb_assert (!tts.tts_u.tts_fork.tts_isparent); >> - >> - related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid, >> - tts.tts_u.tts_fork.tts_flwpid, 0); >> + if (tts.tts_u.tts_fork.tts_isparent) >> + { >> + if (current_inferior ()->waiting_fork_vfork_done) > > I don't think we should have this waiting_fork_vfork_done check here. > This should always be a TARGET_WAITKIND_VFORK_DONE? You are right, at best this check was redundant. Removed it. > >> + ourstatus->kind = TARGET_WAITKIND_VFORK_DONE; >> + } >> + else >> + { >> + related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid, >> + tts.tts_u.tts_fork.tts_flwpid, 0); >> >> - ourstatus->kind = TARGET_WAITKIND_VFORKED; >> - ourstatus->value.related_pid = related_ptid; >> + ourstatus->kind = TARGET_WAITKIND_VFORKED; >> + ourstatus->value.related_pid = related_ptid; >> >> - /* HACK: To avoid touching the parent during the vfork, switch >> - away from it. */ >> - inferior_ptid = ptid; >> + /* HACK: To avoid touching the parent during the vfork, switch >> + away from it. */ >> + inferior_ptid = ptid; > > The core is now aware of the vfork parent inferior. Just delete > this hack. Done. > >> + } >> break; >> >> case TTEVT_LWP_CREATE: >> diff --git a/gdb/infrun.c b/gdb/infrun.c >> index c18267f..af9cbf8 100644 >> --- a/gdb/infrun.c >> +++ b/gdb/infrun.c >> @@ -60,6 +60,7 @@ >> #include "completer.h" >> #include "target-descriptions.h" >> #include "target-dcache.h" >> +#include "terminal.h" >> >> /* Prototypes for local functions */ >> >> @@ -79,6 +80,10 @@ static int restore_selected_frame (void *); >> >> static int follow_fork (void); >> >> +static int follow_fork_inferior (int follow_child, int detach_fork); >> + >> +static void follow_inferior_reset_breakpoints (void); >> + >> static void set_schedlock_func (char *args, int from_tty, >> struct cmd_list_element *c); >> >> @@ -486,9 +491,11 @@ follow_fork (void) >> parent = inferior_ptid; >> child = tp->pending_follow.value.related_pid; >> >> - /* Tell the target to do whatever is necessary to follow >> - either parent or child. */ >> - if (target_follow_fork (follow_child, detach_fork)) >> + /* Set up inferior(s) as specified by the caller, and tell the >> + target to do whatever is necessary to follow either parent >> + or child. */ >> + if (follow_fork_inferior (follow_child, detach_fork) >> + || target_follow_fork (follow_child, detach_fork)) > > I don't think we should ever call follow_fork_inferior without > calling target_follow_fork, right? I think it'd be clearer if > we tail called target_follow_fork inside follow_fork_inferior. Done. > >> { >> /* Target refused to follow, or there's some other reason >> we shouldn't resume. */ >> @@ -560,7 +567,242 @@ follow_fork (void) >> return should_resume; >> } >> >> -void >> +/* Handle changes to the inferior list based on the type of fork, >> + which process is being followed, and whether the other process >> + should be detached. On entry inferior_ptid must be the ptid of >> + the fork parent. At return inferior_ptid is the ptid of the >> + followed inferior. */ >> + >> +int >> +follow_fork_inferior (int follow_child, int detach_fork) >> +{ >> + int has_vforked; >> + int parent_pid, child_pid; >> + >> + has_vforked = (inferior_thread ()->pending_follow.kind >> + == TARGET_WAITKIND_VFORKED); >> + parent_pid = ptid_get_lwp (inferior_ptid); >> + if (parent_pid == 0) >> + parent_pid = ptid_get_pid (inferior_ptid); >> + child_pid >> + = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid); >> + >> + if (has_vforked >> + && !non_stop /* Non-stop always resumes both branches. */ >> + && (!target_is_async_p () || sync_execution) >> + && !(follow_child || detach_fork || sched_multi)) >> + { >> + /* The parent stays blocked inside the vfork syscall until the >> + child execs or exits. If we don't let the child run, then >> + the parent stays blocked. If we're telling the parent to run >> + in the foreground, the user will not be able to ctrl-c to get >> + back the terminal, effectively hanging the debug session. */ >> + fprintf_filtered (gdb_stderr, _("\ >> +Can not resume the parent process over vfork in the foreground while\n\ >> +holding the child stopped. Try \"set detach-on-fork\" or \ >> +\"set schedule-multiple\".\n")); >> + /* FIXME output string > 80 columns. */ >> + return 1; >> + } >> + > > Moving this to common code isn't strictly correct, as we'd probably > be able to interrupt the vfork parent in this case when remote > debugging, or not sharing the terminal with the inferior. But let's > put this here for now. > > >> + else >> + { >> + child_inf->aspace = new_address_space (); >> + child_inf->pspace = add_program_space (child_inf->aspace); >> + child_inf->removable = 1; >> + set_current_program_space (child_inf->pspace); >> + clone_program_space (child_inf->pspace, parent_inf->pspace); >> + >> + /* Let the shared library layer (solib-svr4) learn about > > It's not always solib-svr4, so make that e.g., "e.g., solib-svr4" now. Done. > >> + else >> + { >> + child_inf->aspace = new_address_space (); >> + child_inf->pspace = add_program_space (child_inf->aspace); >> + child_inf->removable = 1; >> + child_inf->symfile_flags = SYMFILE_NO_READ; >> + set_current_program_space (child_inf->pspace); >> + clone_program_space (child_inf->pspace, parent_pspace); >> + >> + /* Let the shared library layer (solib-svr4) learn about > > Likewise. Done. > > Otherwise this looks good to me. Pedro, thanks for this review. The updated patch is included below. I have one remaining question: I took this (above) to mean that the patch was approved with the requested changes, but since this is part of a series I'm not sure if I have the go-ahead to push it. The patch is standalone and I believe it has value independent of the rest of the series. OK to push? Thanks --Don gdb/ 2014-09-26 Don Breazeal * inf-ptrace.c (inf_ptrace_follow_fork): Remove target-independent code so as to work with follow_fork_inferior. * inf-ttrace.c (inf_ttrace_follow_fork): Ditto. (inf_ttrace_create_inferior): Remove reference to inf_ttrace_vfork_ppid. (inf_ttrace_attach): Ditto. (inf_ttrace_detach): Ditto. (inf_ttrace_kill): Use current_inferior instead of inf_ttrace_vfork_ppid. (inf_ttrace_wait): Eliminate use of inf_ttrace_vfork_ppid, report TARGET_WAITKIND_VFORK_DONE event, delete HACK that switched the inferior away from the parent. * infrun.c (follow_fork): Call follow_fork_inferior instead of target_follow_fork. (follow_fork_inferior): New function. (follow_inferior_reset_breakpoints): Make function static. * infrun.h (follow_inferior_reset_breakpoints): Remove declaration. * linux-nat.c (linux_child_follow_fork): Move target-independent code to infrun.c:follow_fork_inferior. --- gdb/inf-ptrace.c | 48 ++--------- gdb/inf-ttrace.c | 179 +++++---------------------------------- gdb/infrun.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++- gdb/infrun.h | 2 - gdb/linux-nat.c | 243 ++++++----------------------------------------------- 5 files changed, 298 insertions(+), 423 deletions(-) gdb_assert (linux_supports_tracefork () >= 0); @@ -628,98 +523,12 @@ holding the child stopped. Try \"set detach-on-fork\" or \ } else { - struct inferior *parent_inf, *child_inf; struct lwp_info *child_lp; - struct program_space *parent_pspace; - - if (info_verbose || debug_linux_nat) - { - target_terminal_ours (); - if (has_vforked) - fprintf_filtered (gdb_stdlog, - _("Attaching after process %d " - "vfork to child process %d.\n"), - parent_pid, child_pid); - else - fprintf_filtered (gdb_stdlog, - _("Attaching after process %d " - "fork to child process %d.\n"), - parent_pid, child_pid); - } - - /* Add the new inferior first, so that the target_detach below - doesn't unpush the target. */ - - child_inf = add_inferior (child_pid); - - parent_inf = current_inferior (); - child_inf->attach_flag = parent_inf->attach_flag; - copy_terminal_info (child_inf, parent_inf); - child_inf->gdbarch = parent_inf->gdbarch; - copy_inferior_target_desc_info (child_inf, parent_inf); - - parent_pspace = parent_inf->pspace; - - /* If we're vforking, we want to hold on to the parent until the - child exits or execs. At child exec or exit time we can - remove the old breakpoints from the parent and detach or - resume debugging it. Otherwise, detach the parent now; we'll - want to reuse it's program/address spaces, but we can't set - them to the child before removing breakpoints from the - parent, otherwise, the breakpoints module could decide to - remove breakpoints from the wrong process (since they'd be - assigned to the same address space). */ - if (has_vforked) - { - gdb_assert (child_inf->vfork_parent == NULL); - gdb_assert (parent_inf->vfork_child == NULL); - child_inf->vfork_parent = parent_inf; - child_inf->pending_detach = 0; - parent_inf->vfork_child = child_inf; - parent_inf->pending_detach = detach_fork; - parent_inf->waiting_for_vfork_done = 0; - } - else if (detach_fork) - target_detach (NULL, 0); - - /* Note that the detach above makes PARENT_INF dangling. */ - - /* Add the child thread to the appropriate lists, and switch to - this new thread, before cloning the program space, and - informing the solib layer about this new process. */ - - inferior_ptid = ptid_build (child_pid, child_pid, 0); - add_thread (inferior_ptid); child_lp = add_lwp (inferior_ptid); child_lp->stopped = 1; child_lp->last_resume_kind = resume_stop; - /* If this is a vfork child, then the address-space is shared - with the parent. If we detached from the parent, then we can - reuse the parent's program/address spaces. */ - if (has_vforked || detach_fork) - { - child_inf->pspace = parent_pspace; - child_inf->aspace = child_inf->pspace->aspace; - } - else - { - child_inf->aspace = new_address_space (); - child_inf->pspace = add_program_space (child_inf->aspace); - child_inf->removable = 1; - child_inf->symfile_flags = SYMFILE_NO_READ; - set_current_program_space (child_inf->pspace); - clone_program_space (child_inf->pspace, parent_pspace); - - /* Let the shared library layer (solib-svr4) learn about - this new process, relocate the cloned exec, pull in - shared libraries, and install the solib event breakpoint. - If a "cloned-VM" event was propagated better throughout - the core, this wouldn't be required. */ - solib_create_inferior_hook (0); - } - /* Let the thread_db layer learn about this new process. */ check_for_thread_db (); } diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c index dd71b3a..6eb8080 100644 --- a/gdb/inf-ptrace.c +++ b/gdb/inf-ptrace.c @@ -36,57 +36,21 @@ #ifdef PT_GET_PROCESS_STATE +/* Target hook for follow_fork. On entry and at return inferior_ptid is + the ptid of the followed inferior. */ + static int inf_ptrace_follow_fork (struct target_ops *ops, int follow_child, int detach_fork) { - pid_t pid, fpid; - ptrace_state_t pe; - - pid = ptid_get_pid (inferior_ptid); - - if (ptrace (PT_GET_PROCESS_STATE, pid, - (PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1) - perror_with_name (("ptrace")); - - gdb_assert (pe.pe_report_event == PTRACE_FORK); - fpid = pe.pe_other_pid; - - if (follow_child) + if (!follow_child) { - struct inferior *parent_inf, *child_inf; - struct thread_info *tp; - - parent_inf = find_inferior_pid (pid); - - /* Add the child. */ - child_inf = add_inferior (fpid); - child_inf->attach_flag = parent_inf->attach_flag; - copy_terminal_info (child_inf, parent_inf); - child_inf->pspace = parent_inf->pspace; - child_inf->aspace = parent_inf->aspace; + pid_t child_pid = inferior_thread->pending_follow.value.related_pid; - /* Before detaching from the parent, remove all breakpoints from - it. */ - remove_breakpoints (); - - if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, 0) == -1) - perror_with_name (("ptrace")); - - /* Switch inferior_ptid out of the parent's way. */ - inferior_ptid = pid_to_ptid (fpid); - - /* Delete the parent. */ - detach_inferior (pid); - - add_thread_silent (inferior_ptid); - } - else - { /* Breakpoints have already been detached from the child by infrun.c. */ - if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1) + if (ptrace (PT_DETACH, child_pid, (PTRACE_TYPE_ARG3)1, 0) == -1) perror_with_name (("ptrace")); } diff --git a/gdb/inf-ttrace.c b/gdb/inf-ttrace.c index 847beb3..dceea42 100644 --- a/gdb/inf-ttrace.c +++ b/gdb/inf-ttrace.c @@ -403,128 +403,18 @@ inf_ttrace_stopped_by_watchpoint (struct target_ops *ops) } -/* When tracking a vfork(2), we cannot detach from the parent until - after the child has called exec(3) or has exited. If we are still - attached to the parent, this variable will be set to the process ID - of the parent. Otherwise it will be set to zero. */ -static pid_t inf_ttrace_vfork_ppid = -1; +/* Target hook for follow_fork. On entry and at return inferior_ptid + is the ptid of the followed inferior. */ static int inf_ttrace_follow_fork (struct target_ops *ops, int follow_child, int detach_fork) { - pid_t pid, fpid; - lwpid_t lwpid, flwpid; - ttstate_t tts; struct thread_info *tp = inferior_thread (); gdb_assert (tp->pending_follow.kind == TARGET_WAITKIND_FORKED || tp->pending_follow.kind == TARGET_WAITKIND_VFORKED); - pid = ptid_get_pid (inferior_ptid); - lwpid = ptid_get_lwp (inferior_ptid); - - /* Get all important details that core GDB doesn't (and shouldn't) - know about. */ - if (ttrace (TT_LWP_GET_STATE, pid, lwpid, - (uintptr_t)&tts, sizeof tts, 0) == -1) - perror_with_name (("ttrace")); - - gdb_assert (tts.tts_event == TTEVT_FORK || tts.tts_event == TTEVT_VFORK); - - if (tts.tts_u.tts_fork.tts_isparent) - { - pid = tts.tts_pid; - lwpid = tts.tts_lwpid; - fpid = tts.tts_u.tts_fork.tts_fpid; - flwpid = tts.tts_u.tts_fork.tts_flwpid; - } - else - { - pid = tts.tts_u.tts_fork.tts_fpid; - lwpid = tts.tts_u.tts_fork.tts_flwpid; - fpid = tts.tts_pid; - flwpid = tts.tts_lwpid; - } - - if (follow_child) - { - struct inferior *inf; - struct inferior *parent_inf; - - parent_inf = find_inferior_pid (pid); - - inferior_ptid = ptid_build (fpid, flwpid, 0); - inf = add_inferior (fpid); - inf->attach_flag = parent_inf->attach_flag; - inf->pspace = parent_inf->pspace; - inf->aspace = parent_inf->aspace; - copy_terminal_info (inf, parent_inf); - detach_breakpoints (ptid_build (pid, lwpid, 0)); - - target_terminal_ours (); - fprintf_unfiltered (gdb_stdlog, - _("Attaching after fork to child process %ld.\n"), - (long)fpid); - } - else - { - inferior_ptid = ptid_build (pid, lwpid, 0); - /* Detach any remaining breakpoints in the child. In the case - of fork events, we do not need to do this, because breakpoints - should have already been removed earlier. */ - if (tts.tts_event == TTEVT_VFORK) - detach_breakpoints (ptid_build (fpid, flwpid, 0)); - - target_terminal_ours (); - fprintf_unfiltered (gdb_stdlog, - _("Detaching after fork from child process %ld.\n"), - (long)fpid); - } - - if (tts.tts_event == TTEVT_VFORK) - { - gdb_assert (!tts.tts_u.tts_fork.tts_isparent); - - if (follow_child) - { - /* We can't detach from the parent yet. */ - inf_ttrace_vfork_ppid = pid; - - reattach_breakpoints (fpid); - } - else - { - if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - - /* Wait till we get the TTEVT_VFORK event in the parent. - This indicates that the child has called exec(3) or has - exited and that the parent is ready to be traced again. */ - if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1) - perror_with_name (("ttrace_wait")); - gdb_assert (tts.tts_event == TTEVT_VFORK); - gdb_assert (tts.tts_u.tts_fork.tts_isparent); - - reattach_breakpoints (pid); - } - } - else - { - gdb_assert (tts.tts_u.tts_fork.tts_isparent); - - if (follow_child) - { - if (ttrace (TT_PROC_DETACH, pid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - } - else - { - if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - } - } - if (follow_child) { struct thread_info *ti; @@ -533,17 +423,21 @@ inf_ttrace_follow_fork (struct target_ops *ops, int follow_child, inf_ttrace_num_lwps = 1; inf_ttrace_num_lwps_in_syscall = 0; - /* Delete parent. */ - delete_thread_silent (ptid_build (pid, lwpid, 0)); - detach_inferior (pid); - - /* Add child thread. inferior_ptid was already set above. */ - ti = add_thread_silent (inferior_ptid); + ti = inferior_thread (); ti->private = xmalloc (sizeof (struct inf_ttrace_private_thread_info)); memset (ti->private, 0, sizeof (struct inf_ttrace_private_thread_info)); } + else + { + pid_t child_pid; + + /* Following parent. Detach child now. */ + child_pid = ptid_get_pid (tp->pending_follow.value.related_pid); + if (ttrace (TT_PROC_DETACH, child_pid, 0, 0, 0, 0) == -1) + perror_with_name (("ttrace")); + } return 0; } @@ -661,7 +555,6 @@ inf_ttrace_create_inferior (struct target_ops *ops, char *exec_file, gdb_assert (inf_ttrace_num_lwps_in_syscall == 0); gdb_assert (inf_ttrace_page_dict.count == 0); gdb_assert (inf_ttrace_reenable_page_protections == 0); - gdb_assert (inf_ttrace_vfork_ppid == -1); pid = fork_inferior (exec_file, allargs, env, inf_ttrace_me, NULL, inf_ttrace_prepare, NULL, NULL); @@ -772,7 +665,6 @@ inf_ttrace_attach (struct target_ops *ops, const char *args, int from_tty) gdb_assert (inf_ttrace_num_lwps == 0); gdb_assert (inf_ttrace_num_lwps_in_syscall == 0); - gdb_assert (inf_ttrace_vfork_ppid == -1); if (ttrace (TT_PROC_ATTACH, pid, 0, TT_KILL_ON_EXIT, TT_VERSION, 0) == -1) perror_with_name (("ttrace")); @@ -822,13 +714,6 @@ inf_ttrace_detach (struct target_ops *ops, const char *args, int from_tty) if (ttrace (TT_PROC_DETACH, pid, 0, 0, sig, 0) == -1) perror_with_name (("ttrace")); - if (inf_ttrace_vfork_ppid != -1) - { - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - inf_ttrace_vfork_ppid = -1; - } - inf_ttrace_num_lwps = 0; inf_ttrace_num_lwps_in_syscall = 0; @@ -850,13 +735,6 @@ inf_ttrace_kill (struct target_ops *ops) perror_with_name (("ttrace")); /* ??? Is it necessary to call ttrace_wait() here? */ - if (inf_ttrace_vfork_ppid != -1) - { - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - inf_ttrace_vfork_ppid = -1; - } - target_mourn_inferior (); } @@ -967,20 +845,6 @@ inf_ttrace_wait (struct target_ops *ops, if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1) perror_with_name (("ttrace_wait")); - if (tts.tts_event == TTEVT_VFORK && tts.tts_u.tts_fork.tts_isparent) - { - if (inf_ttrace_vfork_ppid != -1) - { - gdb_assert (inf_ttrace_vfork_ppid == tts.tts_pid); - - if (ttrace (TT_PROC_DETACH, tts.tts_pid, 0, 0, 0, 0) == -1) - perror_with_name (("ttrace")); - inf_ttrace_vfork_ppid = -1; - } - - tts.tts_event = TTEVT_NONE; - } - clear_sigint_trap (); } while (tts.tts_event == TTEVT_NONE); @@ -1075,17 +939,16 @@ inf_ttrace_wait (struct target_ops *ops, break; case TTEVT_VFORK: - gdb_assert (!tts.tts_u.tts_fork.tts_isparent); - - related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid, - tts.tts_u.tts_fork.tts_flwpid, 0); - - ourstatus->kind = TARGET_WAITKIND_VFORKED; - ourstatus->value.related_pid = related_ptid; + if (tts.tts_u.tts_fork.tts_isparent) + ourstatus->kind = TARGET_WAITKIND_VFORK_DONE; + else + { + related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid, + tts.tts_u.tts_fork.tts_flwpid, 0); - /* HACK: To avoid touching the parent during the vfork, switch - away from it. */ - inferior_ptid = ptid; + ourstatus->kind = TARGET_WAITKIND_VFORKED; + ourstatus->value.related_pid = related_ptid; + } break; case TTEVT_LWP_CREATE: diff --git a/gdb/infrun.c b/gdb/infrun.c index 5e123be..e8b1f01 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -60,6 +60,7 @@ #include "completer.h" #include "target-descriptions.h" #include "target-dcache.h" +#include "terminal.h" /* Prototypes for local functions */ @@ -79,6 +80,10 @@ static int restore_selected_frame (void *); static int follow_fork (void); +static int follow_fork_inferior (int follow_child, int detach_fork); + +static void follow_inferior_reset_breakpoints (void); + static void set_schedlock_func (char *args, int from_tty, struct cmd_list_element *c); @@ -486,9 +491,10 @@ follow_fork (void) parent = inferior_ptid; child = tp->pending_follow.value.related_pid; - /* Tell the target to do whatever is necessary to follow - either parent or child. */ - if (target_follow_fork (follow_child, detach_fork)) + /* Set up inferior(s) as specified by the caller, and tell the + target to do whatever is necessary to follow either parent + or child. */ + if (follow_fork_inferior (follow_child, detach_fork)) { /* Target refused to follow, or there's some other reason we shouldn't resume. */ @@ -560,7 +566,242 @@ follow_fork (void) return should_resume; } -void +/* Handle changes to the inferior list based on the type of fork, + which process is being followed, and whether the other process + should be detached. On entry inferior_ptid must be the ptid of + the fork parent. At return inferior_ptid is the ptid of the + followed inferior. */ + +int +follow_fork_inferior (int follow_child, int detach_fork) +{ + int has_vforked; + int parent_pid, child_pid; + + has_vforked = (inferior_thread ()->pending_follow.kind + == TARGET_WAITKIND_VFORKED); + parent_pid = ptid_get_lwp (inferior_ptid); + if (parent_pid == 0) + parent_pid = ptid_get_pid (inferior_ptid); + child_pid + = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid); + + if (has_vforked + && !non_stop /* Non-stop always resumes both branches. */ + && (!target_is_async_p () || sync_execution) + && !(follow_child || detach_fork || sched_multi)) + { + /* The parent stays blocked inside the vfork syscall until the + child execs or exits. If we don't let the child run, then + the parent stays blocked. If we're telling the parent to run + in the foreground, the user will not be able to ctrl-c to get + back the terminal, effectively hanging the debug session. */ + fprintf_filtered (gdb_stderr, _("\ +Can not resume the parent process over vfork in the foreground while\n\ +holding the child stopped. Try \"set detach-on-fork\" or \ +\"set schedule-multiple\".\n")); + /* FIXME output string > 80 columns. */ + return 1; + } + + if (!follow_child) + { + /* Detach new forked process? */ + if (detach_fork) + { + struct cleanup *old_chain; + + /* Before detaching from the child, remove all breakpoints + from it. If we forked, then this has already been taken + care of by infrun.c. If we vforked however, any + breakpoint inserted in the parent is visible in the + child, even those added while stopped in a vfork + catchpoint. This will remove the breakpoints from the + parent also, but they'll be reinserted below. */ + if (has_vforked) + { + /* Keep breakpoints list in sync. */ + remove_breakpoints_pid (ptid_get_pid (inferior_ptid)); + } + + if (info_verbose || debug_infrun) + { + target_terminal_ours (); + fprintf_filtered (gdb_stdlog, + "Detaching after fork from " + "child process %d.\n", + child_pid); + } + } + else + { + struct inferior *parent_inf, *child_inf; + struct cleanup *old_chain; + + /* Add process to GDB's tables. */ + child_inf = add_inferior (child_pid); + + parent_inf = current_inferior (); + child_inf->attach_flag = parent_inf->attach_flag; + copy_terminal_info (child_inf, parent_inf); + child_inf->gdbarch = parent_inf->gdbarch; + copy_inferior_target_desc_info (child_inf, parent_inf); + + old_chain = save_inferior_ptid (); + save_current_program_space (); + + inferior_ptid = ptid_build (child_pid, child_pid, 0); + add_thread (inferior_ptid); + child_inf->symfile_flags = SYMFILE_NO_READ; + + /* If this is a vfork child, then the address-space is + shared with the parent. */ + if (has_vforked) + { + child_inf->pspace = parent_inf->pspace; + child_inf->aspace = parent_inf->aspace; + + /* The parent will be frozen until the child is done + with the shared region. Keep track of the + parent. */ + child_inf->vfork_parent = parent_inf; + child_inf->pending_detach = 0; + parent_inf->vfork_child = child_inf; + parent_inf->pending_detach = 0; + } + else + { + child_inf->aspace = new_address_space (); + child_inf->pspace = add_program_space (child_inf->aspace); + child_inf->removable = 1; + set_current_program_space (child_inf->pspace); + clone_program_space (child_inf->pspace, parent_inf->pspace); + + /* Let the shared library layer (e.g., solib-svr4) learn + about this new process, relocate the cloned exec, pull + in shared libraries, and install the solib event + breakpoint. If a "cloned-VM" event was propagated + better throughout the core, this wouldn't be + required. */ + solib_create_inferior_hook (0); + } + + do_cleanups (old_chain); + } + + if (has_vforked) + { + struct inferior *parent_inf; + + parent_inf = current_inferior (); + + /* If we detached from the child, then we have to be careful + to not insert breakpoints in the parent until the child + is done with the shared memory region. However, if we're + staying attached to the child, then we can and should + insert breakpoints, so that we can debug it. A + subsequent child exec or exit is enough to know when does + the child stops using the parent's address space. */ + parent_inf->waiting_for_vfork_done = detach_fork; + parent_inf->pspace->breakpoints_not_allowed = detach_fork; + } + } + else + { + /* Follow the child. */ + struct inferior *parent_inf, *child_inf; + struct program_space *parent_pspace; + + if (info_verbose || debug_infrun) + { + target_terminal_ours (); + if (has_vforked) + fprintf_filtered (gdb_stdlog, + _("Attaching after process %d " + "vfork to child process %d.\n"), + parent_pid, child_pid); + else + fprintf_filtered (gdb_stdlog, + _("Attaching after process %d " + "fork to child process %d.\n"), + parent_pid, child_pid); + } + + /* Add the new inferior first, so that the target_detach below + doesn't unpush the target. */ + + child_inf = add_inferior (child_pid); + + parent_inf = current_inferior (); + child_inf->attach_flag = parent_inf->attach_flag; + copy_terminal_info (child_inf, parent_inf); + child_inf->gdbarch = parent_inf->gdbarch; + copy_inferior_target_desc_info (child_inf, parent_inf); + + parent_pspace = parent_inf->pspace; + + /* If we're vforking, we want to hold on to the parent until the + child exits or execs. At child exec or exit time we can + remove the old breakpoints from the parent and detach or + resume debugging it. Otherwise, detach the parent now; we'll + want to reuse it's program/address spaces, but we can't set + them to the child before removing breakpoints from the + parent, otherwise, the breakpoints module could decide to + remove breakpoints from the wrong process (since they'd be + assigned to the same address space). */ + + if (has_vforked) + { + gdb_assert (child_inf->vfork_parent == NULL); + gdb_assert (parent_inf->vfork_child == NULL); + child_inf->vfork_parent = parent_inf; + child_inf->pending_detach = 0; + parent_inf->vfork_child = child_inf; + parent_inf->pending_detach = detach_fork; + parent_inf->waiting_for_vfork_done = 0; + } + else if (detach_fork) + target_detach (NULL, 0); + + /* Note that the detach above makes PARENT_INF dangling. */ + + /* Add the child thread to the appropriate lists, and switch to + this new thread, before cloning the program space, and + informing the solib layer about this new process. */ + + inferior_ptid = ptid_build (child_pid, child_pid, 0); + add_thread (inferior_ptid); + + /* If this is a vfork child, then the address-space is shared + with the parent. If we detached from the parent, then we can + reuse the parent's program/address spaces. */ + if (has_vforked || detach_fork) + { + child_inf->pspace = parent_pspace; + child_inf->aspace = child_inf->pspace->aspace; + } + else + { + child_inf->aspace = new_address_space (); + child_inf->pspace = add_program_space (child_inf->aspace); + child_inf->removable = 1; + child_inf->symfile_flags = SYMFILE_NO_READ; + set_current_program_space (child_inf->pspace); + clone_program_space (child_inf->pspace, parent_pspace); + + /* Let the shared library layer (e.g., solib-svr4) learn + about this new process, relocate the cloned exec, pull in + shared libraries, and install the solib event breakpoint. + If a "cloned-VM" event was propagated better throughout + the core, this wouldn't be required. */ + solib_create_inferior_hook (0); + } + } + + return target_follow_fork (follow_child, detach_fork); +} + +static void follow_inferior_reset_breakpoints (void) { struct thread_info *tp = inferior_thread (); diff --git a/gdb/infrun.h b/gdb/infrun.h index cc9cb33..fb6276b 100644 --- a/gdb/infrun.h +++ b/gdb/infrun.h @@ -115,8 +115,6 @@ extern void insert_step_resume_breakpoint_at_sal (struct gdbarch *, struct symtab_and_line , struct frame_id); -extern void follow_inferior_reset_breakpoints (void); - /* Returns true if we're trying to step past the instruction at ADDRESS in ASPACE. */ extern int stepping_past_instruction_at (struct address_space *aspace, diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 0fe4b0b..df830ae 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -54,7 +54,6 @@ #include #include #include "xml-support.h" -#include "terminal.h" #include #include "solib.h" #include "nat/linux-osdata.h" @@ -369,79 +368,41 @@ delete_lwp_cleanup (void *lp_voidp) delete_lwp (lp->ptid); } +/* Target hook for follow_fork. On entry inferior_ptid must be the + ptid of the followed inferior. At return, inferior_ptid will be + unchanged. */ + static int linux_child_follow_fork (struct target_ops *ops, int follow_child, int detach_fork) { - int has_vforked; - int parent_pid, child_pid; - - has_vforked = (inferior_thread ()->pending_follow.kind - == TARGET_WAITKIND_VFORKED); - parent_pid = ptid_get_lwp (inferior_ptid); - if (parent_pid == 0) - parent_pid = ptid_get_pid (inferior_ptid); - child_pid - = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid); - - if (has_vforked - && !non_stop /* Non-stop always resumes both branches. */ - && (!target_is_async_p () || sync_execution) - && !(follow_child || detach_fork || sched_multi)) - { - /* The parent stays blocked inside the vfork syscall until the - child execs or exits. If we don't let the child run, then - the parent stays blocked. If we're telling the parent to run - in the foreground, the user will not be able to ctrl-c to get - back the terminal, effectively hanging the debug session. */ - fprintf_filtered (gdb_stderr, _("\ -Can not resume the parent process over vfork in the foreground while\n\ -holding the child stopped. Try \"set detach-on-fork\" or \ -\"set schedule-multiple\".\n")); - /* FIXME output string > 80 columns. */ - return 1; - } - - if (! follow_child) + if (!follow_child) { struct lwp_info *child_lp = NULL; + int status = W_STOPCODE (0); + struct cleanup *old_chain; + int has_vforked; + int parent_pid, child_pid; + + has_vforked = (inferior_thread ()->pending_follow.kind + == TARGET_WAITKIND_VFORKED); + parent_pid = ptid_get_lwp (inferior_ptid); + if (parent_pid == 0) + parent_pid = ptid_get_pid (inferior_ptid); + child_pid + = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid); + /* We're already attached to the parent, by default. */ + old_chain = save_inferior_ptid (); + inferior_ptid = ptid_build (child_pid, child_pid, 0); + child_lp = add_lwp (inferior_ptid); + child_lp->stopped = 1; + child_lp->last_resume_kind = resume_stop; /* Detach new forked process? */ if (detach_fork) { - struct cleanup *old_chain; - int status = W_STOPCODE (0); - - /* Before detaching from the child, remove all breakpoints - from it. If we forked, then this has already been taken - care of by infrun.c. If we vforked however, any - breakpoint inserted in the parent is visible in the - child, even those added while stopped in a vfork - catchpoint. This will remove the breakpoints from the - parent also, but they'll be reinserted below. */ - if (has_vforked) - { - /* keep breakpoints list in sync. */ - remove_breakpoints_pid (ptid_get_pid (inferior_ptid)); - } - - if (info_verbose || debug_linux_nat) - { - target_terminal_ours (); - fprintf_filtered (gdb_stdlog, - "Detaching after fork from " - "child process %d.\n", - child_pid); - } - - old_chain = save_inferior_ptid (); - inferior_ptid = ptid_build (child_pid, child_pid, 0); - - child_lp = add_lwp (inferior_ptid); - child_lp->stopped = 1; - child_lp->last_resume_kind = resume_stop; make_cleanup (delete_lwp_cleanup, child_lp); if (linux_nat_prepare_to_resume != NULL) @@ -476,86 +437,20 @@ holding the child stopped. Try \"set detach-on-fork\" or \ ptrace (PTRACE_DETACH, child_pid, 0, signo); } + /* Resets value of inferior_ptid to parent ptid. */ do_cleanups (old_chain); } else { - struct inferior *parent_inf, *child_inf; - struct cleanup *old_chain; - - /* Add process to GDB's tables. */ - child_inf = add_inferior (child_pid); - - parent_inf = current_inferior (); - child_inf->attach_flag = parent_inf->attach_flag; - copy_terminal_info (child_inf, parent_inf); - child_inf->gdbarch = parent_inf->gdbarch; - copy_inferior_target_desc_info (child_inf, parent_inf); - - old_chain = save_inferior_ptid (); - save_current_program_space (); - - inferior_ptid = ptid_build (child_pid, child_pid, 0); - add_thread (inferior_ptid); - child_lp = add_lwp (inferior_ptid); - child_lp->stopped = 1; - child_lp->last_resume_kind = resume_stop; - child_inf->symfile_flags = SYMFILE_NO_READ; - - /* If this is a vfork child, then the address-space is - shared with the parent. */ - if (has_vforked) - { - child_inf->pspace = parent_inf->pspace; - child_inf->aspace = parent_inf->aspace; - - /* The parent will be frozen until the child is done - with the shared region. Keep track of the - parent. */ - child_inf->vfork_parent = parent_inf; - child_inf->pending_detach = 0; - parent_inf->vfork_child = child_inf; - parent_inf->pending_detach = 0; - } - else - { - child_inf->aspace = new_address_space (); - child_inf->pspace = add_program_space (child_inf->aspace); - child_inf->removable = 1; - set_current_program_space (child_inf->pspace); - clone_program_space (child_inf->pspace, parent_inf->pspace); - - /* Let the shared library layer (solib-svr4) learn about - this new process, relocate the cloned exec, pull in - shared libraries, and install the solib event - breakpoint. If a "cloned-VM" event was propagated - better throughout the core, this wouldn't be - required. */ - solib_create_inferior_hook (0); - } - /* Let the thread_db layer learn about this new process. */ check_for_thread_db (); - - do_cleanups (old_chain); } + do_cleanups (old_chain); + if (has_vforked) { struct lwp_info *parent_lp; - struct inferior *parent_inf; - - parent_inf = current_inferior (); - - /* If we detached from the child, then we have to be careful - to not insert breakpoints in the parent until the child - is done with the shared memory region. However, if we're - staying attached to the child, then we can and should - insert breakpoints, so that we can debug it. A - subsequent child exec or exit is enough to know when does - the child stops using the parent's address space. */ - parent_inf->waiting_for_vfork_done = detach_fork; - parent_inf->pspace->breakpoints_not_allowed = detach_fork; parent_lp = find_lwp_pid (pid_to_ptid (parent_pid));