From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 8859 invoked by alias); 22 Sep 2014 15:53:03 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 8839 invoked by uid 89); 22 Sep 2014 15:53: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, 22 Sep 2014 15:52:57 +0000 Received: from svr-orw-exc-10.mgc.mentorg.com ([147.34.98.58]) by relay1.mentorg.com with esmtp id 1XW5v3-0004ff-Qq from Don_Breazeal@mentor.com ; Mon, 22 Sep 2014 08:52:53 -0700 Received: from [127.0.0.1] ([172.30.10.242]) by SVR-ORW-EXC-10.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Mon, 22 Sep 2014 08:52:52 -0700 Message-ID: <542045D5.3040903@codesourcery.com> Date: Mon, 22 Sep 2014 15:53:00 -0000 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> In-Reply-To: <54132443.5060602@codesourcery.com> Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: 7bit X-IsSubscribed: yes X-SW-Source: 2014-09/txt/msg00664.txt.bz2 On 9/12/2014 9:50 AM, Breazeal, Don wrote: > On 9/9/2014 4:09 AM, Pedro Alves wrote: >> On 09/09/2014 12:54 AM, Breazeal, Don wrote: >>> Hi Pedro, >>> >>> I've consolidated my responses to the issues you raised in this email, >>> and attached an updated patch containing the proposed solutions. >>> >>> On 9/5/2014 7:20 AM, Pedro Alves wrote: >>>> linux_child_follow_fork ends up with: >>>> >>>> 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 (!follow_child) >>>> { >>>> ... >>>> } >>>> else >>>> { >>>> struct lwp_info *child_lp; >>>> >>>> child_lp = add_lwp (inferior_ptid); >>>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >>>> child_lp->stopped = 1; >>>> child_lp->last_resume_kind = resume_stop; >>>> >>>> /* Let the thread_db layer learn about this new process. */ >>>> check_for_thread_db (); >>>> } >>>> } >>>> >>>> Nothing appears to switch inferior_ptid to the child, so seems >>>> like we're adding the child_lp with the wrong lwp (and calling >>>> check_for_thread_db in the wrong context) ? Is this managing >>>> to work by chance because follow_fork_inferior leaves inferior_ptid >>>> pointing to the child? >>> >>> Yes, follow_fork_inferior always sets inferior_ptid to the followed >>> inferior. Then on entry, linux_child_follow_fork expects inferior_ptid >>> to be the followed inferior. So I think it is getting the expected >>> inferior from inferior_ptid in these cases. >>> >>> In the original code, inferior_ptid was the parent on entry to >>> target_follow_fork, and the followed inferior after return. I made >>> follow_fork_inferior do the same thing (return the followed inferior), >>> but it would be no problem to have it return the parent and have >>> linux_child_follow_fork expect inferior_ptid to be the parent and return >>> the followed inferior (like it did before) if that would be preferable. >>> Let me know if you'd like me to make that change. >>> >>> Regarding check_for_thread_db, there is something unrelated that I don't >>> understand. Maybe you can clear this up for me. If we have reached >>> linux_child_follow_fork, then aren't we guaranteed that >>> PTRACE_O_TRACECLONE is supported, and that we are using that instead of >>> libthread_db for detecting thread events? If so, why do we need to call >>> check_for_thread_db at all? >>> >>> Then this at the top uses the wrong >>>> inferior_thread (): >>>> >>>> has_vforked = (inferior_thread ()->pending_follow.kind >>>> == TARGET_WAITKIND_VFORKED); >>>> >>>> >>>> and we're lucky that nothing end up using has_vforked in the >>>> follow child path? >>> >>> You are right, this is incorrect and unnecessary in the case where we >>> are following the child. >>> >>>> >>>> I'd much rather we don't have these assumptions in place. >>> >>> Would an acceptable solution be to move the definitions and assignments >>> of has_vforked, parent_pid, and child_pid into the follow-parent case, >>> as below? > > This change was made per your previous comments, and comments added to > specify how inferior_ptid is changed by follow_fork_inferior and the > target follow_fork routines. > >>> >>> static int >>> linux_child_follow_fork (struct target_ops *ops, int follow_child, >>> int detach_fork) >>> { >>> 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 >>>> >>>> These files / targets also have to_follow_fork implementations: >>>> >>>> inf-ptrace.c: t->to_follow_fork = inf_ptrace_follow_fork; >>>> inf-ttrace.c: t->to_follow_fork = inf_ttrace_follow_fork; >>>> >>>> which will break if we don't adjust them as well. Did you >>>> check whether the refactored code (follow_fork_inferior) >>>> makes sense for those? >>> >>> I completely missed these; sorry about that. In my initial response to >>> your review, I thought I should be able to make similar changes to these >>> functions that maintained the existing functionality. After digging into >>> this, I still think it is possible, but that a more prudent approach >>> would be to make follow_fork_inferior into a new target hook that just >>> returns zero for the non-Linux cases. >> >> 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. > >> >>> 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. > >> >>> The changes >>> required to make it work with follow_fork_inferior are more significant >>> than I'd like, given that I don't have any way to test how the OS >>> reports the events. >> >> Don't worry so much about the testing. We can go best effort, and >> if someone can help with testing, good. If not, I'd still push >> forward and have interested parties fix things up when they >> stumble on issues. I'm mostly interested in checking whether >> the model we're committing to makes sense and is at the right level. > > I haven't tested the changes to either of inf-ptrace.c or inf-ttrace.c, > or even built them, lacking a suitable environment. I did re-test on > x64 Ubuntu. > > 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". :-) > >> >> Thanks, >> Pedro Alves >> > > 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. > > =============================================== > Annotated version of inf_ttrace_follow_fork: > > /* 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; > > 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); > > /* BLOCK 1: Since we know that on entry inferior_ptid will be the > ptid of the followed inferior, we can use that to derive the pid > information we need without any system calls. Delete this block, > use inferior_ptid for the child ptid when following the child, and > use inferior_thread ()->pending_follow to find the child ptid > when following the parent. */ > /* 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; > } > /* BLOCK 1 END. */ > > if (follow_child) > { > /* BLOCK 2: this block is replaced almost verbatim by equivalent > code in follow_fork_inferior. We delete this block, except > for the line that switches inferior_ptid to the 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); > /* END BLOCK 2. */ > > /* BLOCK 3: inf_ttrace_follow_fork deals with breakpoints by > using detach_breakpoints on the unfollowed inferior, then just > before returning it calls detach_inferior to clean up all of the > data structures related to the inferior, including the breakpoint > structures associated withthe inferior. By contrast, > follow_fork_inferior calls remove_breakpoints_pid to clean up > the structures immediately, and it leaves the inferior intact > after detaching from it. This allows the user to go back and > run the inferior later. Note that follow_fork_inferior cleans up > the data structures for an unfollowed child, because from the user > standpoint it was never attached. > > The bottom line is that we can delete this call, and the other > detach/reattach_breakpoints calls in this function. */ > detach_breakpoints (ptid_build (pid, lwpid, 0)); > /* END BLOCK 3. */ > > /* BLOCK 4: This output is replaced by code in follow_fork_inferior. > The only difference is that in follow_fork_inferior, the user must > enable the output via 'set verbose' or 'set debug infrun 1'. So > we delete this block. */ > target_terminal_ours (); > fprintf_unfiltered (gdb_stdlog, > _("Attaching after fork to child process %ld.\n"), > (long)fpid); > /* END BLOCK 4. */ > } > else > { > /* BLOCK 5: We don't need to do this, since we are guaranteed that > inferior_ptid already contained the parent's ptid on entry. So > we delete this. */ > inferior_ptid = ptid_build (pid, lwpid, 0); > /* END BLOCK 5. */ > > /* BLOCK 6: As in BLOCK 3, removal and cleanup of breakpoints is > handled by follow_fork_inferior, and as in BLOCK 4, the output > is also handled by follow_fork_inferior. Delete this block. */ > /* 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); > /* END BLOCK 6. */ > } > > if (tts.tts_event == TTEVT_VFORK) > { > gdb_assert (!tts.tts_u.tts_fork.tts_isparent); > > if (follow_child) > { > /* BLOCK 7: This is the moral equivalent of struct > inferior::vfork_parent. When we report the vfork (child) > event to infrun.c, follow_fork_inferior will set up the > inferiors with all the info (vfork_parent, pending_detach, > vfork_child) required to know how and when to detach the > unfollowed vfork parent. In inf_ttrace_wait we will need > to report TARGET_WAITKIND_VFORK_DONE to infrun.c for > TTEVT_VFORK events reported for the parent. We can delete > this block and eliminate the variable entirely. */ > /* We can't detach from the parent yet. */ > inf_ttrace_vfork_ppid = pid; > /* END BLOCK 7. */ > > /* BLOCK 8: as before, let follow_fork_inferior manage the > breakpoints. Delete this. */ > reattach_breakpoints (fpid); > /* END BLOCK 8. */ > } > else > { > /* BLOCK 9: All of the target follow fork functions are > expected to perform the detach from an unfollowed fork > child. (Not just vfork.) We may be able to consolidate > with detach of regular fork child. */ > if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1) > perror_with_name (("ttrace")); > /* END BLOCK 9. */ > > /* BLOCK 10: We want to let the generic event handling code > deal with this. Modify inf_ttrace_wait vfork event handler > to help with this. */ > /* 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); > /* END BLOCK 10. */ > > /* BLOCK 11: as before, let follow_fork_inferior manage the > breakpoints. Delete this. */ > reattach_breakpoints (pid); > /* END BLOCK 11. */ > } > } > else > { > gdb_assert (tts.tts_u.tts_fork.tts_isparent); > > if (follow_child) > { > /* BLOCK 12: follow_fork_inferior takes care of detaching the > parent when it calls target_detach. Delete this. */ > if (ttrace (TT_PROC_DETACH, pid, 0, 0, 0, 0) == -1) > perror_with_name (("ttrace")); > /* END BLOCK 12. */ > } > else > { > /* BLOCK 13: This is like BLOCK 9 - we need to detach all > unfollowed child processes. Keep this in some form. */ > if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1) > perror_with_name (("ttrace")); > /* END BLOCK 13. */ > } > } > > if (follow_child) > { > struct thread_info *ti; > > /* BLOCK 14: These variables are used throughout this file, so > we need to keep this. */ > /* The child will start out single-threaded. */ > inf_ttrace_num_lwps = 1; > inf_ttrace_num_lwps_in_syscall = 0; > /* END BLOCK 14. */ > > /* BLOCK 15: This is handled by follow_fork_inferior when it > calls target_detach on the parent, and we don't want to delete > the parent inferior, but leave it in the list for possible re-run > later on. */ > /* 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); > /* END BLOCK 15. */ > > /* BLOCK 16: We need to keep this target-specific code. */ > ti->private = > xmalloc (sizeof (struct inf_ttrace_private_thread_info)); > memset (ti->private, 0, > sizeof (struct inf_ttrace_private_thread_info)); > /* END BLOCK 16. */ > } > > return 0; > } > > =============================================== > Annotated version of inf_ptrace_follow_fork: > > static int > inf_ptrace_follow_fork (struct target_ops *ops, int follow_child, > int detach_fork) > { > /* BLOCK 1: Since we know that on entry inferior_ptid will be the > ptid of the followed inferior, we can use that to derive the pid > information we need without any system calls. Delete this block, > use inferior_ptid for the child ptid when following the child, and > use inferior_thread ()->pending_follow to find the child ptid > when following the parent. */ > 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; > /* BLOCK 1. */ > > if (follow_child) > { > /* BLOCK 2: This is all done in follow_fork_inferior, so delete. */ > 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; > > /* 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")); > /* END BLOCK 2 */ > > /* BLOCK 3: We must switch inferior_ptid to the child, so keep. */ > /* Switch inferior_ptid out of the parent's way. */ > inferior_ptid = pid_to_ptid (fpid); > /* END BLOCK 3. */ > > /* BLOCK 4: We don't detach the inferior, and the thread is added > by follow_fork_inferior. */ > /* Delete the parent. */ > detach_inferior (pid); > > add_thread_silent (inferior_ptid); > /* END BLOCK 4. */ > } > else > { > /* BLOCK 5: We are required to detach the child, so keep. */ > /* Breakpoints have already been detached from the child by > infrun.c. */ > > if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1) > perror_with_name (("ptrace")); > /* END BLOCK 5. */ > } > > return 0; > } > > ============================================= > > Thanks, > --Don > > gdb/ > 2014-09-12 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. > * infrun.c (follow_fork): Call follow_fork_inferior. > (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 | 185 ++++++++-------------------------------- > gdb/infrun.c | 250 > +++++++++++++++++++++++++++++++++++++++++++++++++++++- > gdb/infrun.h | 2 - > gdb/linux-nat.c | 243 > ++++++---------------------------------------------- > 5 files changed, 314 insertions(+), 414 deletions(-) > > 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..3360ee7 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,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); > 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); > } > > 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); > } > > 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) > + 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; > + } > 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)) > { > /* 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; > + } > + > + 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 (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 (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 0; > +} > + > +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 1e8991d..de4ccc2 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)); > 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 (); > } > Ping! Thanks, --Don