diff --git a/gdb/linux-fork.c b/gdb/linux-fork.c
index bb8119a36d0..d21ab8e567e 100644
--- a/gdb/linux-fork.c
+++ b/gdb/linux-fork.c
@@ -28,6 +28,7 @@
 #include "linux-nat.h"
 #include "gdbthread.h"
 #include "source.h"
+#include "progspace-and-thread.h"
 
 #include "nat/gdb_ptrace.h"
 #include "gdbsupport/gdb_wait.h"
@@ -38,10 +39,11 @@
 #include <list>
 
 /* Fork list data structure:  */
+
 struct fork_info
 {
-  explicit fork_info (pid_t pid)
-    : ptid (pid, pid)
+  explicit fork_info (pid_t pid, int fork_num)
+    : ptid (pid, pid), num (fork_num)
   {
   }
 
@@ -69,7 +71,7 @@ struct fork_info
   ptid_t parent_ptid = null_ptid;
 
   /* Convenient handle (GDB fork id).  */
-  int num = 0;
+  int num;
 
   /* Convenient for info fork, saves having to actually switch
      contexts.  */
@@ -83,115 +85,253 @@ struct fork_info
   int maxfd = 0;
 };
 
-static std::list<fork_info> fork_list;
-static int highest_fork_num;
+/* Per-inferior checkpoint data.  */
+
+struct checkpoint_inferior_data
+{
+  /* List of forks (checkpoints) in particular inferior.  Once a
+     checkpoint has been created, fork_list will contain at least two
+     items, the first in the list will be the original (or, if not
+     original, then the oldest) fork.  */
+  std::list<fork_info> fork_list;
+
+  /* Most recently assigned fork number; when 0, no checkpoints have
+     been created yet.  */
+  int highest_fork_num = 0;
+};
+
+/* Per-inferior data key.  */
+
+static const registry<inferior>::key<checkpoint_inferior_data>
+  checkpoint_inferior_data_key;
+
+/* Fetch per-inferior checkpoint data.  It always returns a valid pointer
+   to a checkpoint_inferior_info struct.  */
+
+static struct checkpoint_inferior_data *
+get_checkpoint_inferior_data (struct inferior *inf)
+{
+  struct checkpoint_inferior_data *data;
+
+  data = checkpoint_inferior_data_key.get (inf);
+  if (data == nullptr)
+    data = checkpoint_inferior_data_key.emplace (inf);
+
+  return data;
+}
+
+/* Return a reference to the per-inferior fork list.  */
+
+static std::list<fork_info> &
+fork_list (inferior *inf)
+{
+  return get_checkpoint_inferior_data (inf)->fork_list;
+}
+
+/* Return a reference to 'highest_fork_num' for the given inferior INF.  */
+
+static int &
+highest_fork_num (inferior *inf)
+{
+  return get_checkpoint_inferior_data (inf)->highest_fork_num;
+}
+
+/* Predicate which returns true when more than one inferior contains
+   a checkpoint, false otherwise.  */
+
+static bool
+forks_exist_in_multiple_inferiors_p ()
+{
+  int count = 0;
+  for (inferior *inf : all_inferiors ())
+    {
+      if (forks_exist_p (inf))
+	count++;
+      if (count > 1)
+	return true;
+    }
+  return false;
+}
 
 /* Fork list methods:  */
 
-int
-forks_exist_p (void)
+/* Predicate which returns true if checkpoint(s) exist in the inferior
+   INF, false otherwise.  */
+
+bool
+forks_exist_p (inferior *inf)
 {
-  return !fork_list.empty ();
+  return !fork_list (inf).empty ();
 }
 
-/* Return the last fork in the list.  */
+/* Return the last fork in the list for inferior INF.  */
 
 static struct fork_info *
-find_last_fork (void)
+find_last_fork (inferior *inf)
 {
+  auto &fork_list = ::fork_list (inf);
+
   if (fork_list.empty ())
     return NULL;
 
   return &fork_list.back ();
 }
 
-/* Return true iff there's one fork in the list.  */
+/* Return true iff there's one fork in the list for inferior INF.  */
 
 static bool
-one_fork_p ()
+one_fork_p (inferior *inf)
 {
-  return fork_list.size () == 1;
+  return fork_list (inf).size () == 1;
 }
 
 /* Add a new fork to the internal fork list.  */
 
 void
-add_fork (pid_t pid)
+add_fork (pid_t pid, inferior *inf)
 {
-  fork_list.emplace_back (pid);
-
-  if (one_fork_p ())
-    highest_fork_num = 0;
-
-  fork_info *fp = &fork_list.back ();
-  fp->num = ++highest_fork_num;
+  fork_list (inf).emplace_back (pid, ++highest_fork_num (inf));
 }
 
+/* Delete a fork for PTID in inferior INF.  When the last fork is
+   deleted, HIGHEST_FORK_NUM for the given inferior is reset to 0.
+   The fork list may also be made to be empty when only one fork
+   remains.  */
+
 static void
-delete_fork (ptid_t ptid)
+delete_fork (ptid_t ptid, inferior *inf)
 {
   linux_target->low_forget_process (ptid.pid ());
 
+  auto &fork_list = ::fork_list (inf);
   for (auto it = fork_list.begin (); it != fork_list.end (); ++it)
     if (it->ptid == ptid)
       {
 	fork_list.erase (it);
 
+	if (fork_list.empty ())
+	  {
+	    /* Reset highest_fork_num.  */
+	    highest_fork_num (inf) = 0;
+	  }
+
 	/* Special case: if there is now only one process in the list,
 	   and if it is (hopefully!) the current inferior_ptid, then
 	   remove it, leaving the list empty -- we're now down to the
 	   default case of debugging a single process.  */
-	if (one_fork_p () && fork_list.front ().ptid == inferior_ptid)
+	if (one_fork_p (inf) && fork_list.front ().ptid == inferior_ptid)
 	  {
 	    /* Last fork -- delete from list and handle as solo
 	       process (should be a safe recursion).  */
-	    delete_fork (inferior_ptid);
+	    delete_fork (inferior_ptid, inf);
 	  }
 	return;
       }
 }
 
-/* Find a fork_info by matching PTID.  */
-static struct fork_info *
+/* Find a fork_info and inferior by matching PTID.  */
+
+static std::pair<fork_info *, inferior *>
 find_fork_ptid (ptid_t ptid)
 {
-  for (fork_info &fi : fork_list)
-    if (fi.ptid == ptid)
-      return &fi;
+  for (inferior *inf : all_inferiors ())
+    {
+      for (fork_info &fi : fork_list (inf))
+	if (fi.ptid == ptid)
+	  return { &fi, inf };
+    }
 
-  return NULL;
+  return { nullptr, nullptr };
 }
 
