#include "gdbcore.h"
#include "gdb_wait.h"
#include "regcache.h"
+#include "gdb_string.h"
#include <signal.h>
extern CORE_ADDR text_end;
static void
fetch_register (int regno)
{
- char buf[MAX_REGISTER_RAW_SIZE];
+ char buf[MAX_REGISTER_SIZE];
unsigned int addr, len, offset;
int i;
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
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. */
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
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
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,
}
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;
}
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)
{
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. */