コード例 #1
0
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;
}
コード例 #2
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;
}
コード例 #3
0
ptid_t
child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
{
  int save_errno;
  int status;
  char *execd_pathname = NULL;
  int exit_status;
  int related_pid;
  int syscall_id;
  enum target_waitkind kind;
  int pid;

  if (saved_vfork_state == STATE_FAKE_EXEC)
    {
      saved_vfork_state = STATE_NONE;
      ourstatus->kind = TARGET_WAITKIND_EXECD;
      ourstatus->value.execd_pathname = saved_child_execd_pathname;
      return inferior_ptid;
    }

  do
    {
      set_sigint_trap ();	/* Causes SIGINT to be passed on to the
				   attached process. */
      set_sigio_trap ();

      pid = ptrace_wait (inferior_ptid, &status);

      save_errno = errno;

      clear_sigio_trap ();

      clear_sigint_trap ();

      if (pid == -1)
	{
	  if (save_errno == EINTR)
	    continue;

	  fprintf_unfiltered (gdb_stderr, "Child process unexpectedly missing: %s.\n",
			      safe_strerror (save_errno));

	  /* Claim it exited with unknown signal.  */
	  ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
	  ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
	  return pid_to_ptid (-1);
	}

      /* Did it exit?
       */
      if (target_has_exited (pid, status, &exit_status))
	{
	  /* ??rehrauer: For now, ignore this. */
	  continue;
	}

      if (!target_thread_alive (pid_to_ptid (pid)))
	{
	  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
	  return pid_to_ptid (pid);
	}

      if (hpux_has_forked (pid, &related_pid))
	{
	  /* Ignore the parent's fork event.  */
	  if (pid == PIDGET (inferior_ptid))
	    {
	      ourstatus->kind = TARGET_WAITKIND_IGNORE;
	      return inferior_ptid;
	    }

	  /* If this is the child's fork event, report that the
	     process has forked.  */
	  if (related_pid == PIDGET (inferior_ptid))
	    {
	      ourstatus->kind = TARGET_WAITKIND_FORKED;
	      ourstatus->value.related_pid = pid;
	      return inferior_ptid;
	    }
	}

      if (hpux_has_vforked (pid, &related_pid))
	{
	  if (pid == PIDGET (inferior_ptid))
	    {
	      if (saved_vfork_state == STATE_GOT_CHILD)
		saved_vfork_state = STATE_GOT_PARENT;
	      else if (saved_vfork_state == STATE_GOT_EXEC)
		saved_vfork_state = STATE_FAKE_EXEC;
	      else
		fprintf_unfiltered (gdb_stdout,
				    "hppah: parent vfork: confused\n");
	    }
	  else if (related_pid == PIDGET (inferior_ptid))
	    {
	      if (saved_vfork_state == STATE_NONE)
		saved_vfork_state = STATE_GOT_CHILD;
	      else
		fprintf_unfiltered (gdb_stdout,
				    "hppah: child vfork: confused\n");
	    }
	  else
	    fprintf_unfiltered (gdb_stdout,
				"hppah: unknown vfork: confused\n");

	  if (saved_vfork_state == STATE_GOT_CHILD)
	    {
	      child_post_startup_inferior (pid_to_ptid (pid));
	      detach_breakpoints (pid);
#ifdef SOLIB_REMOVE_INFERIOR_HOOK
	      SOLIB_REMOVE_INFERIOR_HOOK (pid);
#endif
	      child_resume (pid_to_ptid (pid), 0, TARGET_SIGNAL_0);
	      ourstatus->kind = TARGET_WAITKIND_IGNORE;
	      return pid_to_ptid (related_pid);
	    }
	  else if (saved_vfork_state == STATE_FAKE_EXEC)
	    {
	      ourstatus->kind = TARGET_WAITKIND_VFORKED;
	      ourstatus->value.related_pid = related_pid;
	      return pid_to_ptid (pid);
	    }
	  else
	    {
	      /* We saw the parent's vfork, but we haven't seen the exec yet.
		 Wait for it, for simplicity's sake.  It should be pending.  */
	      saved_vfork_pid = related_pid;
	      ourstatus->kind = TARGET_WAITKIND_IGNORE;
	      return pid_to_ptid (pid);
	    }
	}

      if (hpux_has_execd (pid, &execd_pathname))
	{
	  /* On HP-UX, events associated with a vforking inferior come in
	     threes: a vfork event for the child (always first), followed
	     a vfork event for the parent and an exec event for the child.
	     The latter two can come in either order.  Make sure we get
	     both.  */
	  if (saved_vfork_state != STATE_NONE)
	    {
	      if (saved_vfork_state == STATE_GOT_CHILD)
		{
		  saved_vfork_state = STATE_GOT_EXEC;
		  /* On HP/UX with ptrace, the child must be resumed before
		     the parent vfork event is delivered.  A single-step
		     suffices.  */
		  if (RESUME_EXECD_VFORKING_CHILD_TO_GET_PARENT_VFORK ())
		    target_resume (pid_to_ptid (pid), 1, TARGET_SIGNAL_0);
		  ourstatus->kind = TARGET_WAITKIND_IGNORE;
		}
	      else if (saved_vfork_state == STATE_GOT_PARENT)
		{
		  saved_vfork_state = STATE_FAKE_EXEC;
		  ourstatus->kind = TARGET_WAITKIND_VFORKED;
		  ourstatus->value.related_pid = saved_vfork_pid;
		}
	      else
		fprintf_unfiltered (gdb_stdout,
				    "hppa: exec: unexpected state\n");

	      saved_child_execd_pathname = execd_pathname;

	      return inferior_ptid;
	    }
	  
	  /* Are we ignoring initial exec events?  (This is likely because
	     we're in the process of starting up the inferior, and another
	     (older) mechanism handles those.)  If so, we'll report this
	     as a regular stop, not an exec.
	   */
	  if (inferior_ignoring_startup_exec_events)
	    {
	      inferior_ignoring_startup_exec_events--;
	    }
	  else
	    {
	      ourstatus->kind = TARGET_WAITKIND_EXECD;
	      ourstatus->value.execd_pathname = execd_pathname;
	      return pid_to_ptid (pid);
	    }
	}

      /* All we must do with these is communicate their occurrence
         to wait_for_inferior...
       */
      if (hpux_has_syscall_event (pid, &kind, &syscall_id))
	{
	  ourstatus->kind = kind;
	  ourstatus->value.syscall_id = syscall_id;
	  return pid_to_ptid (pid);
	}

      /*##  } while (pid != PIDGET (inferior_ptid)); ## *//* Some other child died or stopped */
/* hack for thread testing */
    }
  while ((pid != PIDGET (inferior_ptid)) && not_same_real_pid);
/*## */

  store_waitstatus (ourstatus, status);
  return pid_to_ptid (pid);
}
コード例 #4
0
ファイル: fork-child.c プロジェクト: jichu4n/prc-tools-remix
/* An inferior Unix process CHILD_PID has been created by a call to
   fork() (or variants like vfork).  It is presently stopped, and waiting
   to be resumed.  clone_and_follow_inferior will fork the debugger,
   and that clone will "follow" (attach to) CHILD_PID.  The original copy
   of the debugger will not touch CHILD_PID again.

   Also, the original debugger will set FOLLOWED_CHILD FALSE, while the
   clone will set it TRUE.
 */