-/* Find a fork_info by matching ID.  */
-static struct fork_info *
-find_fork_id (int num)
+/* Find a fork_info by matching NUM in inferior INF.  */
+
+static fork_info *
+find_fork_id (inferior *inf, int num)
 {
-  for (fork_info &fi : fork_list)
+  for (fork_info &fi : fork_list (inf))
     if (fi.num == num)
       return &fi;
 
-  return NULL;
+  return nullptr;
 }
 
-/* Find a fork_info by matching pid.  */
-extern struct fork_info *
+/* Find a fork_info and inferior by matching pid.  */
+
+extern std::pair<fork_info *, inferior *>
 find_fork_pid (pid_t pid)
 {
-  for (fork_info &fi : fork_list)
-    if (pid == fi.ptid.pid ())
-      return &fi;
+  for (inferior *inf : all_inferiors ())
+    {
+      for (fork_info &fi : fork_list (inf))
+	if (pid == fi.ptid.pid ())
+	  return { &fi, inf };
+    }
 
-  return NULL;
+  return { nullptr, nullptr };
 }
 
-static ptid_t
-fork_id_to_ptid (int num)
+/* Parse a command argument representing a checkpoint id.  This
+   can take one of two forms:
+
+   Num
+
+   -or-
+
+   Inf.Num
+
+   where Num is a non-negative decimal integer and Inf, if present, is
+   a positive decimal integer.
+   
+   Return a pair with a pointer to the fork_info struct and pointer
+   to the inferior.  This function will throw an error if there's
+   a problem with the parsing or if either the inferior or checkpoint
+   id does not exist. */
+
+static std::pair<fork_info *, inferior *>
+parse_checkpoint_id (const char *ckptstr)
 {
-  struct fork_info *fork = find_fork_id (num);
-  if (fork)
-    return fork->ptid;
+  const char *number = ckptstr;
+  const char *p1;
+  struct inferior *inf;
+
+  const char *dot = strchr (number, '.');
+
+  if (dot != nullptr)
+    {
+      /* Parse number to the left of the dot.  */
+      int inf_num;
+
+      p1 = number;
+      inf_num = get_number_trailer (&p1, '.');
+      if (inf_num <= 0)
+	error (_("Inferior number must be a positive integer"));
+
+      inf = find_inferior_id (inf_num);
+      if (inf == NULL)
+	error (_("No inferior number '%d'"), inf_num);
+
+      p1 = dot + 1;
+    }
   else
-    return ptid_t (-1);
+    {
+      inf = current_inferior ();
+      p1 = number;
+    }
+
+  int fork_num = get_number_trailer (&p1, 0);
+  if (fork_num < 0)
+    error (_("Checkpoint number must be a non-negative integer"));
+
+  if (!forks_exist_p (inf))
+    error (_("Inferior %d has no checkpoints"), inf->num);
+
+  auto fork_ptr = find_fork_id (inf, fork_num);
+  if (fork_ptr == nullptr)
+    error (_("Invalid checkpoint number %d for inferior %d"),
+           fork_num, inf->num);
+
+  return { fork_ptr, inf };
 }
 
 /* Fork list <-> gdb interface.  */
@@ -296,7 +436,7 @@ fork_save_infrun_state (struct fork_info *fp)
 /* Kill 'em all, let God sort 'em out...  */
 
 void
-linux_fork_killall (void)
+linux_fork_killall (inferior *inf)
 {
   /* Walk list and kill every pid.  No need to treat the
      current inferior_ptid as special (we do not return a
@@ -304,6 +444,7 @@ linux_fork_killall (void)
      or a parent, so may get a SIGCHLD from a previously
      killed child.  Wait them all out.  */
 
+  auto &fork_list = ::fork_list (inf);
   for (fork_info &fi : fork_list)
     {
       pid_t pid = fi.ptid.pid ();
@@ -322,6 +463,7 @@ linux_fork_killall (void)
 
   /* Clear list, prepare to start fresh.  */
   fork_list.clear ();
+  highest_fork_num (inf) = 0;
 }
 
 /* The current inferior_ptid has exited, but there are other viable
@@ -329,7 +471,7 @@ linux_fork_killall (void)
    first available.  */
 
 void
-linux_fork_mourn_inferior (void)
+linux_fork_mourn_inferior (inferior *inf)
 {
   struct fork_info *last;
   int status;
@@ -341,23 +483,23 @@ linux_fork_mourn_inferior (void)
   waitpid (inferior_ptid.pid (), &status, 0);
 
   /* OK, presumably inferior_ptid is the one who has exited.
-     We need to delete that one from the fork_list, and switch
+     We need to delete that one from the fork list, and switch
      to the next available fork.  */
-  delete_fork (inferior_ptid);
+  delete_fork (inferior_ptid, inf);
 
   /* 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 ());
+  gdb_assert (!fork_list (inf).empty ());
 
-  last = find_last_fork ();
+  last = find_last_fork (inf);
   fork_load_infrun_state (last);
   gdb_printf (_("[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);
+  if (one_fork_p (inf))
+    delete_fork (inferior_ptid, inf);
 }
 
 /* The current inferior_ptid is being detached, but there are other
@@ -365,13 +507,13 @@ linux_fork_mourn_inferior (void)
    the first available.  */
 
 void
-linux_fork_detach (int from_tty, lwp_info *lp)
+linux_fork_detach (int from_tty, lwp_info *lp, inferior *inf)
 {
   gdb_assert (lp != nullptr);
   gdb_assert (lp->ptid == inferior_ptid);
 
   /* 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
+     delete it from the fork list, and switch to the next available
      fork.  But before doing the detach, do make sure that the lwp
      hasn't exited or been terminated first.  */
 
@@ -384,11 +526,12 @@ linux_fork_detach (int from_tty, lwp_info *lp)
 	       target_pid_to_str (inferior_ptid).c_str ());
     }
 
-  delete_fork (inferior_ptid);
+  delete_fork (inferior_ptid, inf);
 
   /* 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.  */
+  auto &fork_list = ::fork_list (inf);
   gdb_assert (!fork_list.empty ());
 
   fork_load_infrun_state (&fork_list.front ());
@@ -398,8 +541,8 @@ linux_fork_detach (int from_tty, lwp_info *lp)
 		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);
+  if (one_fork_p (inf))
+    delete_fork (inferior_ptid, inf);
 }
 
 /* Temporarily switch to the infrun state stored on the fork_info
@@ -412,19 +555,26 @@ class scoped_switch_fork_info
   /* 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)
+    : m_oldfp (nullptr), m_oldinf (nullptr)
   {
     if (pptid != inferior_ptid)
       {
-	struct fork_info *newfp = nullptr;
-
 	/* Switch to pptid.  */
