Re: [PATCH 01/16 v2] Refactor native follow-fork

Breazeal, Don
2014-09-22T15:53:03Z

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 <donb@codesourcery.com>
>
> * 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)
> }
> \f
>
> -/* 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 <sys/types.h>
> #include <dirent.h>
> #include "xml-support.h"
> -#include "terminal.h"
> #include <sys/vfs.h>
> #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