+static struct breakpoint *step_resume_breakpoint = NULL;
+static struct breakpoint *through_sigtramp_breakpoint = NULL;
+
+/* On some platforms (e.g., HP-UX), hardware watchpoints have bad
+ interactions with an inferior that is running a kernel function
+ (aka, a system call or "syscall"). wait_for_inferior therefore
+ may have a need to know when the inferior is in a syscall. This
+ is a count of the number of inferior threads which are known to
+ currently be running in a syscall. */
+static int number_of_threads_in_syscalls;
+
+/* This is used to remember when a fork, vfork or exec event
+ was caught by a catchpoint, and thus the event is to be
+ followed at the next resume of the inferior, and not
+ immediately. */
+static struct
+ {
+ enum target_waitkind kind;
+ struct
+ {
+ int parent_pid;
+ int saw_parent_fork;
+ int child_pid;
+ int saw_child_fork;
+ int saw_child_exec;
+ }
+ fork_event;
+ char *execd_pathname;
+ }
+pending_follow;
+
+/* Some platforms don't allow us to do anything meaningful with a
+ vforked child until it has exec'd. Vforked processes on such
+ platforms can only be followed after they've exec'd.
+
+ When this is set to 0, a vfork can be immediately followed,
+ and an exec can be followed merely as an exec. When this is
+ set to 1, a vfork event has been seen, but cannot be followed
+ until the exec is seen.
+
+ (In the latter case, inferior_pid is still the parent of the
+ vfork, and pending_follow.fork_event.child_pid is the child. The
+ appropriate process is followed, according to the setting of
+ follow-fork-mode.) */
+static int follow_vfork_when_exec;
+
+static char *follow_fork_mode_kind_names[] =
+{
+ /* ??rehrauer: The "both" option is broken, by what may be a 10.20
+ kernel problem. It's also not terribly useful without a GUI to
+ help the user drive two debuggers. So for now, I'm disabling the
+ "both" option. */
+ /* "parent", "child", "both", "ask" */
+ "parent", "child", "ask", NULL
+};
+
+static char *follow_fork_mode_string = NULL;
+\f
+
+static void
+follow_inferior_fork (int parent_pid, int child_pid, int has_forked,
+ int has_vforked)
+{
+ int followed_parent = 0;
+ int followed_child = 0;
+
+ /* Which process did the user want us to follow? */
+ char *follow_mode =
+ savestring (follow_fork_mode_string, strlen (follow_fork_mode_string));
+
+ /* Or, did the user not know, and want us to ask? */
+ if (STREQ (follow_fork_mode_string, "ask"))
+ {
+ char requested_mode[100];
+
+ free (follow_mode);
+ error ("\"ask\" mode NYI");
+ follow_mode = savestring (requested_mode, strlen (requested_mode));
+ }
+
+ /* If we're to be following the parent, then detach from child_pid.
+ We're already following the parent, so need do nothing explicit
+ for it. */
+ if (STREQ (follow_mode, "parent"))
+ {
+ followed_parent = 1;
+
+ /* We're already attached to the parent, by default. */
+
+ /* Before detaching from the child, remove all breakpoints from
+ it. (This won't actually modify the breakpoint list, but will
+ physically remove the breakpoints from the child.) */
+ if (!has_vforked || !follow_vfork_when_exec)
+ {
+ detach_breakpoints (child_pid);
+#ifdef SOLIB_REMOVE_INFERIOR_HOOK
+ SOLIB_REMOVE_INFERIOR_HOOK (child_pid);
+#endif
+ }
+
+ /* Detach from the child. */
+ dont_repeat ();
+
+ target_require_detach (child_pid, "", 1);
+ }
+
+ /* If we're to be following the child, then attach to it, detach
+ from inferior_pid, and set inferior_pid to child_pid. */
+ else if (STREQ (follow_mode, "child"))
+ {
+ char child_pid_spelling[100]; /* Arbitrary length. */
+
+ followed_child = 1;
+
+ /* Before detaching from the parent, detach all breakpoints from
+ the child. But only if we're forking, or if we follow vforks
+ as soon as they happen. (If we're following vforks only when
+ the child has exec'd, then it's very wrong to try to write
+ back the "shadow contents" of inserted breakpoints now -- they
+ belong to the child's pre-exec'd a.out.) */
+ if (!has_vforked || !follow_vfork_when_exec)
+ {
+ 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 (inferior_pid);
+#endif
+
+ /* Detach from the parent. */
+ dont_repeat ();
+ target_detach (NULL, 1);
+
+ /* Attach to the child. */
+ inferior_pid = child_pid;
+ sprintf (child_pid_spelling, "%d", child_pid);
+ dont_repeat ();
+
+ target_require_attach (child_pid_spelling, 1);
+
+ /* Was there a step_resume breakpoint? (There was if the user
+ did a "next" at the fork() call.) If so, explicitly reset its
+ thread number.
+
+ step_resumes are a form of bp that are made to be per-thread.
+ Since we created the step_resume bp when the parent process
+ was being debugged, and now are switching to the child process,
+ from the breakpoint package's viewpoint, that's a switch of
+ "threads". We must update the bp's notion of which thread
+ it is for, or it'll be ignored when it triggers... */
+ if (step_resume_breakpoint &&
+ (!has_vforked || !follow_vfork_when_exec))
+ breakpoint_re_set_thread (step_resume_breakpoint);
+
+ /* Reinsert all breakpoints in the child. (The user may've set
+ breakpoints after catching the fork, in which case those
+ actually didn't get set in the child, but only in the parent.) */
+ if (!has_vforked || !follow_vfork_when_exec)
+ {
+ breakpoint_re_set ();
+ insert_breakpoints ();
+ }
+ }
+
+ /* If we're to be following both parent and child, then fork ourselves,
+ and attach the debugger clone to the child. */
+ else if (STREQ (follow_mode, "both"))
+ {
+ char pid_suffix[100]; /* Arbitrary length. */
+
+ /* Clone ourselves to follow the child. This is the end of our
+ involvement with child_pid; our clone will take it from here... */
+ dont_repeat ();
+ target_clone_and_follow_inferior (child_pid, &followed_child);
+ followed_parent = !followed_child;
+
+ /* We continue to follow the parent. To help distinguish the two
+ debuggers, though, both we and our clone will reset our prompts. */
+ sprintf (pid_suffix, "[%d] ", inferior_pid);
+ set_prompt (strcat (get_prompt (), pid_suffix));
+ }
+
+ /* 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 */
+ if (has_vforked)
+ {
+ target_post_follow_vfork (parent_pid,
+ followed_parent,
+ child_pid,
+ followed_child);
+ }
+
+ pending_follow.fork_event.saw_parent_fork = 0;
+ pending_follow.fork_event.saw_child_fork = 0;
+
+ free (follow_mode);
+}
+
+static void
+follow_fork (int parent_pid, int child_pid)
+{
+ follow_inferior_fork (parent_pid, child_pid, 1, 0);
+}
+
+
+/* Forward declaration. */
+static void follow_exec (int, char *);
+
+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 && (inferior_pid == child_pid))
+ {
+ pending_follow.fork_event.saw_child_exec = 0;
+ pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
+ follow_exec (inferior_pid, pending_follow.execd_pathname);
+ free (pending_follow.execd_pathname);
+ }
+}
+
+static void
+follow_exec (int pid, char *execd_pathname)
+{
+ int saved_pid = pid;
+ struct target_ops *tgt;
+
+ if (!may_follow_exec)
+ return;
+
+ /* Did this exec() follow a vfork()? If so, we must follow the
+ vfork now too. Do it before following the exec. */
+ if (follow_vfork_when_exec &&
+ (pending_follow.kind == TARGET_WAITKIND_VFORKED))
+ {
+ pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
+ follow_vfork (inferior_pid, pending_follow.fork_event.child_pid);
+ follow_vfork_when_exec = 0;
+ saved_pid = inferior_pid;
+
+ /* Did we follow the parent? If so, we're done. If we followed
+ the child then we must also follow its exec(). */
+ if (inferior_pid == pending_follow.fork_event.parent_pid)
+ return;
+ }
+
+ /* This is an exec event that we actually wish to pay attention to.
+ Refresh our symbol table to the newly exec'd program, remove any
+ momentary bp's, etc.
+
+ If there are breakpoints, they aren't really inserted now,
+ since the exec() transformed our inferior into a fresh set
+ of instructions.
+
+ We want to preserve symbolic breakpoints on the list, since
+ we have hopes that they can be reset after the new a.out's
+ symbol table is read.
+
+ However, any "raw" breakpoints must be removed from the list
+ (e.g., the solib bp's), since their address is probably invalid
+ now.
+
+ And, we DON'T want to call delete_breakpoints() here, since
+ that may write the bp's "shadow contents" (the instruction
+ value that was overwritten witha TRAP instruction). Since
+ we now have a new a.out, those shadow contents aren't valid. */
+ update_breakpoints_after_exec ();
+
+ /* If there was one, it's gone now. We cannot truly step-to-next
+ statement through an exec(). */
+ step_resume_breakpoint = NULL;
+ step_range_start = 0;
+ step_range_end = 0;
+
+ /* If there was one, it's gone now. */
+ through_sigtramp_breakpoint = NULL;
+
+ /* What is this a.out's name? */
+ printf_unfiltered ("Executing new program: %s\n", execd_pathname);