diff --git a/gdb/ChangeLog b/gdb/ChangeLog index a83927b689..c427bf0f40 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,4 +1,19 @@ -2002-12-06 Daniel Jacobowitz +2002-12-10 Daniel Jacobowitz + + * hppah-nat.c (saved_child_execd_pathname, saved_vfork_state): New. + (child_post_follow_vfork): Cancel pending exec event if we follow + the parent. + (child_wait): Only return TARGET_WAITKIND_VFORKED when all necessary + events have been processed. Return a fake TARGET_WAITKIND_EXECD + event at the following wait call if necessary. + * infrun.c (follow_vfork): Don't follow_exec here. + (handle_inferior_event): Add comment to TARGET_WAITKIND_EXECD + case about HP/UX 10.20. Remove code pushed down to + hppah-nat.c:child_wait. + * infttrace.c (child_resume): Use TT_PROC_CONTINUE if + vfork_in_flight is set. + +2002-12-10 Daniel Jacobowitz * hppah-nat.c (child_wait): Return TARGET_WAITKIND_IGNORE for the parent's fork event. diff --git a/gdb/hppah-nat.c b/gdb/hppah-nat.c index 17614073e2..d7919e6cef 100644 --- a/gdb/hppah-nat.c +++ b/gdb/hppah-nat.c @@ -384,6 +384,14 @@ child_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write, return len; } +char *saved_child_execd_pathname = NULL; +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, @@ -410,6 +418,13 @@ child_post_follow_vfork (int parent_pid, int followed_parent, int child_pid, reattach_breakpoints (parent_pid); } + /* 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 (followed_parent || saved_vfork_state == STATE_GOT_PARENT) + saved_vfork_state = STATE_NONE; + /* 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. */ @@ -466,7 +481,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 +496,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 @@ -543,17 +565,73 @@ child_wait (ptid_t ptid, struct target_waitstatus *ourstatus) } } - 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)); + ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + return pid_to_ptid (pid); + } + else + { + ourstatus->kind = TARGET_WAITKIND_VFORKED; + ourstatus->value.related_pid = related_pid; + 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. + + If we get the parent vfork event first, life's good: We follow + either the parent or child, and then the child's exec event is + a "don't care". + + But if we get the child's exec event first, then we delay + responding to it until we handle the parent's vfork. Because, + otherwise we can't satisfy a "catch vfork". */ + if (saved_vfork_state == STATE_GOT_CHILD) + { + saved_child_execd_pathname = execd_pathname; + 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; + 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 diff --git a/gdb/infrun.c b/gdb/infrun.c index 7cb9f3338e..b96e1c7a25 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -534,16 +534,6 @@ static void follow_vfork (int parent_pid, int child_pid) { follow_inferior_fork (parent_pid, child_pid, 0, 1); - - /* Did we follow the child? Had it exec'd before we saw the parent vfork? */ - if (pending_follow.fork_event.saw_child_exec - && (PIDGET (inferior_ptid) == child_pid)) - { - pending_follow.fork_event.saw_child_exec = 0; - pending_follow.kind = TARGET_WAITKIND_SPURIOUS; - follow_exec (PIDGET (inferior_ptid), pending_follow.execd_pathname); - xfree (pending_follow.execd_pathname); - } } /* EXECD_PATHNAME is assumed to be non-NULL. */ @@ -1555,6 +1545,9 @@ handle_inferior_event (struct execution_control_state *ecs) case TARGET_WAITKIND_EXECD: stop_signal = TARGET_SIGNAL_TRAP; + /* NOTE drow/2002-12-05: This code should be pushed down into the + target_wait function. Until then following vfork on HP/UX 10.20 + is probably broken by this. Of course, it's broken anyway. */ /* Is this a target which reports multiple exec events per actual call to exec()? (HP-UX using ptrace does, for example.) If so, ignore all but the last one. Just resume the exec'r, and wait @@ -1576,36 +1569,6 @@ handle_inferior_event (struct execution_control_state *ecs) savestring (ecs->ws.value.execd_pathname, strlen (ecs->ws.value.execd_pathname)); - /* Did inferior_ptid exec, or did a (possibly not-yet-followed) - child of a vfork exec? - - ??rehrauer: This is unabashedly an HP-UX specific thing. 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. - - If we get the parent vfork event first, life's good: We follow - either the parent or child, and then the child's exec event is - a "don't care". - - But if we get the child's exec event first, then we delay - responding to it until we handle the parent's vfork. Because, - otherwise we can't satisfy a "catch vfork". */ - if (pending_follow.kind == TARGET_WAITKIND_VFORKED) - { - pending_follow.fork_event.saw_child_exec = 1; - - /* On some targets, 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 (ecs->ptid, 1, TARGET_SIGNAL_0); - /* We expect the parent vfork event to be available now. */ - prepare_to_wait (ecs); - return; - } - /* This causes the eventpoints and symbol table to be reset. Must do this now, before trying to determine whether to stop. */ follow_exec (PIDGET (inferior_ptid), pending_follow.execd_pathname); diff --git a/gdb/infttrace.c b/gdb/infttrace.c index 7f0910f571..69a67dfa1b 100644 --- a/gdb/infttrace.c +++ b/gdb/infttrace.c @@ -4523,7 +4523,15 @@ child_resume (ptid_t ptid, int step, enum target_signal signal) pending signal will be passed to the inferior. interrupt.exp in the testsuite does this precise thing and fails due to the unwanted signal delivery to the inferior. */ - if (resume_all_threads) + /* drow/2002-12-05: However, note that we must use TT_PROC_CONTINUE + if we are tracing a vfork. */ + if (vfork_in_flight) + { + call_ttrace (TT_PROC_CONTINUE, tid, TT_NIL, TT_NIL, TT_NIL); + clear_all_handled (); + clear_all_stepping_mode (); + } + else if (resume_all_threads) { #ifdef THREAD_DEBUG if (debug_on)