+
+ /* There should still be a fork - if there's only one left,
+ delete_fork won't remove it, because we haven't updated
+ inferior_ptid yet. */
+ gdb_assert (!fork_list.empty ());
+
+ last = find_last_fork ();
+ fork_load_infrun_state (last);
+ printf_filtered (_("[Switching to %s]\n"),
+ target_pid_to_str (inferior_ptid).c_str ());
+
+ /* If there's only one fork, switch back to non-fork mode. */
+ if (one_fork_p ())
+ delete_fork (inferior_ptid);
+}
+
+/* The current inferior_ptid is being detached, but there are other
+ viable forks to debug. Detach and delete it and context-switch to
+ the first available. */
+
+void
+linux_fork_detach (int from_tty)
+{
+ /* OK, inferior_ptid is the one we are detaching from. We need to
+ delete it from the fork_list, and switch to the next available
+ fork. */
+
+ if (ptrace (PTRACE_DETACH, inferior_ptid.pid (), 0, 0))
+ error (_("Unable to detach %s"),
+ target_pid_to_str (inferior_ptid).c_str ());
+
+ delete_fork (inferior_ptid);
+
+ /* There should still be a fork - if there's only one left,
+ delete_fork won't remove it, because we haven't updated
+ inferior_ptid yet. */
+ gdb_assert (!fork_list.empty ());
+
+ fork_load_infrun_state (&fork_list.front ());
+
+ if (from_tty)
+ printf_filtered (_("[Switching to %s]\n"),
+ target_pid_to_str (inferior_ptid).c_str ());
+
+ /* If there's only one fork, switch back to non-fork mode. */
+ if (one_fork_p ())
+ delete_fork (inferior_ptid);
+}
+
+/* Temporarily switch to the infrun state stored on the fork_info
+ identified by a given ptid_t. When this object goes out of scope,
+ restore the currently selected infrun state. */
+
+class scoped_switch_fork_info
+{
+public:
+ /* Switch to the infrun state held on the fork_info identified by
+ PPTID. If PPTID is the current inferior then no switch is done. */
+ explicit scoped_switch_fork_info (ptid_t pptid)
+ : m_oldfp (nullptr)
+ {
+ if (pptid != inferior_ptid)
+ {
+ struct fork_info *newfp = nullptr;
+
+ /* Switch to pptid. */
+ m_oldfp = find_fork_ptid (inferior_ptid);
+ gdb_assert (m_oldfp != nullptr);
+ newfp = find_fork_ptid (pptid);
+ gdb_assert (newfp != nullptr);
+ fork_save_infrun_state (m_oldfp);
+ remove_breakpoints ();
+ fork_load_infrun_state (newfp);
+ insert_breakpoints ();
+ }
+ }
+
+ /* Restore the previously selected infrun state. If the constructor
+ didn't need to switch states, then nothing is done here either. */
+ ~scoped_switch_fork_info ()
+ {
+ if (m_oldfp != nullptr)
+ {
+ /* Switch back to inferior_ptid. */
+ try
+ {
+ remove_breakpoints ();
+ fork_load_infrun_state (m_oldfp);
+ insert_breakpoints ();
+ }
+ catch (const gdb_exception &ex)
+ {
+ warning (_("Couldn't restore checkpoint state in %s: %s"),
+ target_pid_to_str (m_oldfp->ptid).c_str (),
+ ex.what ());
+ }
+ }
+ }
+
+ DISABLE_COPY_AND_ASSIGN (scoped_switch_fork_info);
+
+private:
+ /* The fork_info for the previously selected infrun state, or nullptr if
+ we were already in the desired state, and nothing needs to be
+ restored. */
+ struct fork_info *m_oldfp;
+};
+
+static int
+inferior_call_waitpid (ptid_t pptid, int pid)
+{
+ struct objfile *waitpid_objf;
+ struct value *waitpid_fn = NULL;
+ int ret = -1;
+
+ scoped_switch_fork_info switch_fork_info (pptid);
+
+ /* Get the waitpid_fn. */
+ if (lookup_minimal_symbol ("waitpid", NULL, NULL).minsym != NULL)
+ waitpid_fn = find_function_in_inferior ("waitpid", &waitpid_objf);
+ if (!waitpid_fn
+ && lookup_minimal_symbol ("_waitpid", NULL, NULL).minsym != NULL)
+ waitpid_fn = find_function_in_inferior ("_waitpid", &waitpid_objf);
+ if (waitpid_fn != nullptr)