This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


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)
>  }
>  
> 
> -/* 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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]