===============================================
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));