-	m_oldfp = find_fork_ptid (inferior_ptid);
+	auto [oldfp, oldinf] = find_fork_ptid (inferior_ptid);
+	m_oldfp = oldfp;
 	gdb_assert (m_oldfp != nullptr);
-	newfp = find_fork_ptid (pptid);
+	auto [newfp, newinf]  = find_fork_ptid (pptid);
 	gdb_assert (newfp != nullptr);
 	fork_save_infrun_state (m_oldfp);
 	remove_breakpoints ();
+
+	if (oldinf != newinf)
+	  {
+	    thread_info *tp = any_thread_of_inferior (newinf);
+	    switch_to_thread (tp);
+	    m_oldinf = oldinf;
+	  }
+
 	fork_load_infrun_state (newfp);
 	insert_breakpoints ();
       }
@@ -434,12 +584,17 @@ class scoped_switch_fork_info
      didn't need to switch states, then nothing is done here either.  */
   ~scoped_switch_fork_info ()
   {
-    if (m_oldfp != nullptr)
+    if (m_oldinf != nullptr || m_oldfp != nullptr)
       {
 	/* Switch back to inferior_ptid.  */
 	try
 	  {
 	    remove_breakpoints ();
+	    if (m_oldinf != nullptr)
+	      {
+		thread_info *tp = any_thread_of_inferior (m_oldinf);
+		switch_to_thread (tp);
+	      }
 	    fork_load_infrun_state (m_oldfp);
 	    insert_breakpoints ();
 	  }
@@ -471,6 +626,7 @@ class scoped_switch_fork_info
      we were already in the desired state, and nothing needs to be
      restored.  */
   struct fork_info *m_oldfp;
+  inferior *m_oldinf;
 };
 
 static int
@@ -513,30 +669,35 @@ static void
 delete_checkpoint_command (const char *args, int from_tty)
 {
   ptid_t ptid, pptid;
-  struct fork_info *fi;
 
   if (!args || !*args)
     error (_("Requires argument (checkpoint id to delete)"));
 
-  ptid = fork_id_to_ptid (parse_and_eval_long (args));
-  if (ptid == minus_one_ptid)
-    error (_("No such checkpoint id, %s"), args);
+  auto [fi, inf] = parse_checkpoint_id (args);
+  ptid = fi->ptid;
+  gdb_assert (fi != nullptr);
+  pptid = fi->parent_ptid;
 
-  if (ptid == inferior_ptid)
-    error (_("\
+  thread_info *inf_thr = any_thread_of_inferior (inf);
+  gdb_assert (inf_thr != nullptr);
+  if (ptid == inf_thr->ptid)
+    {
+      if (inf == current_inferior ())
+	error (_("\
 Please switch to another checkpoint before deleting the current one"));
+      else
+	error (_("\
+Please switch to another checkpoint before deleting the current one\n\
+in inferior %d"), inf->num);
+    }
 
   if (ptrace (PTRACE_KILL, ptid.pid (), 0, 0))
     error (_("Unable to kill pid %s"), target_pid_to_str (ptid).c_str ());
 
-  fi = find_fork_ptid (ptid);
-  gdb_assert (fi);
-  pptid = fi->parent_ptid;
-
   if (from_tty)
     gdb_printf (_("Killed %s\n"), target_pid_to_str (ptid).c_str ());
 
-  delete_fork (ptid);
+  delete_fork (ptid, inf);
 
   if (pptid == null_ptid)
     {
@@ -554,7 +715,7 @@ Please switch to another checkpoint before deleting the current one"));
      If fi->parent_ptid is a part of lwp and it is stopped, waitpid the
      ptid.  */
   thread_info *parent = linux_target->find_thread (pptid);
-  if ((parent == NULL && find_fork_ptid (pptid))
+  if ((parent == NULL && find_fork_ptid (pptid).first != nullptr)
       || (parent != NULL && parent->state == THREAD_STOPPED))
     {
       if (inferior_call_waitpid (pptid, ptid.pid ()))
@@ -571,9 +732,8 @@ detach_checkpoint_command (const char *args, int from_tty)
   if (!args || !*args)
     error (_("Requires argument (checkpoint id to detach)"));
 
-  ptid = fork_id_to_ptid (parse_and_eval_long (args));
-  if (ptid == minus_one_ptid)
-    error (_("No such checkpoint id, %s"), args);
+  auto [fi, inf] = parse_checkpoint_id (args);
+  ptid = fi->ptid;
 
   if (ptid == inferior_ptid)
     error (_("\
@@ -585,7 +745,7 @@ Please switch to another checkpoint before detaching the current one"));
   if (from_tty)
     gdb_printf (_("Detached %s\n"), target_pid_to_str (ptid).c_str ());
 
-  delete_fork (ptid);
+  delete_fork (ptid, current_inferior ());
 }
 
 /* Print information about currently known checkpoints.  */
@@ -594,72 +754,96 @@ static void
 info_checkpoints_command (const char *arg, int from_tty)
 {
   struct gdbarch *gdbarch = get_current_arch ();
-  int requested = -1;
+  struct inferior *cur_inf = current_inferior ();
+  inferior *req_inf = nullptr;
+  fork_info *req_fi = nullptr;
   bool printed = false;
 
   if (arg && *arg)
-    requested = (int) parse_and_eval_long (arg);
+    std::tie (req_fi, req_inf) = parse_checkpoint_id (arg);
 
-  for (const fork_info &fi : fork_list)
-    {
-      if (requested > 0 && fi.num != requested)
-	continue;
-      printed = true;
-
-      bool is_current = fi.ptid == inferior_ptid;
-      if (is_current)
-	gdb_printf ("* ");
-      else
-	gdb_printf ("  ");
+  /* Figure out whether to print the inferior number in the
+     checkpoint list.  */
+  bool print_inf = (!forks_exist_p (current_inferior ())
+                    || forks_exist_in_multiple_inferiors_p ());
 
-      gdb_printf ("%d %s", fi.num, target_pid_to_str (fi.ptid).c_str ());
-      if (fi.num == 0)
-	gdb_printf (_(" (main process)"));
+  for (inferior *inf : all_inferiors ())
+    {
+      if (req_inf != nullptr && req_inf != inf)
+        continue;
 
-      if (is_current && inferior_thread ()->state == THREAD_RUNNING)
-	{
-	  gdb_printf (_(" <running>\n"));
-	  continue;
-	}
+      scoped_restore_current_pspace_and_thread restore_pspace_thread;
+      switch_to_program_space_and_thread (inf->pspace);
 
-      gdb_printf (_(" at "));
-      ULONGEST pc
-	= (is_current
-	   ? regcache_read_pc (get_thread_regcache (inferior_thread ()))
-	   : fi.pc);
-      gdb_puts (paddress (gdbarch, pc));
-
-      symtab_and_line sal = find_pc_line (pc, 0);
-      if (sal.symtab)
-	gdb_printf (_(", file %s"),
-		    symtab_to_filename_for_display (sal.symtab));
-      if (sal.line)
-	gdb_printf (_(", line %d"), sal.line);
-      if (!sal.symtab && !sal.line)
+      for (const fork_info &fi : fork_list (inf))
 	{
-	  struct bound_minimal_symbol msym;
-
-	  msym = lookup_minimal_symbol_by_pc (pc);
-	  if (msym.minsym)
-	    gdb_printf (", <%s>", msym.minsym->linkage_name ());
+	  if (req_fi != nullptr && req_fi != &fi)
+	    continue;
+	  printed = true;
+
+	  thread_info *t = any_thread_of_inferior (inf);
+	  bool is_current = fi.ptid == t->ptid;
+	  if (is_current && cur_inf == inf)
+	    gdb_printf ("*");
+	  else
+	    gdb_printf (" ");
+
+	  if (print_inf)
+	    gdb_printf ("%3d.%-2d", inf->num, fi.num);
+	  else
+	    gdb_printf ("%3d ", fi.num);
+
+	  /* Print out state associated with current checkpoint.  */
+	  if (is_current)
+	    {
+	      gdb_printf ("A");
+	      if (t->state == THREAD_RUNNING)
+		gdb_printf ("R");
+	      else
+	        gdb_printf (" ");
+	      gdb_printf (" ");
+	    }
+	  else
+	    gdb_printf ("   ");
+
+	  gdb_printf ("%s", target_pid_to_str (fi.ptid).c_str ());
+
+	  gdb_printf (_(" at "));
+	  ULONGEST pc
+	    = (is_current
+	       ? regcache_read_pc (get_thread_regcache (t))
+	       : fi.pc);
+	  gdb_puts (paddress (gdbarch, pc));
+
+	  symtab_and_line sal = find_pc_line (pc, 0);
+	  if (sal.symtab)
+	    gdb_printf (_(", file %s"),
+			symtab_to_filename_for_display (sal.symtab));
+	  if (sal.line)
+	    gdb_printf (_(", line %d"), sal.line);
+	  if (!sal.symtab && !sal.line)
+	    {
+	      struct bound_minimal_symbol msym;
+
+	      msym = lookup_minimal_symbol_by_pc (pc);
+	      if (msym.minsym)
+		gdb_printf (", <%s>", msym.minsym->linkage_name ());
+	    }
+
+	  gdb_putc ('\n');
 	}
-
-      gdb_putc ('\n');
     }
 
   if (!printed)
     {
-      if (requested > 0)
-	gdb_printf (_("No checkpoint number %d.\n"), requested);
-      else
-	gdb_printf (_("No checkpoints.\n"));
+      gdb_printf (_("No checkpoints.\n"));
     }
 }
 
 /* The PID of the process we're checkpointing.  */
 static int checkpointing_pid = 0;
 
-int
+bool
 linux_fork_checkpointing_p (int pid)
 {
   return (checkpointing_pid == pid);
@@ -689,7 +873,6 @@ checkpoint_command (const char *args, int from_tty)
   struct target_waitstatus last_target_waitstatus;
   ptid_t last_target_ptid;
   struct value *fork_fn = NULL, *ret;
-  struct fork_info *fp;
   pid_t retpid;
 
   if (!target_has_execution ()) 
@@ -727,14 +910,22 @@ checkpoint_command (const char *args, int from_tty)
   retpid = value_as_long (ret);
   get_last_target_status (nullptr, &last_target_ptid, &last_target_waitstatus);
 
-  fp = find_fork_pid (retpid);
+  auto [fp, inf] = find_fork_pid (retpid);
+
+  if (!fp)
+    error (_("Failed to find new fork"));
 
   if (from_tty)
     {
       int parent_pid;
 
-      gdb_printf (_("checkpoint %d: fork returned pid %ld.\n"),
-		  fp != NULL ? fp->num : -1, (long) retpid);
+      gdb_printf (_("Checkpoint "));
+      if (forks_exist_in_multiple_inferiors_p ())
+	gdb_printf ("%d.%d: ", inf->num, fp->num);
+      else
+	gdb_printf ("%d: ", fp->num);
+      gdb_printf (_("fork returned pid %ld.\n"), (long) retpid);
+
       if (info_verbose)
 	{
 	  parent_pid = last_target_ptid.lwp ();
@@ -745,15 +936,12 @@ checkpoint_command (const char *args, int from_tty)
 	}
     }
 
-  if (!fp)
-    error (_("Failed to find new fork"));
-
-  if (one_fork_p ())
+  if (one_fork_p (inf))
     {
       /* Special case -- if this is the first fork in the list (the
-	 list was hitherto empty), then add inferior_ptid first, as a
-	 special zeroeth fork id.  */
-      fork_list.emplace_front (inferior_ptid.pid ());
+	 list was hitherto empty), then add inferior_ptid as a special
+	 zeroeth fork id.  */
+      fork_list (inf).emplace_front (inferior_ptid.pid (), 0);
     }
 
   fork_save_infrun_state (fp);
@@ -761,40 +949,53 @@ checkpoint_command (const char *args, int from_tty)
 }
 
 static void
-linux_fork_context (struct fork_info *newfp, int from_tty)
+linux_fork_context (struct fork_info *newfp, int from_tty, inferior *newinf)
 {
-  /* Now we attempt to switch processes.  */
-  struct fork_info *oldfp;
+  bool inferior_changed = false;
 
+  /* Now we attempt to switch processes.  */
   gdb_assert (newfp != NULL);
 
-  oldfp = find_fork_ptid (inferior_ptid);
-  gdb_assert (oldfp != NULL);
+  if (newinf != current_inferior ())
+    {
+      thread_info *tp = any_thread_of_inferior (newinf);
+      switch_to_thread (tp);
+      inferior_changed = true;
+    }
 
-  fork_save_infrun_state (oldfp);
-  remove_breakpoints ();
-  fork_load_infrun_state (newfp);
-  insert_breakpoints ();
+  auto [oldfp, oldinf] = find_fork_ptid (inferior_ptid);
+  gdb_assert (oldfp != NULL);
 
-  gdb_printf (_("Switching to %s\n"),
-	      target_pid_to_str (inferior_ptid).c_str ());
+  if (oldfp == newfp)
+    {
+      if (!inferior_changed)
+	error (_("Already using checkpoint %d"), oldfp->num);
+    }
+  else
+    {
+      fork_save_infrun_state (oldfp);
+      remove_breakpoints ();
+      fork_load_infrun_state (newfp);
+      insert_breakpoints ();
+      if (!inferior_changed)
+	gdb_printf (_("Switching to %s\n"),
+		    target_pid_to_str (inferior_ptid).c_str ());
+    }
 
-  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
+  notify_user_selected_context_changed
+    (inferior_changed ? (USER_SELECTED_INFERIOR | USER_SELECTED_FRAME)
+		      : USER_SELECTED_FRAME);
 }
 
 /* Switch inferior process (checkpoint) context, by checkpoint id.  */
 static void
 restart_command (const char *args, int from_tty)
 {
-  struct fork_info *fp;
-
   if (!args || !*args)
     error (_("Requires argument (checkpoint id to restart)"));
 
-  if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: checkpoint id %s"), args);
-
-  linux_fork_context (fp, from_tty);
+  auto [fp, inf] = parse_checkpoint_id (args);
+  linux_fork_context (fp, from_tty, inf);
 }
 
 void _initialize_linux_fork ();
diff --git a/gdb/linux-fork.h b/gdb/linux-fork.h
index c553aaf0740..0233a6c8cfc 100644
--- a/gdb/linux-fork.h
+++ b/gdb/linux-fork.h
@@ -22,12 +22,13 @@
 
 struct fork_info;
 struct lwp_info;
-extern void add_fork (pid_t);
-extern struct fork_info *find_fork_pid (pid_t);
-extern void linux_fork_killall (void);
-extern void linux_fork_mourn_inferior (void);
-extern void linux_fork_detach (int, lwp_info *);
-extern int forks_exist_p (void);
-extern int linux_fork_checkpointing_p (int);
+class inferior;
+extern void add_fork (pid_t, inferior *inf);
+extern std::pair<fork_info *, inferior *> find_fork_pid (pid_t);
+extern void linux_fork_killall (inferior *inf);
+extern void linux_fork_mourn_inferior (inferior *inf);
+extern void linux_fork_detach (int, lwp_info *, inferior *inf);
+extern bool forks_exist_p (inferior *inf);
+extern bool linux_fork_checkpointing_p (int);
 
 #endif /* LINUX_FORK_H */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 2602e1f240d..e774691cb20 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1552,13 +1552,13 @@ linux_nat_target::detach (inferior *inf, int from_tty)
   gdb_assert (num_lwps (pid) == 1
 	      || (target_is_non_stop_p () && num_lwps (pid) == 0));
 
-  if (pid == inferior_ptid.pid () && forks_exist_p ())
+  if (forks_exist_p (inf))
     {
       /* Multi-fork case.  The current inferior_ptid is being detached
 	 from, but there are other viable forks to debug.  Detach from
 	 the current fork, and context-switch to the first
 	 available.  */
-      linux_fork_detach (from_tty, find_lwp_pid (ptid_t (pid)));
+      linux_fork_detach (from_tty, find_lwp_pid (ptid_t (pid)), inf);
     }
   else
     {
@@ -2076,8 +2076,12 @@ linux_handle_extended_wait (struct lwp_info *lp, int status)
 	  detach_breakpoints (ptid_t (new_pid, new_pid));
 
 	  /* Retain child fork in ptrace (stopped) state.  */
-	  if (!find_fork_pid (new_pid))
-	    add_fork (new_pid);
+	  if (find_fork_pid (new_pid).first == nullptr)
+	    {
+	      struct inferior *inf = find_inferior_ptid (linux_target,
+	                                                 lp->ptid);
+	      add_fork (new_pid, inf);
+	    }
 
 	  /* Report as spurious, so that infrun doesn't want to follow
 	     this fork.  We're actually doing an infcall in
@@ -3757,8 +3761,8 @@ linux_nat_target::kill ()
      parent will be sleeping if this is a vfork.  */
   iterate_over_lwps (pid_ptid, kill_unfollowed_child_callback);
 
-  if (forks_exist_p ())
-    linux_fork_killall ();
+  if (forks_exist_p (current_inferior ()))
+    linux_fork_killall (current_inferior ());
   else
     {
       /* Stop all threads before killing them, since ptrace requires
@@ -3789,14 +3793,14 @@ linux_nat_target::mourn_inferior ()
 
   close_proc_mem_file (pid);
 
-  if (! forks_exist_p ())
+  if (! forks_exist_p (current_inferior ()))
     /* Normal case, no other forks available.  */
     inf_ptrace_target::mourn_inferior ();
   else
     /* Multi-fork case.  The current inferior_ptid has exited, but
        there are other viable forks to debug.  Delete the exiting
        one and context-switch to the first available.  */
-    linux_fork_mourn_inferior ();
+    linux_fork_mourn_inferior (current_inferior ());
 
   /* Let the arch-specific native code know this process is gone.  */
   linux_target->low_forget_process (pid);
diff --git a/gdb/testsuite/gdb.base/checkpoint.exp b/gdb/testsuite/gdb.base/checkpoint.exp
index f87f528e167..0662fe1c654 100644
--- a/gdb/testsuite/gdb.base/checkpoint.exp
+++ b/gdb/testsuite/gdb.base/checkpoint.exp
@@ -274,17 +274,17 @@ gdb_test "kill" "" "kill all one" \
 # and confirm that all are gone
 #
 
-gdb_test "restart 0" "Not found.*" "no more checkpoint 0"
-gdb_test "restart 1" "Not found.*" "no more checkpoint 1"
-gdb_test "restart 2" "Not found.*" "no more checkpoint 2"
-gdb_test "restart 3" "Not found.*" "no more checkpoint 3"
-gdb_test "restart 4" "Not found.*" "no more checkpoint 4"
-gdb_test "restart 5" "Not found.*" "no more checkpoint 5"
-gdb_test "restart 6" "Not found.*" "no more checkpoint 6"
-gdb_test "restart 7" "Not found.*" "no more checkpoint 7"
-gdb_test "restart 8" "Not found.*" "no more checkpoint 8"
-gdb_test "restart 9" "Not found.*" "no more checkpoint 9"
-gdb_test "restart 10" "Not found.*" "no more checkpoint 10"
+gdb_test "restart 0" "has no checkpoints" "no more checkpoint 0"
+gdb_test "restart 1" "has no checkpoints" "no more checkpoint 1"
+gdb_test "restart 2" "has no checkpoints" "no more checkpoint 2"
+gdb_test "restart 3" "has no checkpoints" "no more checkpoint 3"
+gdb_test "restart 4" "has no checkpoints" "no more checkpoint 4"
+gdb_test "restart 5" "has no checkpoints" "no more checkpoint 5"
+gdb_test "restart 6" "has no checkpoints" "no more checkpoint 6"
+gdb_test "restart 7" "has no checkpoints" "no more checkpoint 7"
+gdb_test "restart 8" "has no checkpoints" "no more checkpoint 8"
+gdb_test "restart 9" "has no checkpoints" "no more checkpoint 9"
+gdb_test "restart 10" "has no checkpoints" "no more checkpoint 10"
 
 #
 # Now let's try setting a large number of checkpoints (>600)
@@ -309,7 +309,7 @@ gdb_test "continue" "breakpoint 2.*" "break2 with many checkpoints"
 set count 0
 set msg "info checkpoints with at least 600 checkpoints"
 gdb_test_multiple "info checkpoints" $msg {
-    -re "  $decimal process \[^\r\]*\r\n" {
+    -re " $decimal A? *process \[^\r\]*\r\n" {
 	incr count
 	exp_continue
     }
@@ -345,7 +345,7 @@ with_test_prefix "delete checkpoint 0" {
     clean_restart $binfile
     runto_main
 
-    gdb_test "checkpoint" "checkpoint 1: fork returned pid $decimal\\."
+    gdb_test "checkpoint" "Checkpoint 1: fork returned pid $decimal\\."
     gdb_test "restart 1" "Switching to .*"
     gdb_test "delete checkpoint 0" "Killed process $decimal"
     gdb_test "info checkpoints" [string_to_regexp "No checkpoints."]
diff --git a/gdb/testsuite/gdb.multi/checkpoint-multi.exp b/gdb/testsuite/gdb.multi/checkpoint-multi.exp
new file mode 100644
index 00000000000..a880d31d326
--- /dev/null
+++ b/gdb/testsuite/gdb.multi/checkpoint-multi.exp
@@ -0,0 +1,687 @@
+# Copyright 2009-2024 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file tests various scenarios involving multiple inferiors
+# and the checkpoint command.
+
+# Checkpoint support works only on Linux.
+require {istarget "*-*-linux*"}
+
+# Checkpoint support is implemented for the (Linux) native target,
+# which is indicated when [target_info gdb_protocol] returns the empty
+# string.
+if { [target_info gdb_protocol] != "" } {
+    return
+}
+
+set testfile "checkpoint-multi"
+
+set exec1 "hello"
+set srcfile1 ${exec1}.c
+set binfile1 [standard_output_file ${exec1}]
+
+set exec2 "goodbye"
+set srcfile2 ${exec2}.c
+set binfile2 [standard_output_file ${exec2}]
+
+set exec3 "hangout"
+set srcfile3 ${exec3}.c
+set binfile3 [standard_output_file ${exec3}]
+
+if { [build_executable ${testfile}.exp ${exec1} "${srcfile1}" {debug}] == -1 } {
+    return -1
+}
+
+if { [build_executable ${testfile}.exp ${exec2} "${srcfile2}" {debug}] == -1 } {
+    return -1
+}
+
+if { [build_executable ${testfile}.exp ${exec3} "${srcfile3}" {debug}] == -1 } {
+    return -1
+}
+
+# Start two inferiors, place a checkpoint on inferior 2, but switch
+# back to inferior 1.
+proc start_2_inferiors_checkpoint_on_inf_2 {} {
+    clean_restart $::exec1
+
+    # Start inferior 1.
+    if {[gdb_start_cmd] < 0} {
+	fail "start first inferior"
+    } else {
+	gdb_test "" "main.*" "start first inferior"
+    }
+
+    # Add a new inferior and exec into it.
+    gdb_test "add-inferior -exec $::binfile2" \
+	"Added inferior 2.*" \
+	"add inferior 2 with -exec $::exec2"
+
+    # Check that we have multiple inferiors.
+    gdb_test "info inferiors" \
+	"Executable.*$::exec1.*$::exec2.*"
+
+    # Switch to inferior 2.
+    gdb_test "inferior 2" \
+	"Switching to inferior 2.*$::exec2.*"
+
+    # Start inferior 2:
+    if {[gdb_start_cmd] < 0} {
+	fail "start second inferior"
+    } else {
+	gdb_test "" "main.*" "start second inferior"
+    }
+
+    # Set a checkpoint in inferior 2
+    gdb_test "checkpoint" "Checkpoint 1: fork returned pid $::decimal.*"
+
+    # Step one line in inferior 2.
+    gdb_test "step" "glob = 46;"
+
+    # Switch back to inferior 1.
+    gdb_test "inferior 1" "Switching to inferior 1.*$::exec1.*"
+}
+
+# Start two inferiors, place a checkpoint on inferior 2, but switch
+# back to inferior 1.  This is like the one above, except that it
+# swaps the executables loaded into inferior 1 and inferior 2.	This
+# is important for being able to test "continue to exit".  (Because... 
+# hello.c has an infinite loop, but goodbye.c doesn't.	In order to
+# test "continue to exit", we need to continue in an executable which
+# will actually exit.)
+
+proc start_2_inferiors_checkpoint_on_inf_2_alt {} {
+    clean_restart $::exec2
+
+    # Start inferior 1.
+    if {[gdb_start_cmd] < 0} {
+	fail "start first inferior"
+    } else {
+	gdb_test "" "main.*" "start first inferior"
+    }
+
+    # Add a new inferior and exec exec1 into it.
+    gdb_test "add-inferior -exec $::binfile1" \
+	"Added inferior 2.*" \
+	"add inferior 2 with -exec $::exec1"
+
+    # Check that we have two inferiors.
+    gdb_test "info inferiors" \
+	"Executable.*$::exec2.*$::exec1.*"
+
+    # Switch to inferior 2.
+    gdb_test "inferior 2" \
+	"Switching to inferior 2.*$::exec1.*"
+
+    # Start inferior 2:
+    if {[gdb_start_cmd] < 0} {
+	fail "start second inferior"
+    } else {
+	gdb_test "" "main.*" "start second inferior"
+    }
+
+    # Set a checkpoint in inferior 2
+    gdb_test "checkpoint" "Checkpoint 1: fork returned pid $::decimal.*"
+
+    # next one line in inferior 2.
+    gdb_test "next" "bar\\(\\).*"
+
+    # Switch back to inferior 1.
+    gdb_test "inferior 1" "Switching to inferior 1.*$::exec2.*"
+}
+
+with_test_prefix "check detach on non-checkpointed inferior" {
+    start_2_inferiors_checkpoint_on_inf_2
+    gdb_test "detach" "Detaching from program.*$::exec1.*Inferior 1.*detached.*"
+}
+
+with_test_prefix "check kill on non-checkpointed inferior" {
+    start_2_inferiors_checkpoint_on_inf_2
+    gdb_test "kill" "" "kill non-checkpointed inferior" \
+	     "Kill the program being debugged.*y or n. $" "y"
+}
+
+with_test_prefix "check restart 0 on non-checkpointed inferior" {
+    start_2_inferiors_checkpoint_on_inf_2
+    gdb_test "restart 0" "Inferior 1 has no checkpoints"
+    gdb_test "restart 2.0" "Switching to inferior 2.*?goodbye.*?#0 +mailand .*?glob = 46;.*"
+}
+
+with_test_prefix "check restart 1 on non-checkpointed inferior" {
+    start_2_inferiors_checkpoint_on_inf_2
+    gdb_test "restart 1" "Inferior 1 has no checkpoints"
+    gdb_test "restart 2.1" "Switching to inferior 2.*?goodbye.*?#0 +main .*?mailand\\(\\);.*"
+}
+
+with_test_prefix "check continue to exit on non-checkpointed inferior" {
+    start_2_inferiors_checkpoint_on_inf_2_alt
+    gdb_test "continue" "Inferior 1.*? exited normally.*"
+}
+
+set proc_re "(?:process $::decimal|Thread $::hex \\(LWP $::decimal\\))"
+set main_proc "\\(main process\\)"
+set hello_c "hello\\.c"
+set goodbye_c "goodbye\\.c"
+set hangout_c "hangout\\.c"
+
+with_test_prefix "two inferiors with checkpoints" {
+    start_2_inferiors_checkpoint_on_inf_2
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   2.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 1"
+    gdb_test "checkpoint" "Checkpoint 1\\.1: fork returned pid $::decimal.*" \
+	     "checkpoint in inferior 1"
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "\\*  1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 2"
+    gdb_test "restart 0" "Already using checkpoint 0"
+    gdb_test "restart 2.0" \
+	     "\\\[Switching to inferior 2.*?mailand.*?glob = 46;.*"
+    gdb_test "next" "\}"
+
+    gdb_test "restart 1" "^Switching to $proc_re.*?#0  main \\(\\) at.*?$goodbye_c.*mailand\\(\\);"
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 3"
+
+    # Doing "info_checkpoints" twice in a row might seem pointless,
+    # but during work on making the checkpoint code inferior aware,
+    # there was a point at which doing it twice in a row did not
+    # produce the same output.
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 4"
+
+    # Switch back to checkpoint 0; again, there should be no
+    # "Switching to inferior" message.
+    gdb_test "restart 0" "^Switching to $proc_re.*?#0  mailand \\(\\) at.*?$goodbye_c.*\}" \
+	     "restart 0 #2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "\\*  2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 5"
+
+    # Try switching to invalid checkpoints:
+    gdb_test "restart 3" "Invalid checkpoint number 3 for inferior 2"
+    gdb_test "restart 2" "Invalid checkpoint number 2 for inferior 2"
+    gdb_test "restart -1" "Checkpoint number must be a non-negative integer"
+    gdb_test "restart 2.3" "Invalid checkpoint number 3 for inferior 2"
+    gdb_test "restart 3.0" "No inferior number '3'"
+    gdb_test "restart 1.2" "Invalid checkpoint number 2 for inferior 1"
+    gdb_test "restart 1.3" "Invalid checkpoint number 3 for inferior 1"
+    gdb_test "restart 1.-1" "Checkpoint number must be a non-negative integer"
+    gdb_test "restart -1.0" "Inferior number must be a positive integer"
+
+    # Switch to checkpoint 3; this time, we should see a "Switching to
+    # inferior" message.
+    gdb_test "restart 1.1" \
+	     "\\\[Switching to inferior 1.*?main.*?$hello_c.*?alarm \\(240\\);"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "\\*  1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 6"
+
+    gdb_test "restart 2.1" \
+	     "Switching to inferior 2.*?#0  main \\(\\) at.*?$goodbye_c.*mailand\\(\\);"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 7"
+
+    gdb_test "checkpoint" "Checkpoint 2\\.2: fork returned pid $::decimal.*" \
+	     "second checkpoint in inferior 2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 8"
+
+    gdb_test "checkpoint" "Checkpoint 2.3: fork returned pid $::decimal.*" \
+	     "third checkpoint in inferior 2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.3    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 9"
+
+    gdb_test "continue" \
+	     "Inferior 2 \\(process $decimal\\) exited normally.*?Switching to $proc_re.*?" \
+	     "continue to exit in checkpoint 2.1"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.3 A  $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 10"
+
+    gdb_test "continue" \
+	     "Inferior 2 \\(process $decimal\\) exited normally.*?Switching to process $decimal.*?" \
+	     "continue to exit in checkpoint 2.3"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 11"
+
+    gdb_test "continue" \
+	     "Inferior 2 \\(process $decimal\\) exited normally.*?Switching to process $decimal.*?" \
+	     "continue to exit in checkpoint 2.2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?"] \
+	"info checkpoints 12"
+
+    gdb_test "checkpoint" "Checkpoint 2.1: fork returned pid $::decimal.*" \
+	     "fourth checkpoint in inferior 2"
+
+    gdb_test "checkpoint" "Checkpoint 2.2: fork returned pid $::decimal.*" \
+	     "fifth checkpoint in inferior 2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "\\*  2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 13"
+
+    gdb_test "delete checkpoint 2.0" \
+	     "Please switch to another checkpoint before deleting the current one"
+
+    gdb_test "restart 2.1" \
+	     "^Switching to process.*?#0  mailand \\(\\) at.*?$goodbye_c.*\}" \
+	     "restart 2.1 #2"
+
+    gdb_test "delete checkpoint 2.0" \
+	     "Killed process $::decimal" \
+	     "delete checkpoint 2.0 #2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "\\*  2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 14"
+
+    gdb_test "delete checkpoint 2.2" \
+	     "Killed process $::decimal"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?"] \
+	"info checkpoints 15"
+
+    gdb_test "checkpoint" "Checkpoint 2.1: fork returned pid $::decimal.*" \
+	     "sixth checkpoint in inferior 2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "\\*  2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 16"
+
+    gdb_test "inferior 1" "Switching to inferior 1.*?alarm \\(240\\);" \
+	     "inferior 1 #2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "\\*  1\\.1 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 17"
+
+    gdb_test "kill" "\\\[Inferior 1 \\(process $::decimal\\) killed\\\]" \
+	     "kill inferior 1" \
+	     "Kill the program being debugged.*y or n. $" "y"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 18"
+
+    gdb_test "checkpoint" "The program is not being run\\." \
+	     "checkpoint in non-running inferior"
+
+    gdb_test "start" "Starting program.*?hello.*?alarm \\(240\\);"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 19"
+
+    gdb_test "checkpoint" "Checkpoint 1.1: fork returned pid $::decimal.*" \
+	     "second checkpoint in inferior 1"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "\\*  1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 20"
+}
+
+with_test_prefix "three inferiors with checkpoints" {
+    start_2_inferiors_checkpoint_on_inf_2
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?"] \
+	"info checkpoints 1"
+
+    # Add a third inferior and exec into it.
+    gdb_test "add-inferior -exec $::binfile3" \
+	     "Added inferior 3.*" \
+	     "add inferior 3 with -exec $::exec3"
+
+    # Check that we have three inferiors.
+    gdb_test "info inferiors" \
+	     "Executable.*?\\* 1 .*?$::exec1.*? 2 .*?$::exec2.*? 3 .*?$::exec3.*?" \
+	     "check for three inferiors"
+
+    # Switch to inferior 3.
+    gdb_test "inferior 3" \
+	"Switching to inferior 3.*$::exec3.*"
+
+    # Start inferior 2:
+    if {[gdb_start_cmd] < 0} {
+	fail "start third inferior"
+    } else {
+	gdb_test "" "main.*" "start third inferior"
+    }
+
+    gdb_test "checkpoint" "Checkpoint 3\\.1: fork returned pid $::decimal.*" \
+	     "first checkpoint in inferior 3"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.1    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 2"
+
+    gdb_test "inferior 1" "Switching to inferior 1.*?alarm \\(240\\);" \
+	     "inferior 1 #2"
+
+    gdb_test "checkpoint" "Checkpoint 1\\.1: fork returned pid $::decimal.*" \
+	     "first checkpoint in inferior 1"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "\\*  1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.1    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 3"
+
+    gdb_test "restart 2.1" \
+	     "Switching to inferior 2.*?#0  main \\(\\) at.*?$goodbye_c.*mailand\\(\\);" \
+	     "restart 1"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.1    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 4"
+
+    gdb_test "next" "foo\\(glob\\);"
+
+    gdb_test "checkpoint" "Checkpoint 2\\.2: fork returned pid $::decimal.*" \
+	     "second checkpoint in inferior 2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.1    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 5"
+
+    gdb_test "inferior 3" "Switching to inferior 3.*?alarm \\(30\\);" \
+	     "inferior 3 #2"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.1    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 6"
+
+    gdb_test "kill" "\\\[Inferior 3 \\(process $::decimal\\) killed\\\]" \
+	     "kill inferior 3" \
+	     "Kill the program being debugged.*y or n. $" "y"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.0    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?" ]\
+	"info checkpoints 7"
+
+    gdb_test "delete checkpoint 0" \
+	     "Inferior 3 has no checkpoints"
+    gdb_test "delete checkpoint 2.0" \
+	     "Killed process $::decimal"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2    $proc_re at $::hex, file.*?$goodbye_c.*?" ]\
+	"info checkpoints 8"
+
+    gdb_test "restart 2.2" \
+	     "Switching to inferior 2.*?#0  main \\(\\) at.*?$goodbye_c.*foo\\(glob\\);"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?" ]\
+	"info checkpoints 9"
+
+    gdb_test "inferior 3" "\\\[Switching to inferior 3 \\\[<null>\\\] \\(.*?$::exec3\\)\\\]" \
+	     "inferior 3 #3"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?" ]\
+	"info checkpoints 10"
+
+    gdb_test "start" "Starting program.*?hangout.*?alarm \\(30\\);"
+
+    gdb_test "checkpoint" "Checkpoint 3\\.1: fork returned pid $::decimal.*" \
+	     "second checkpoint in inferior 3"
+
+    gdb_test "checkpoint" "Checkpoint 3\\.2: fork returned pid $::decimal.*" \
+	     "third checkpoint in inferior 3"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.1    $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.2    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 11"
+
+    gdb_test "delete checkpoint 1" \
+	     "Killed process $::decimal"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.2    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 12"
+
+    # Switch to inferior 1, add another checkpoint - so that there are
+    # three of them in inferior 1 - then switch back to inferior 3 and
+    # delete active checkpoint in inferior 1.  Then, switch to
+    # inferior 1 and attempt to add another checkpoint.  During
+    # development, a "Cannot access memory at address ..." message was
+    # seen.  This was a bug - there were several problems, but one
+    # of them was that the checkpoint in question was an "active"
+    # checkpoint.  The fix was to disallow this case.
+    gdb_test "inferior 1" "Switching to inferior 1.*?alarm \\(240\\);" \
+	     "inferior 1 #3"
+
+    gdb_test "checkpoint" "Checkpoint 1\\.2: fork returned pid $::decimal.*" \
+	     "second checkpoint in inferior 1"
+
+    gdb_test "inferior 3" "Switching to inferior 3.*?alarm \\(30\\);" \
+	     "inferior 3 #4"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.1    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.2    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.2    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 13"
+
+    # Check that deleting active checkpoints in other (non-current)
+    # inferiors is disallowed.
+    gdb_test "delete checkpoint 1.0" \
+	     "Please switch to another checkpoint before deleting the current one\[ \r\n\]+in inferior 1"
+
+    # But deleting non-active checkpoints, even in other inferiors 
+    # should work.
+    gdb_test "delete checkpoint 1.1" \
+	     "Killed process $::decimal"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "   1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.2    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "\\*  3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.2    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 14"
+
+    gdb_test "inferior 1" "Switching to inferior 1.*?alarm \\(240\\);" \
+	     "inferior 1 #4"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "\\*  1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.2    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.2    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 15"
+
+    gdb_test "checkpoint" "Checkpoint 1\\.3: fork returned pid $::decimal.*" \
+	     "third checkpoint in inferior 1"
+
+    gdb_test "info checkpoints" \
+	[multi_line \
+	    "\\*  1\\.0 A  $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.2    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   1\\.3    $proc_re at $::hex, file.*?$hello_c.*?" \
+	    "   2\\.1    $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   2\\.2 A  $proc_re at $::hex, file.*?$goodbye_c.*?" \
+	    "   3\\.0 A  $proc_re at $::hex, file.*?$hangout_c.*?" \
+	    "   3\\.2    $proc_re at $::hex, file.*?$hangout_c.*?"] \
+	"info checkpoints 16"
+
+    gdb_test "x/i \$pc" "=> $::hex <main.*"
+
+    gdb_test "remove-inferior 3" \
+	     "warning: Can not remove active inferior 3\."
+}