void
clone_and_follow_inferior (int child_pid, int *followed_child)
{
  int debugger_pid;
  int status;
  char pid_spelling[100];	/* Arbitrary but sufficient length. */

  /* This semaphore is used to coordinate the two debuggers' handoff
     of CHILD_PID.  The original debugger will detach from CHILD_PID,
     and then the clone debugger will attach to it.  (It must be done
     this way because on some targets, only one process at a time can
     trace another.  Thus, the original debugger must relinquish its
     tracing rights before the clone can pick them up.)
   */
#define SEM_TALK (1)
#define SEM_LISTEN (0)
  int handoff_semaphore[2];	/* Original "talks" to [1], clone "listens" to [0] */
  int talk_value = 99;
  int listen_value;

  /* Set debug_fork then attach to the child while it sleeps, to debug. */
  static int debug_fork = 0;

  /* It is generally good practice to flush any possible pending stdio
     output prior to doing a fork, to avoid the possibility of both the
     parent and child flushing the same data after the fork. */

  gdb_flush (gdb_stdout);
  gdb_flush (gdb_stderr);

  /* Open the semaphore pipes.
   */
  status = pipe (handoff_semaphore);
  if (status < 0)
    error ("error getting pipe for handoff semaphore");

  /* Clone the debugger.  Note that the apparent call to vfork()
     below *might* actually be a call to fork() due to the fact that
     autoconf will ``#define vfork fork'' on certain platforms.  */
  if (debug_fork)
    debugger_pid = fork ();
  else
    debugger_pid = vfork ();

  if (debugger_pid < 0)
    perror_with_name ("fork");

  /* Are we the original debugger?  If so, we must relinquish all claims
     to CHILD_PID. */
  if (debugger_pid != 0)
    {
      char signal_spelling[100];	/* Arbitrary but sufficient length */

      /* Detach from CHILD_PID.  Deliver a "stop" signal when we do, though,
         so that it remains stopped until the clone debugger can attach
         to it.
       */
      detach_breakpoints (child_pid);

      sprintf (signal_spelling, "%d", target_signal_to_host (TARGET_SIGNAL_STOP));
      target_require_detach (child_pid, signal_spelling, 1);

      /* Notify the clone debugger that it should attach to CHILD_PID. */
      write (handoff_semaphore[SEM_TALK], &talk_value, sizeof (talk_value));

      *followed_child = 0;
    }

  /* We're the child. */
  else
    {
      if (debug_fork)
	sleep (debug_fork);

      /* The child (i.e., the cloned debugger) must now attach to
         CHILD_PID.  inferior_ptid is presently set to the parent process
         of the fork, while CHILD_PID should be the child process of the
         fork.

         Wait until the original debugger relinquishes control of CHILD_PID,
         though.
       */
      read (handoff_semaphore[SEM_LISTEN], &listen_value, sizeof (listen_value));

      /* Note that we DON'T want to actually detach from inferior_ptid,
         because that would allow it to run free.  The original
         debugger wants to retain control of the process.  So, we
         just reset inferior_ptid to CHILD_PID, and then ensure that all
         breakpoints are really set in CHILD_PID.
       */
      target_mourn_inferior ();

      /* Ask the tty subsystem to switch to the one we specified earlier
         (or to share the current terminal, if none was specified).  */

      new_tty ();

      dont_repeat ();
      sprintf (pid_spelling, "%d", child_pid);
      target_require_attach (pid_spelling, 1);

      /* Perform any necessary cleanup, after attachment.  (This form
         of attaching can behave differently on some targets than the
         standard method, where a process formerly not under debugger
         control was suddenly attached to..)
       */
      target_post_follow_inferior_by_clone ();

      *followed_child = 1;
    }

  /* Discard the handoff sempahore. */
  (void) close (handoff_semaphore[SEM_LISTEN]);
  (void) close (handoff_semaphore[SEM_TALK]);
}