+/* When tracking a vfork(2), we cannot detach from the parent until
+ after the child has called exec(3) or has exited. If we are still
+ attached to the parent, this variable will be set to the process ID
+ of the parent. Otherwise it will be set to zero. */
+static pid_t inf_ttrace_vfork_ppid = -1;
+
+static int
+inf_ttrace_follow_fork (struct target_ops *ops, int follow_child)
+{
+ pid_t pid, fpid;
+ lwpid_t lwpid, flwpid;
+ ttstate_t tts;
+ struct thread_info *tp = inferior_thread ();
+
+ gdb_assert (tp->pending_follow.kind == TARGET_WAITKIND_FORKED
+ || tp->pending_follow.kind == TARGET_WAITKIND_VFORKED);
+
+ pid = ptid_get_pid (inferior_ptid);
+ lwpid = ptid_get_lwp (inferior_ptid);
+
+ /* Get all important details that core GDB doesn't (and shouldn't)
+ know about. */
+ if (ttrace (TT_LWP_GET_STATE, pid, lwpid,
+ (uintptr_t)&tts, sizeof tts, 0) == -1)
+ perror_with_name (("ttrace"));
+
+ gdb_assert (tts.tts_event == TTEVT_FORK || tts.tts_event == TTEVT_VFORK);
+
+ if (tts.tts_u.tts_fork.tts_isparent)
+ {
+ pid = tts.tts_pid;
+ lwpid = tts.tts_lwpid;
+ fpid = tts.tts_u.tts_fork.tts_fpid;
+ flwpid = tts.tts_u.tts_fork.tts_flwpid;
+ }
+ else
+ {
+ pid = tts.tts_u.tts_fork.tts_fpid;
+ lwpid = tts.tts_u.tts_fork.tts_flwpid;
+ fpid = tts.tts_pid;
+ flwpid = tts.tts_lwpid;
+ }
+
+ if (follow_child)
+ {
+ struct inferior *inf;
+ struct inferior *parent_inf;
+
+ parent_inf = find_inferior_pid (pid);
+
+ inferior_ptid = ptid_build (fpid, flwpid, 0);
+ inf = add_inferior (fpid);
+ inf->attach_flag = parent_inf->attach_flag;
+ inf->pspace = parent_inf->pspace;
+ inf->aspace = parent_inf->aspace;
+ copy_terminal_info (inf, parent_inf);
+ detach_breakpoints (pid);
+
+ target_terminal_ours ();
+ fprintf_unfiltered (gdb_stdlog,
+ _("Attaching after fork to child process %ld.\n"),
+ (long)fpid);
+ }
+ else
+ {
+ inferior_ptid = ptid_build (pid, lwpid, 0);
+ detach_breakpoints (fpid);
+
+ target_terminal_ours ();
+ fprintf_unfiltered (gdb_stdlog,
+ _("Detaching after fork from child process %ld.\n"),
+ (long)fpid);
+ }
+
+ if (tts.tts_event == TTEVT_VFORK)
+ {
+ gdb_assert (!tts.tts_u.tts_fork.tts_isparent);
+
+ if (follow_child)
+ {
+ /* We can't detach from the parent yet. */
+ inf_ttrace_vfork_ppid = pid;
+
+ reattach_breakpoints (fpid);
+ }
+ else
+ {
+ if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+
+ /* Wait till we get the TTEVT_VFORK event in the parent.
+ This indicates that the child has called exec(3) or has
+ exited and that the parent is ready to be traced again. */
+ if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1)
+ perror_with_name (("ttrace_wait"));
+ gdb_assert (tts.tts_event == TTEVT_VFORK);
+ gdb_assert (tts.tts_u.tts_fork.tts_isparent);
+
+ reattach_breakpoints (pid);
+ }
+ }
+ else
+ {
+ gdb_assert (tts.tts_u.tts_fork.tts_isparent);
+
+ if (follow_child)
+ {
+ if (ttrace (TT_PROC_DETACH, pid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+ }
+ else
+ {
+ if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+ }
+ }
+
+ if (follow_child)
+ {
+ struct thread_info *ti;
+
+ /* The child will start out single-threaded. */
+ inf_ttrace_num_lwps = 1;
+ inf_ttrace_num_lwps_in_syscall = 0;
+
+ /* Delete parent. */
+ delete_thread_silent (ptid_build (pid, lwpid, 0));
+ detach_inferior (pid);
+
+ /* Add child thread. inferior_ptid was already set above. */
+ ti = add_thread_silent (inferior_ptid);
+ ti->private =
+ xmalloc (sizeof (struct inf_ttrace_private_thread_info));
+ memset (ti->private, 0,
+ sizeof (struct inf_ttrace_private_thread_info));
+ }
+
+ return 0;
+}
+\f
+