static int
inf_ptrace_follow_fork (struct target_ops *ops, int follow_child)
{
  pid_t pid, fpid;
  ptrace_state_t pe;

  /* FIXME: kettenis/20050720: This stuff should really be passed as
     an argument by our caller.  */
  {
    ptid_t ptid;
    struct target_waitstatus status;

    get_last_target_status (&ptid, &status);
    gdb_assert (status.kind == TARGET_WAITKIND_FORKED);

    pid = ptid_get_pid (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)
    {
      inferior_ptid = pid_to_ptid (fpid);
      detach_breakpoints (pid);

      /* Reset breakpoints in the child as appropriate.  */
      follow_inferior_reset_breakpoints ();

      if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
	perror_with_name (("ptrace"));
    }
  else
    {
      inferior_ptid = pid_to_ptid (pid);
      detach_breakpoints (fpid);

      if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
	perror_with_name (("ptrace"));
    }

  return 0;
}
int
child_follow_fork (int follow_child)
{
  ptid_t last_ptid;
  struct target_waitstatus last_status;
  int has_vforked;
  int parent_pid, child_pid;

  get_last_target_status (&last_ptid, &last_status);
  has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
  parent_pid = ptid_get_pid (last_ptid);
  child_pid = last_status.value.related_pid;

  /* At this point, if we are vforking, breakpoints were already
     detached from the child in child_wait; and the child has already
     called execve().  If we are forking, both the parent and child
     have breakpoints inserted.  */

  if (! follow_child)
    {
      if (! has_vforked)
	{
	  detach_breakpoints (child_pid);
#ifdef SOLIB_REMOVE_INFERIOR_HOOK
	  SOLIB_REMOVE_INFERIOR_HOOK (child_pid);
#endif
	}

      /* Detach from the child. */
      printf_unfiltered ("Detaching after fork from %s\n",
			 target_pid_to_str (pid_to_ptid (child_pid)));
      hppa_require_detach (child_pid, 0);

      /* The parent and child of a vfork share the same address space.
	 Also, on some targets the order in which vfork and exec events
	 are received for parent in child requires some delicate handling
	 of the events.

	 For instance, on ptrace-based HPUX we receive the child's vfork
	 event first, at which time the parent has been suspended by the
	 OS and is essentially untouchable until the child's exit or second
	 exec event arrives.  At that time, the parent's vfork event is
	 delivered to us, and that's when we see and decide how to follow
	 the vfork.  But to get to that point, we must continue the child
	 until it execs or exits.  To do that smoothly, all breakpoints
	 must be removed from the child, in case there are any set between
	 the vfork() and exec() calls.  But removing them from the child
	 also removes them from the parent, due to the shared-address-space
	 nature of a vfork'd parent and child.  On HPUX, therefore, we must
	 take care to restore the bp's to the parent before we continue it.
	 Else, it's likely that we may not stop in the expected place.  (The
	 worst scenario is when the user tries to step over a vfork() call;
	 the step-resume bp must be restored for the step to properly stop
	 in the parent after the call completes!)

	 Sequence of events, as reported to gdb from HPUX:

	 Parent        Child           Action for gdb to take
	 -------------------------------------------------------
	 1                VFORK               Continue child
	 2                EXEC
	 3                EXEC or EXIT
	 4  VFORK

	 Now that the child has safely exec'd or exited, we must restore
	 the parent's breakpoints before we continue it.  Else, we may
	 cause it run past expected stopping points.  */

      if (has_vforked)
	reattach_breakpoints (parent_pid);
    }
  else
    {
      /* Needed to keep the breakpoint lists in sync.  */
      if (! has_vforked)
	detach_breakpoints (child_pid);

      /* Before detaching from the parent, remove all breakpoints from it. */
      remove_breakpoints ();

      /* Also reset the solib inferior hook from the parent. */
#ifdef SOLIB_REMOVE_INFERIOR_HOOK
      SOLIB_REMOVE_INFERIOR_HOOK (PIDGET (inferior_ptid));
#endif

      /* Detach from the parent. */
      target_detach (NULL, 1);

      /* Attach to the child. */
      printf_unfiltered ("Attaching after fork to %s\n",
			 target_pid_to_str (pid_to_ptid (child_pid)));
      hppa_require_attach (child_pid);
      inferior_ptid = pid_to_ptid (child_pid);

      /* If we vforked, then we've also execed by now.  The exec will be
	 reported momentarily.  follow_exec () will handle breakpoints, so
	 we don't have to..  */
      if (!has_vforked)
	follow_inferior_reset_breakpoints ();
    }

  if (has_vforked)
    {
      /* If we followed the parent, don't try to follow the child's exec.  */
      if (saved_vfork_state != STATE_GOT_PARENT
	  && saved_vfork_state != STATE_FAKE_EXEC)
	fprintf_unfiltered (gdb_stdout,
			    "hppa: post follow vfork: confused state\n");

      if (! follow_child || saved_vfork_state == STATE_GOT_PARENT)
	saved_vfork_state = STATE_NONE;
      else
	return 1;
    }
  return 0;
}