2003-08-02 Andrew Cagney <cagney@redhat.com>
[deliverable/binutils-gdb.git] / gdb / hppah-nat.c
index d5767a9c55f6126d14f529704893b6acc73de714..380a901e9821c10d1008d03a7aeba8055a91e33d 100644 (file)
@@ -31,6 +31,7 @@
 #include "gdbcore.h"
 #include "gdb_wait.h"
 #include "regcache.h"
+#include "gdb_string.h"
 #include <signal.h>
 
 extern CORE_ADDR text_end;
@@ -189,7 +190,7 @@ store_inferior_registers (int regno)
 static void
 fetch_register (int regno)
 {
-  char buf[MAX_REGISTER_RAW_SIZE];
+  char buf[MAX_REGISTER_SIZE];
   unsigned int addr, len, offset;
   int i;
 
@@ -384,42 +385,131 @@ child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
   return len;
 }
 
+char *saved_child_execd_pathname = NULL;
+int saved_vfork_pid;
+enum {
+  STATE_NONE,
+  STATE_GOT_CHILD,
+  STATE_GOT_EXEC,
+  STATE_GOT_PARENT,
+  STATE_FAKE_EXEC
+} saved_vfork_state = STATE_NONE;
 
-void
-child_post_follow_vfork (int parent_pid, int followed_parent, int child_pid,
-                        int followed_child)
+int
+child_follow_fork (int follow_child)
 {
-  /* Are we a debugger that followed the parent of a vfork?  If so,
-     then recall that the child's vfork event was delivered to us
-     first.  And, that the parent was suspended by the OS until the
-     child's exec or exit events were received.
-
-     Upon receiving that child vfork, then, we were forced to remove
-     all breakpoints in the child and continue it so that it could
-     reach the exec or exit point.
-
-     But also recall that the parent and child of a vfork share the
-     same address space.  Thus, removing bp's in the child also
-     removed them from the parent.
-
-     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 (followed_parent)
+  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)
     {
-      reattach_breakpoints (parent_pid);
+      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 ();
     }
 
-  /* Are we a debugger that followed the child of a vfork?  If so,
-     then recall that we don't actually acquire control of the child
-     until after it has exec'd or exited.  */
-  if (followed_child)
+  if (has_vforked)
     {
-      /* If the child has exited, then there's nothing for us to do.
-         In the case of an exec event, we'll let that be handled by
-         the normal mechanism that notices and handles exec events, in
-         resume(). */
+      /* 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;
 }
 
 /* Format a process id, given PID.  Be sure to terminate
@@ -466,7 +556,6 @@ hppa_tid_to_str (ptid_t ptid)
 int not_same_real_pid = 1;
 /*## */
 
-
 /* Wait for child to do something.  Return pid of child, or -1 in case
    of error; store status through argument pointer OURSTATUS.  */
 
@@ -482,6 +571,14 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
   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
@@ -524,26 +621,110 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
          return pid_to_ptid (pid);
        }
 
-      if (hpux_has_forked (pid, &related_pid)
-         && ((pid == PIDGET (inferior_ptid)) 
-             || (related_pid == PIDGET (inferior_ptid))))
+      if (hpux_has_forked (pid, &related_pid))
        {
-         ourstatus->kind = TARGET_WAITKIND_FORKED;
-         ourstatus->value.related_pid = related_pid;
-         return pid_to_ptid (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)
-         && ((pid == PIDGET (inferior_ptid))
-             || (related_pid == PIDGET (inferior_ptid))))
+      if (hpux_has_vforked (pid, &related_pid))
        {
-         ourstatus->kind = TARGET_WAITKIND_VFORKED;
-         ourstatus->value.related_pid = related_pid;
-         return pid_to_ptid (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
@@ -605,8 +786,6 @@ startup_semaphore_t;
 
 static startup_semaphore_t startup_semaphore;
 
-extern int parent_attach_all (int, PTRACE_ARG3_TYPE, int);
-
 #ifdef PT_SETTRC
 /* This function causes the caller's process to be traced by its
    parent.  This is intended to be called after GDB forks itself,
@@ -727,14 +906,13 @@ hppa_insert_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len, int type)
 }
 
 int
-hppa_remove_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len,
-                          enum bptype type)
+hppa_remove_hw_watchpoint (int pid, CORE_ADDR start, LONGEST len, int type)
 {
   error ("Hardware watchpoints not implemented on this platform.");
 }
 
 int
-hppa_can_use_hw_watchpoint (enum bptype type, int cnt, enum bptype ot)
+hppa_can_use_hw_watchpoint (int type, int cnt, int ot)
 {
   return 0;
 }
@@ -752,16 +930,6 @@ hppa_pid_or_tid_to_str (ptid_t id)
   return child_pid_to_str (id);
 }
 
-/* This function has no meaning in a non-threaded world.  Thus, we
-   return 0 (FALSE).  See the use of "hppa_prepare_to_proceed" in
-   hppa-tdep.c. */
-
-pid_t
-hppa_switched_threads (pid_t pid)
-{
-  return (pid_t) 0;
-}
-
 void
 hppa_ensure_vforking_parent_remains_stopped (int pid)
 {
@@ -1154,7 +1322,7 @@ child_pid_to_exec_file (int pid)
   int name_index;
   int i;
   ptid_t saved_inferior_ptid;
-  boolean done;
+  int done;
 
 #ifdef PT_GET_PROCESS_PATHNAME
   /* As of 10.x HP-UX, there's an explicit request to get the pathname. */
This page took 0.027124 seconds and 4 git commands to generate.