Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* Move the multi-forks support to the generic multi-inferiors support.
@ 2009-05-30 23:13 Pedro Alves
  2009-05-31 20:46 ` Tom Tromey
                   ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Pedro Alves @ 2009-05-30 23:13 UTC (permalink / raw)
  To: gdb-patches; +Cc: Eli Zaretskii, Michael Snyder

Hi,

I was mentioning this earlier in the other thread that adds a new command
to allow resuming all threads of all processes, or just of the
current process.  I may have added confusion because I was talking
about this change, while nobody else was seeing it, naturally.  So,
I'm posting it now, in hopes of making things clearer.

The current "multi-forks" support, as implemented by linux-fork.c,
serves as backend for two things.  Staying attached to parent and
child forks simultaneously, and, for checkpoint support.  The former,
is a limited implementation of multi-process/multi-exec.  If one of
the child or parent execs, all hell breaks loose.  It doesn't really
support multi-threaded inferiors.  It bypasses linux-nat.c for killing
and detaching, leaving state inconsistent, and possibly mis-kills or
mis-detaches, etc., etc.  This is what I want to fix.

The checkpoints support however, is a different thing altogether.
The fact that it is implemented using forks for doing the grunt work,
is an implementation detail.  In fact, I believe that the correct
abstraction is for all these checkpoint forks to be associated with
the same inferior id.  When switching between checkpoints, the process
switching should be made transparent to most of GDB and the user (as
already is as possible).  I'm not intersted at this point in improving
the checkpoints implementation to support multi-threaded apps, or
to make it behave if one of the checkpoints does an exec.  However,
I don't want to break it more than it is currently --- it's useful
as is.

So, what I want to do is, to get rid of the linux-fork.c "multi-forks"
support, replace it with generic "multi-inferior" support, but
leave the checkpoint support there.  That is what the patch
below does.

With this in place, it is still possible to debug multiple
forks at once, under the same GDB session.   With a few more patches,
and with the patches I've been posting applied, it will be possible
to follow different execs for each of these forks, all under
the same GDB.  This will allow, for example, the
mythical "gdb /usr/bin/make" -> follow all gcc, ld, as, sed, whatnot
invocations make does until gcc crashes, and catch that SIGSEGV.  So,
as you see, multi-forks is a subset of multi-process/multi-executable
debugging.

While doing this, there are few a commands that don't make sense
to have anymore in a generalized multi-inferior framework.  E.g.,
"info forks" stops making sense if one of the forks has done
an execl...

So, to do this, I'm proposing a few command replacements, similar
to what I've described here:

 http://sourceware.org/gdb/wiki/MultiProcess

|-----------------+-------------------------------------|
| Current command | New command                         |
|-----------------+-------------------------------------|
| delete fork NUM | kill inferior NUM                   |
|-----------------+-------------------------------------|
| detach fork NUM | detach inferior NUM                 |
|-----------------+-------------------------------------|
| restart NUM     | keep, but apply only to checkpoints |
|-----------------+-------------------------------------|
| checkpoint      | keep, adds a new checkpoint.        |
|                 | make followfork child/parent have   |
|                 | no effect on this command (always   |
|                 | follow the parent)                  |
|-----------------+-------------------------------------|
| info forks      | delete, replaced by the             |
|                 | generic "info inferiors"            |
|-----------------+-------------------------------------|
| fork NUM        | delete, replaced by the new generic |
|                 | command 'inferior NUM'              |
|-----------------+-------------------------------------|

Luckily, the multi-forks/checkpoints commands are marked
experimental in GDB.

Note that this even fixes a couple of bugs.  E.g., doing
a checkpoint with "set follow-fork-mode child" was broken
currently.

This applies on top of this other patch,

 http://sourceware.org/ml/gdb-patches/2009-05/msg00650.html

... and has no regressions.

I can now make the linux-nat.c target report multi-process
support to the core of GDB with this (in fact, I have to,
so that it only tries to resume threads of a single-inferior,
since that support is only enabled if the target reports support
for multi-process).

What do you think?  I know multi-inferior/multi-exec/multi-fork
debugging needs better examples in the manual, however, we still
don't have the necessary support to show that off yet.  I'm trying
to push things incrementally without breaking much...

And, I'll need to write a NEWS entry in case this is good
to go, I know.  ;-)

Eli, I hope this clears a few things up.  Michael, CCing you, since
these checkpoints stuff is your baby.

-- 
Pedro Alves

gdb/
2009-05-30  Pedro Alves  <pedro@codesourcery.com>

	* linux-nat.c (linux_child_follow_fork): If we're staying attached
	to the child process, enable event reporting on it.  Don't handle
	checkpoints here.  Instead, add the child fork to the lwp thread
	and inferior lists without clobbering the previous inferior.  Let
	the thread_db layer learn about a new child process, even if
	following the parent.
	(linux_nat_switch_fork): Delete lwps of the current inferior only,
	instead of clearing the whole list.  Use thread_change_ptid to
	give the core the illusion the new checkpoint is still the same
	inferior.  Clear the register cache.
	(linux_handle_extended_wait): Handle checkpoints here.
	(linux_multi_process): Turn on.
	* linux-fork.c (struct fork_info) <pc>: Remove field.
	(init_fork_list): Do not delete the checkpoint from the inferior
	list (it is not there).
	(fork_load_infrun_state): Don't switch inferior_ptid here.  Pass
	the new checkpoint's ptid to linux_nat_switch_fork.
	(fork_save_infrun_state): Make static.  Don't stop the pc field of
	fork_info, it's gone.
	(linux_fork_mourn_inferior): Don't delete the checkpoint from the
	inferior list, it's not there.
	(linux_fork_detach): Ditto.
	(delete_fork_command): Replace mention of fork/checkpoint by
	checkpoint only.
	(detach_fork_command): Likewise.  Don't delete the checkpoint from
	the inferior list.
	(info_forks_command): Adjust.
	(restore_detach_fork): Delete.
	(checkpointing_pid): New.
	(linux_fork_checkpointing_p): New.
	(save_detach_fork): Delete.
	(checkpoint_command): Delete temp_detach_fork.  Don't remove
	breakpoints, that's a nop.  Store the pid of the process we're
	checkpointing, and use make_cleanup_restore_integer to restore it.
	Don't reinsert breakpoints here.
	(process_command, fork_command): Delete.
	(restart_command): Update comments to only mention checkpoints,
	not forks.
	(_initialize_linux_fork): Delete "fork", "process", "info forks"
	commands.
	* linux-fork.h (fork_save_infrun_state, fork_list): Delete
	declarations.
	(linux_fork_checkpointing_p): Declare.
	* cli/cli-cmds.c (killlist): New.
	* cli/cli-cmds.h (killlist): Declare.
	* gdbcmd.h (killlist): Declare.
	* inferior.c: Include "gdbthread.h".
	(detach_inferior_command, kill_inferior_command)
	(inferior_command): New.
	(info_inferiors_command): Allow specifying a specific inferior id.
	(_initialize_inferiors): Register "inferior", "kill inferior" and
	"detach inferior" commands.
	* infcmd.c (_initialize_infcmd): Make "kill" a prefix command.
	* gdbthread.h (any_thread_of_process): Declare.
	* thread.c (any_thread_of_process): New.

gdb/testsuite/
2009-05-30  Pedro Alves  <pedro@codesourcery.com>

	* gdb.base/multi-forks.exp: Only run detach-on-fork tests on
	linux.  Adjust to use "inferior", "info inferiors", "detach
	inferior" and "kill inferior" instead of "restart", "info fork",
	"detach fork" and "delete fork".
	* gdb.base/ending-run.exp: Spell out "info".
	* gdb.base/help.exp: Adjust to use test_prefix_command_help for
	the "kill" command.

gdb/doc/
2009-05-30  Pedro Alves  <pedro@codesourcery.com>

	* gdb.texinfo (Debugging multiple inferiors): Document the
	"inferior", "detach inferior" and "kill inferior" commands.
	(Debugging Programs with Multiple Processes): Adjust to mention
	generic "inferior" commands.  Delete mention of "detach fork" and
	"delete fork".  Cross reference to "Debugging multiple inferiors"
	section.

---
 gdb/cli/cli-cmds.c                     |    4 +
 gdb/cli/cli-cmds.h                     |    4 +
 gdb/doc/gdb.texinfo                    |   88 +++++++++++------------
 gdb/gdbcmd.h                           |    4 +
 gdb/gdbthread.h                        |    3 
 gdb/infcmd.c                           |    5 -
 gdb/inferior.c                         |  120 +++++++++++++++++++++++++++++++-
 gdb/linux-fork.c                       |  123 ++++++---------------------------
 gdb/linux-fork.h                       |    6 -
 gdb/linux-nat.c                        |  106 ++++++++++++++++++++--------
 gdb/testsuite/gdb.base/ending-run.exp  |    2 
 gdb/testsuite/gdb.base/help.exp        |    4 -
 gdb/testsuite/gdb.base/multi-forks.exp |   86 +++++++++++++----------
 gdb/thread.c                           |   12 +++
 14 files changed, 346 insertions(+), 221 deletions(-)

Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/linux-nat.c	2009-05-30 23:13:33.000000000 +0100
@@ -338,6 +338,12 @@ static int stop_callback (struct lwp_inf
 
 static void block_child_signals (sigset_t *prev_mask);
 static void restore_child_signals_mask (sigset_t *prev_mask);
+
+struct lwp_info;
+static struct lwp_info *add_lwp (ptid_t ptid);
+static void purge_lwp_list (int pid);
+static struct lwp_info *find_lwp_pid (ptid_t ptid);
+
 \f
 /* Trivial list manipulation functions to keep track of a list of
    new stopped processes.  */
@@ -587,6 +593,9 @@ linux_child_follow_fork (struct target_o
     parent_pid = ptid_get_pid (inferior_ptid);
   child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
 
+  if (!detach_fork)
+    linux_enable_event_reporting (pid_to_ptid (child_pid));
+
   if (! follow_child)
     {
       /* We're already attached to the parent, by default. */
@@ -617,8 +626,9 @@ linux_child_follow_fork (struct target_o
 	}
       else
 	{
-	  struct fork_info *fp;
 	  struct inferior *parent_inf, *child_inf;
+	  struct lwp_info *lp;
+	  struct cleanup *old_chain;
 
 	  /* Add process to GDB's tables.  */
 	  child_inf = add_inferior (child_pid);
@@ -627,11 +637,16 @@ linux_child_follow_fork (struct target_o
 	  child_inf->attach_flag = parent_inf->attach_flag;
 	  copy_terminal_info (child_inf, parent_inf);
 
-	  /* Retain child fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (child_pid);
-	  if (!fp)
-	    fp = add_fork (child_pid);
-	  fork_save_infrun_state (fp, 0);
+	  old_chain = save_inferior_ptid ();
+
+	  inferior_ptid = ptid_build (child_pid, child_pid, 0);
+	  add_thread (inferior_ptid);
+	  lp = add_lwp (inferior_ptid);
+	  lp->stopped = 1;
+
+	  check_for_thread_db ();
+
+	  do_cleanups (old_chain);
 	}
 
       if (has_vforked)
@@ -692,6 +707,7 @@ linux_child_follow_fork (struct target_o
     {
       struct thread_info *tp;
       struct inferior *parent_inf, *child_inf;
+      struct lwp_info *lp;
 
       /* Before detaching from the parent, remove all breakpoints from it. */
       remove_breakpoints ();
@@ -733,30 +749,33 @@ linux_child_follow_fork (struct target_o
 
       if (has_vforked)
 	{
+	  struct lwp_info *parent_lwp;
+
 	  linux_parent_pid = parent_pid;
+
+	  /* Get rid of the inferior on the core side as well.  */
+	  inferior_ptid = null_ptid;
 	  detach_inferior (parent_pid);
-	}
-      else if (!detach_fork)
-	{
-	  struct fork_info *fp;
-	  /* Retain parent fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (parent_pid);
-	  if (!fp)
-	    fp = add_fork (parent_pid);
-	  fork_save_infrun_state (fp, 0);
 
-	  /* Also add an entry for the child fork.  */
-	  fp = find_fork_pid (child_pid);
-	  if (!fp)
-	    fp = add_fork (child_pid);
-	  fork_save_infrun_state (fp, 0);
+	  /* Also get rid of all its lwps.  We will detach from this
+	     inferior soon-ish, but, we will still get an exit event
+	     reported through waitpid when it exits.  If we didn't get
+	     rid of the lwps from our list, we would end up reporting
+	     the inferior exit to the core, which would then try to
+	     mourn a non-existing (from the core's perspective)
+	     inferior.  */
+	  parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
+	  purge_lwp_list (GET_PID (parent_lwp->ptid));
+	  linux_parent_pid = parent_pid;
 	}
-      else
+      else if (detach_fork)
 	target_detach (NULL, 0);
 
       inferior_ptid = ptid_build (child_pid, child_pid, 0);
+      add_thread (inferior_ptid);
+      lp = add_lwp (inferior_ptid);
+      lp->stopped = 1;
 
-      linux_nat_switch_fork (inferior_ptid);
       check_for_thread_db ();
     }
 
@@ -1073,22 +1092,30 @@ iterate_over_lwps (ptid_t filter,
   return NULL;
 }
 
-/* Update our internal state when changing from one fork (checkpoint,
-   et cetera) to another indicated by NEW_PTID.  We can only switch
-   single-threaded applications, so we only create one new LWP, and
-   the previous list is discarded.  */
+/* Update our internal state when changing from one checkpoint to
+   another indicated by NEW_PTID.  We can only switch single-threaded
+   applications, so we only create one new LWP, and the previous list
+   is discarded.  */
 
 void
 linux_nat_switch_fork (ptid_t new_ptid)
 {
   struct lwp_info *lp;
 
-  init_lwp_list ();
+  purge_lwp_list (GET_PID (inferior_ptid));
+
   lp = add_lwp (new_ptid);
   lp->stopped = 1;
 
-  init_thread_list ();
-  add_thread_silent (new_ptid);
+  /* This changes the thread's ptid while preserving the gdb thread
+     num.  Also changes the inferior pid, while preserving the
+     inferior num.  */
+  thread_change_ptid (inferior_ptid, new_ptid);
+
+  /* We've just told GDB core that the thread changed target id, but,
+     in fact, it really is a different thread, with different register
+     contents.  */
+  registers_changed ();
 }
 
 /* Handle the exit of a single thread LP.  */
@@ -1815,6 +1842,25 @@ linux_handle_extended_wait (struct lwp_i
 
       ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
 
+      if (event == PTRACE_EVENT_FORK
+	  && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+	{
+	  struct fork_info *fp;
+
+	  /* This won't actually modify the breakpoint list, but will
+	     physically remove the breakpoints from the child.  */
+	  detach_breakpoints (new_pid);
+
+	  /* Retain child fork in ptrace (stopped) state.  */
+	  fp = find_fork_pid (new_pid);
+	  if (!fp)
+	    fp = add_fork (new_pid);
+
+	  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+	  linux_enable_event_reporting (pid_to_ptid (new_pid));
+	  return 0;
+	}
+
       if (event == PTRACE_EVENT_FORK)
 	ourstatus->kind = TARGET_WAITKIND_FORKED;
       else if (event == PTRACE_EVENT_VFORK)
@@ -4289,7 +4335,7 @@ linux_nat_supports_non_stop (void)
 /* True if we want to support multi-process.  To be removed when GDB
    supports multi-exec.  */
 
-int linux_multi_process = 0;
+int linux_multi_process = 1;
 
 static int
 linux_nat_supports_multi_process (void)
Index: src/gdb/linux-fork.c
===================================================================
--- src.orig/gdb/linux-fork.c	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/linux-fork.c	2009-05-30 23:03:52.000000000 +0100
@@ -27,6 +27,7 @@
 #include "gdb_string.h"
 #include "linux-fork.h"
 #include "linux-nat.h"
+#include "gdbthread.h"
 
 #include <sys/ptrace.h>
 #include "gdb_wait.h"
@@ -52,7 +53,6 @@ struct fork_info
   struct regcache *savedregs;	/* Convenient for info fork, saves 
 				   having to actually switch contexts.  */
   int clobber_regs;		/* True if we should restore saved regs.  */
-  ULONGEST pc;			/* PC for info fork.  */
   off_t *filepos;		/* Set of open file descriptors' offsets.  */
   int maxfd;
 };
@@ -210,7 +210,6 @@ init_fork_list (void)
   for (fp = fork_list; fp; fp = fpnext)
     {
       fpnext = fp->next;
-      delete_inferior (ptid_get_pid (fp->ptid));
       free_fork (fp);
     }
 
@@ -240,9 +239,7 @@ fork_load_infrun_state (struct fork_info
   extern void nullify_last_target_wait_ptid ();
   int i;
 
-  inferior_ptid = fp->ptid;
-
-  linux_nat_switch_fork (inferior_ptid);
+  linux_nat_switch_fork (fp->ptid);
 
   if (fp->savedregs && fp->clobber_regs)
     regcache_cpy (get_current_regcache (), fp->savedregs);
@@ -268,7 +265,7 @@ fork_load_infrun_state (struct fork_info
 /* Save infrun state for the fork PTID.
    Exported for use by linux child_follow_fork.  */
 
-extern void
+static void
 fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
 {
   char path[MAXPATHLEN];
@@ -280,7 +277,6 @@ fork_save_infrun_state (struct fork_info
 
   fp->savedregs = regcache_dup (get_current_regcache ());
   fp->clobber_regs = clobber_regs;
-  fp->pc = regcache_read_pc (get_current_regcache ());
 
   if (clobber_regs)
     {
@@ -370,8 +366,6 @@ linux_fork_mourn_inferior (void)
      We need to delete that one from the fork_list, and switch
      to the next available fork.  */
   delete_fork (inferior_ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (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
@@ -402,8 +396,6 @@ linux_fork_detach (char *args, int from_
     error (_("Unable to detach %s"), target_pid_to_str (inferior_ptid));
 
   delete_fork (inferior_ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (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
@@ -429,14 +421,14 @@ delete_fork_command (char *args, int fro
   ptid_t ptid;
 
   if (!args || !*args)
-    error (_("Requires argument (fork/checkpoint id to delete)"));
+    error (_("Requires argument (checkpoint id to delete)"));
 
   ptid = fork_id_to_ptid (parse_and_eval_long (args));
   if (ptid_equal (ptid, minus_one_ptid))
-    error (_("No such fork/checkpoint id, %s"), args);
+    error (_("No such checkpoint id, %s"), args);
 
   if (ptid_equal (ptid, inferior_ptid))
-    error (_("Please switch to another fork/checkpoint before deleting the current one"));
+    error (_("Please switch to another checkpoint before deleting the current one"));
 
   if (ptrace (PTRACE_KILL, PIDGET (ptid), 0, 0))
     error (_("Unable to kill pid %s"), target_pid_to_str (ptid));
@@ -445,8 +437,6 @@ delete_fork_command (char *args, int fro
     printf_filtered (_("Killed %s\n"), target_pid_to_str (ptid));
 
   delete_fork (ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (ptid));
 }
 
 static void
@@ -455,14 +445,15 @@ detach_fork_command (char *args, int fro
   ptid_t ptid;
 
   if (!args || !*args)
-    error (_("Requires argument (fork id to detach)"));
+    error (_("Requires argument (checkpoint id to detach)"));
 
   ptid = fork_id_to_ptid (parse_and_eval_long (args));
   if (ptid_equal (ptid, minus_one_ptid))
-    error (_("No such fork id, %s"), args);
+    error (_("No such checkpoint id, %s"), args);
 
   if (ptid_equal (ptid, inferior_ptid))
-    error (_("Please switch to another fork before detaching the current one"));
+    error (_("\
+Please switch to another checkpoint before detaching the current one"));
 
   if (ptrace (PTRACE_DETACH, PIDGET (ptid), 0, 0))
     error (_("Unable to detach %s"), target_pid_to_str (ptid));
@@ -471,8 +462,6 @@ detach_fork_command (char *args, int fro
     printf_filtered (_("Detached %s\n"), target_pid_to_str (ptid));
 
   delete_fork (ptid);
-  /* Delete process from GDB's process table.  */
-  detach_inferior (ptid_get_pid (ptid));
 }
 
 /* Print information about currently known forks.  */
@@ -506,7 +495,7 @@ info_forks_command (char *arg, int from_
       else
 	{
 	  printf_filtered ("  ");
-	  pc = fp->pc;
+	  pc = regcache_read_pc (fp->savedregs);
 	}
       printf_filtered ("%d %s", fp->num, target_pid_to_str (fp->ptid));
       if (fp->num == 0)
@@ -546,25 +535,13 @@ info_forks_command (char *arg, int from_
     }
 }
 
-/* Save/restore mode variable 'detach_fork':
-   We need to temporarily take over this mode variable, while
-   preserving the user-specified state, and make sure that it 
-   gets restored in case of error.
+/* The PID of the process we're checkpointing.  */
+static int checkpointing_pid = 0;
 
-   The int pointer that we use comes from the caller, so we can
-   be called more than once (even though currently we don't need to).  */
-
-static void 
-restore_detach_fork (void *arg)
+int
+linux_fork_checkpointing_p (int pid)
 {
-  detach_fork = *(int *) arg;
-}
-
-static struct cleanup *
-save_detach_fork (int *saved_val)
-{
-  *saved_val = detach_fork;
-  return make_cleanup (restore_detach_fork, (void *) saved_val);
+  return (checkpointing_pid == pid);
 }
 
 static void
@@ -579,12 +556,6 @@ checkpoint_command (char *args, int from
   pid_t retpid;
   struct cleanup *old_chain;
   long i;
-  /* Make this temp var static, 'cause it's used in the error context.  */
-  static int temp_detach_fork;
-
-  /* Remove breakpoints, so that they are not inserted
-     in the forked process.  */
-  remove_breakpoints ();
 
   /* Make the inferior fork, record its (and gdb's) state.  */
 
@@ -598,8 +569,11 @@ checkpoint_command (char *args, int from
 
   gdbarch = get_objfile_arch (fork_objf);
   ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
-  old_chain = save_detach_fork (&temp_detach_fork);
-  detach_fork = 0;
+
+  /* Tell linux-nat.c that we're checkpointing this inferior.  */
+  old_chain = make_cleanup_restore_integer (&checkpointing_pid);
+  checkpointing_pid = PIDGET (inferior_ptid);
+
   ret = call_function_by_hand (fork_fn, 0, &ret);
   do_cleanups (old_chain);
   if (!ret)	/* Probably can't happen.  */
@@ -627,7 +601,6 @@ checkpoint_command (char *args, int from
   if (!fp)
     error (_("Failed to find new fork"));
   fork_save_infrun_state (fp, 1);
-  insert_breakpoints ();
 }
 
 static void
@@ -654,37 +627,7 @@ linux_fork_context (struct fork_info *ne
   print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
 }
 
-/* Switch inferior process (fork) context, by process id.  */
-static void
-process_command (char *args, int from_tty)
-{
-  struct fork_info *fp;
-
-  if (!args || !*args)
-    error (_("Requires argument (process id to switch to)"));
-
-  if ((fp = find_fork_pid (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: process id %s"), args);
-
-  linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by fork id.  */
-static void
-fork_command (char *args, int from_tty)
-{
-  struct fork_info *fp;
-
-  if (!args || !*args)
-    error (_("Requires argument (fork id to switch to)"));
-
-  if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: fork id %s"), args);
-
-  linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by checkpoint id.  */
+/* Switch inferior process (checkpoint) context, by checkpoint id.  */
 static void
 restart_command (char *args, int from_tty)
 {
@@ -725,9 +668,8 @@ Tells gdb whether to detach the child of
   add_com ("checkpoint", class_obscure, checkpoint_command, _("\
 Fork a duplicate process (experimental)."));
 
-  /* Restart command: restore the context of a specified fork
-     process.  May be used for "program forks" as well as for
-     "debugger forks" (checkpoints).  */
+  /* Restart command: restore the context of a specified checkpoint
+     process.  */
 
   add_com ("restart", class_obscure, restart_command, _("\
 restart <n>: restore program context from a checkpoint.\n\
@@ -752,21 +694,4 @@ Detach from a fork/checkpoint (experimen
 
   add_info ("checkpoints", info_forks_command,
 	    _("IDs of currently known forks/checkpoints."));
-
-  /* Command aliases (let "fork" and "checkpoint" be used 
-     interchangeably).  */
-
-  add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &deletelist);
-  add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &detachlist);
-  add_info_alias ("forks", "checkpoints", 0);
-
-  /* "fork <n>" (by analogy to "thread <n>").  */
-  add_com ("fork", class_obscure, fork_command, _("\
-fork <n>: Switch between forked processes.\n\
-Argument 'n' is fork ID, as displayed by 'info forks'."));
-
-  /* "process <proc id>" as opposed to "fork <fork id>".  */
-  add_com ("process", class_obscure, process_command, _("\
-process <pid>: Switch between forked processes.\n\
-Argument 'pid' is process ID, as displayed by 'info forks' or 'shell ps'."));
 }
Index: src/gdb/linux-fork.h
===================================================================
--- src.orig/gdb/linux-fork.h	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/linux-fork.h	2009-05-30 23:03:52.000000000 +0100
@@ -20,13 +20,11 @@
 struct fork_info;
 extern struct fork_info *add_fork (pid_t);
 extern struct fork_info *find_fork_pid (pid_t);
-extern void fork_save_infrun_state (struct fork_info *, int);
 extern void linux_fork_killall (void);
 extern void linux_fork_mourn_inferior (void);
 extern void linux_fork_detach (char *, int);
-extern int  forks_exist_p (void);
-
-struct fork_info *fork_list;
+extern int forks_exist_p (void);
+extern int linux_fork_checkpointing_p (int);
 
 extern int detach_fork;
 
Index: src/gdb/cli/cli-cmds.c
===================================================================
--- src.orig/gdb/cli/cli-cmds.c	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/cli/cli-cmds.c	2009-05-30 23:03:52.000000000 +0100
@@ -124,6 +124,10 @@ struct cmd_list_element *deletelist;
 
 struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands. */
+
+struct cmd_list_element *killlist;
+
 /* Chain containing all defined "enable breakpoint" subcommands. */
 
 struct cmd_list_element *enablebreaklist;
Index: src/gdb/cli/cli-cmds.h
===================================================================
--- src.orig/gdb/cli/cli-cmds.h	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/cli/cli-cmds.h	2009-05-30 23:03:52.000000000 +0100
@@ -41,6 +41,10 @@ extern struct cmd_list_element *deleteli
 
 extern struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands.  */
+
+extern struct cmd_list_element *killlist;
+
 /* Chain containing all defined toggle subcommands. */
 
 extern struct cmd_list_element *togglelist;
Index: src/gdb/gdbcmd.h
===================================================================
--- src.orig/gdb/gdbcmd.h	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/gdbcmd.h	2009-05-30 23:03:52.000000000 +0100
@@ -52,6 +52,10 @@ extern struct cmd_list_element *deleteli
 
 extern struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands.  */
+
+extern struct cmd_list_element *killlist;
+
 /* Chain containing all defined toggle subcommands.  */
 
 extern struct cmd_list_element *togglelist;
Index: src/gdb/inferior.c
===================================================================
--- src.orig/gdb/inferior.c	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/inferior.c	2009-05-30 23:03:52.000000000 +0100
@@ -25,6 +25,7 @@
 #include "gdbthread.h"
 #include "ui-out.h"
 #include "observer.h"
+#include "gdbthread.h"
 
 void _initialize_inferiors (void);
 
@@ -322,12 +323,114 @@ print_inferior (struct ui_out *uiout, in
   do_cleanups (old_chain);
 }
 
+static void
+detach_inferior_command (char *args, int from_tty)
+{
+  int num = -1;
+  int pid;
+  struct thread_info *tp;
+
+  if (!args || !*args)
+    error (_("Requires argument (inferior id to detach)"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  tp = any_thread_of_process (pid);
+  if (!tp)
+    error (_("Inferior has no threads."));
+
+  switch_to_thread (tp->ptid);
+
+  detach_command (NULL, from_tty);
+}
+
+static void
+kill_inferior_command (char *args, int from_tty)
+{
+  int num = -1;
+  int pid;
+  struct thread_info *tp;
+
+  if (!args || !*args)
+    error (_("Requires argument (inferior id to kill)"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  tp = any_thread_of_process (pid);
+  if (!tp)
+    error (_("Inferior has no threads."));
+
+  switch_to_thread (tp->ptid);
+
+  target_kill ();
+
+  bfd_cache_close_all ();
+}
+
+static void
+inferior_command (char *args, int from_tty)
+{
+  int num, pid;
+
+  if (!have_inferiors ())
+    error (_("No inferiors"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  if (pid != ptid_get_pid (inferior_ptid))
+    {
+      struct thread_info *tp;
+
+      tp = any_thread_of_process (pid);
+      if (!tp)
+	error (_("Inferior has no threads."));
+
+      switch_to_thread (tp->ptid);
+    }
+
+  printf_filtered (_("[Switching to thread %d (%s)] "),
+		   pid_to_thread_id (inferior_ptid),
+		   target_pid_to_str (inferior_ptid));
+
+  if (is_running (inferior_ptid))
+    ui_out_text (uiout, "(running)\n");
+  else
+    {
+      ui_out_text (uiout, "\n");
+      print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+    }
+}
+
 /* Print information about currently known inferiors.  */
 
 static void
-info_inferiors_command (char *arg, int from_tty)
+info_inferiors_command (char *args, int from_tty)
 {
-  print_inferior (uiout, -1);
+  int requested = -1;
+
+  if (args && *args)
+    {
+      requested = parse_and_eval_long (args);
+      if (!valid_gdb_inferior_id (requested))
+	error (_("Inferior ID %d not known."), requested);
+    }
+
+  print_inferior (uiout, requested);
 }
 
 /* Print notices when new inferiors are created and die.  */
@@ -351,4 +454,17 @@ Show printing of inferior events (e.g., 
          NULL,
          show_print_inferior_events,
          &setprintlist, &showprintlist);
+
+  add_cmd ("inferior", class_run, detach_inferior_command, _("\
+Detach from inferior ID."),
+	   &detachlist);
+
+  add_cmd ("inferior", class_run, kill_inferior_command, _("\
+Kill inferior ID."),
+	   &killlist);
+
+  add_cmd ("inferior", class_run, inferior_command, _("\
+Use this command to switch between inferiors.\n\
+The new inferior ID must be currently known."),
+	   &cmdlist);
 }
Index: src/gdb/testsuite/gdb.base/multi-forks.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/multi-forks.exp	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/testsuite/gdb.base/multi-forks.exp	2009-05-30 23:03:52.000000000 +0100
@@ -140,6 +140,19 @@ gdb_test "print pids\[0\]==0 || pids\[1\
 # Now test with detach-on-fork off.
 #
 
+# detach-on-fork isn't implemented on hpux.
+#
+if {![istarget "*-*-linux*"]} then {
+    continue
+}
+
+# Start with a fresh gdb
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
 runto_main
 gdb_breakpoint $exit_bp_loc
 
@@ -152,60 +165,59 @@ gdb_test "set detach off" "" "set detach
 
 #
 # We will now run every fork up to the exit bp, 
-# eventually winding up with 16 forks.
+# eventually winding up with 16 inferiors.
 #
 
 for {set i 1} {$i <= 15} {incr i} {
   gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit $i"
-  gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork $i"
-  gdb_test "restart $i" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
-      "restart $i"
+  gdb_test "info inferior" " 5 .* 4 .* 3 .* 2 .*" "info inferior $i"
+  gdb_test "inferior $i + 1" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
+      "inferior $i"
 }
 
 gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit 16"
-gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork 16"
-gdb_test "restart 0" " main .*" "restart final"
+gdb_test "info inferiors" " 5 .* 4 .* 3 .* 2 .*" "info inferior 16"
+gdb_test "inferior 2" " main .*" "restart final"
 
 #
 # Now we should examine all the pids.
 #
 
 # 
-# Test detach fork
+# Test detach inferior
 # 
 
-# [assumes we're at #0]
-gdb_test "detach fork 1" "Detached .*" "Detach 1"
-gdb_test "detach fork 2" "Detached .*" "Detach 2"
-gdb_test "detach fork 3" "Detached .*" "Detach 3"
-gdb_test "detach fork 4" "Detached .*" "Detach 4"
+# [assumes we're at #1]
+gdb_test "detach inferior 2" "Detaching .*" "Detach 2"
+gdb_test "detach inferior 3" "Detaching .*" "Detach 3"
+gdb_test "detach inferior 4" "Detaching .*" "Detach 4"
+gdb_test "detach inferior 5" "Detaching .*" "Detach 5"
 
 # 
-# Test delete fork
-# 
+# Test kill inferior
+#
 
-gdb_test "delete fork 5" "" "Delete 5"
-gdb_test "info fork 5"   "No fork number 5." "Did delete 5"
-gdb_test "delete fork 6" "" "Delete 6"
-gdb_test "info fork 6"   "No fork number 6." "Did delete 6"
-gdb_test "delete fork 7" "" "Delete 7"
-gdb_test "info fork 7"   "No fork number 7." "Did delete 7"
-gdb_test "delete fork 8" "" "Delete 8"
-gdb_test "info fork 8"   "No fork number 8." "Did delete 8"
-gdb_test "delete fork 9" "" "Delete 9"
-gdb_test "info fork 9"   "No fork number 9." "Did delete 9"
-gdb_test "delete fork 10" "" "Delete 10"
-gdb_test "info fork 10"   "No fork number 10." "Did delete 10"
-gdb_test "delete fork 11" "" "Delete 11"
-gdb_test "info fork 11"   "No fork number 11." "Did delete 11"
-gdb_test "delete fork 12" "" "Delete 12"
-gdb_test "info fork 12"   "No fork number 12." "Did delete 12"
-gdb_test "delete fork 13" "" "Delete 13"
-gdb_test "info fork 13"   "No fork number 13." "Did delete 13"
-gdb_test "delete fork 14" "" "Delete 14"
-gdb_test "info fork 14"   "No fork number 14." "Did delete 14"
-gdb_test "delete fork 15" "" "Delete 15"
-gdb_test "info fork 15"   "No fork number 15." "Did delete 15"
+gdb_test "kill inferior 6" "" "Kill 6"
+gdb_test "info inferior 6" "Inferior ID 6 not known." "Did kill 6"
+gdb_test "kill inferior 7" "" "Kill 7"
+gdb_test "info inferior 7" "Inferior ID 7 not known." "Did kill 7"
+gdb_test "kill inferior 8" "" "Kill 8"
+gdb_test "info inferior 8" "Inferior ID 8 not known." "Did kill 8"
+gdb_test "kill inferior 9" "" "Kill 9"
+gdb_test "info inferior 9" "Inferior ID 9 not known." "Did kill 9"
+gdb_test "kill inferior 10" "" "Kill 10"
+gdb_test "info inferior 10" "Inferior ID 10 not known." "Did kill 10"
+gdb_test "kill inferior 11" "" "Kill 11"
+gdb_test "info inferior 11" "Inferior ID 11 not known." "Did kill 11"
+gdb_test "kill inferior 12" "" "Kill 12"
+gdb_test "info inferior 12" "Inferior ID 12 not known." "Did kill 12"
+gdb_test "kill inferior 13" "" "Kill 13"
+gdb_test "info inferior 13" "Inferior ID 13 not known." "Did kill 13"
+gdb_test "kill inferior 14" "" "Kill 14"
+gdb_test "info inferior 14" "Inferior ID 14 not known." "Did kill 14"
+gdb_test "kill inferior 15" "" "Kill 15"
+gdb_test "info inferior 15" "Inferior ID 15 not known." "Did kill 15"
+gdb_test "kill inferior 16" "" "Kill 16"
+gdb_test "info inferior 16" "Inferior ID 16 not known." "Did kill 16"
 
 return 0
-
Index: src/gdb/testsuite/gdb.base/ending-run.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/ending-run.exp	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/testsuite/gdb.base/ending-run.exp	2009-05-30 23:03:52.000000000 +0100
@@ -71,7 +71,7 @@ gdb_test "b ending-run.c:14" ".*Note.*al
 gdb_test "cle ending-run.c:14" \
 	".*Deleted breakpoint 5.*" "Cleared 2 by line"
 
-send_gdb "inf line ending-run.c:14\n"
+send_gdb "info line ending-run.c:14\n"
 gdb_expect {
     -re ".*address (0x\[0-9a-fA-F]*).*$gdb_prompt $" {
         set line_nine $expect_out(1,string)
Index: src/gdb/testsuite/gdb.base/help.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/help.exp	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/testsuite/gdb.base/help.exp	2009-05-30 23:03:52.000000000 +0100
@@ -300,7 +300,9 @@ gdb_test "help inspect" "Same as \"print
 # test help jump
 gdb_test "help jump" "Continue program being debugged at specified line or address\.\[\r\n\]+Give as argument either LINENUM or \[*\]+ADDR, where ADDR is an expression\[\r\n\]+for an address to start at\." "help jump"
 # test help kill
-gdb_test "help kill" "Kill execution of program being debugged\." "help kill"
+test_prefix_command_help "kill" {
+    "Kill execution of program being debugged\.\[\r\n\]+"
+}
 # test help list "l" abbreviation
 gdb_test "help l" "List specified function or line\.\[\r\n\]+With no argument, lists ten more lines after or around previous listing\.\[\r\n\]+\"list -\" lists the ten lines before a previous ten-line listing\.\[\r\n\]+One argument specifies a line, and ten lines are listed around that line\.\[\r\n\]+Two arguments with comma between specify starting and ending lines to list\.\[\r\n\]+Lines can be specified in these ways:\[\r\n\]+  LINENUM, to list around that line in current file,\[\r\n\]+  FILE:LINENUM, to list around that line in that file,\[\r\n\]+  FUNCTION, to list around beginning of that function,\[\r\n\]+  FILE:FUNCTION, to distinguish among like-named static functions\.\[\r\n\]+  \[*\]ADDRESS, to list around the line containing that address\.\[\r\n\]+With two args if one is empty it stands for ten lines away from the other arg\." "help list \"l\" abbreviation"
 # test help list
Index: src/gdb/infcmd.c
===================================================================
--- src.orig/gdb/infcmd.c	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/infcmd.c	2009-05-30 23:03:52.000000000 +0100
@@ -2626,8 +2626,9 @@ fully linked executable files and separa
 	       &showlist);
   set_cmd_completer (c, noop_completer);
 
-  add_com ("kill", class_run, kill_command,
-	   _("Kill execution of program being debugged."));
+  add_prefix_cmd ("kill", class_run, kill_command,
+		  _("Kill execution of program being debugged."),
+		  &killlist, "kill ", 0, &cmdlist);
 
   add_com ("attach", class_run, attach_command, _("\
 Attach to a process or file outside of GDB.\n\
Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/gdbthread.h	2009-05-30 23:03:52.000000000 +0100
@@ -238,6 +238,9 @@ struct thread_info *find_thread_id (int 
    returns the first thread in the list.  */
 struct thread_info *first_thread_of_process (int pid);
 
+/* Returns any thread of process PID.  */
+extern struct thread_info *any_thread_of_process (int pid);
+
 /* Change the ptid of thread OLD_PTID to NEW_PTID.  */
 void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
 
Index: src/gdb/thread.c
===================================================================
--- src.orig/gdb/thread.c	2009-05-30 23:03:28.000000000 +0100
+++ src/gdb/thread.c	2009-05-30 23:03:52.000000000 +0100
@@ -428,6 +428,18 @@ first_thread_of_process (int pid)
   return ret;
 }
 
+struct thread_info *
+any_thread_of_process (int pid)
+{
+  struct thread_info *tp;
+
+  for (tp = thread_list; tp; tp = tp->next)
+    if (ptid_get_pid (tp->ptid) == pid)
+      return tp;
+
+  return NULL;
+}
+
 /* Print a list of thread ids currently known, and the total number of
    threads. To be used from within catch_errors. */
 static int
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo	2009-05-30 23:03:25.000000000 +0100
+++ src/gdb/doc/gdb.texinfo	2009-05-30 23:03:52.000000000 +0100
@@ -2388,7 +2388,39 @@ To find out what inferiors exist at any 
 @kindex info inferiors
 @item info inferiors
 Print a list of all inferiors currently being managed by @value{GDBN}.
+@end table
+
+To switch focus between inferiors, use the @code{inferior} command:
+
+@table @code
+@kindex inferior @var{inferior-id}
+@item inferior @var{inferior-id}
+Make inferior number @var{inferior-id} the current inferior.  The
+argument @var{inferior-id} is the internal inferior number assigned by
+@value{GDBN}, as shown in the first field of the @samp{info inferiors}
+display.
+@end table
+
+To quit debugging one of the inferiors, you can either detach from it
+by using the @w{@code{detach inferior}} command (allowing it to run
+independently), or kill it using the @w{@code{kill inferior}} command:
+
+@table @code
+@kindex detach inferior @var{inferior-id}
+@item detach inferior @var{inferior-id}
+Detach from the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+
+@kindex kill inferior @var{inferior-id}
+@item kill inferior @var{inferior-id}
+Kill the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+@end table
+
+To be notified when inferiors are started or exit under @value{GDBN}'s
+control use @w{@code{set print inferior-events}}:
 
+@table @code
 @kindex set print inferior-events
 @cindex print messages on inferior start and exit
 @item set print inferior-events
@@ -2772,51 +2804,17 @@ is held suspended.  
 Show whether detach-on-fork mode is on/off.
 @end table
 
-If you choose to set @samp{detach-on-fork} mode off, then
-@value{GDBN} will retain control of all forked processes (including
-nested forks).  You can list the forked processes under the control of
-@value{GDBN} by using the @w{@code{info forks}} command, and switch
-from one fork to another by using the @w{@code{fork}} command.
-
-@table @code
-@kindex info forks
-@item info forks
-Print a list of all forked processes under the control of @value{GDBN}.
-The listing will include a fork id, a process id, and the current 
-position (program counter) of the process.
-
-@kindex fork @var{fork-id}
-@item fork @var{fork-id}
-Make fork number @var{fork-id} the current process.  The argument
-@var{fork-id} is the internal fork number assigned by @value{GDBN},
-as shown in the first field of the @samp{info forks} display.
-
-@kindex process @var{process-id}
-@item process @var{process-id}
-Make process number @var{process-id} the current process.  The
-argument @var{process-id} must be one that is listed in the output of
-@samp{info forks}.
-
-@end table
+If you choose to set @samp{detach-on-fork} mode off, then @value{GDBN}
+will retain control of all forked processes (including nested forks).
+You can list the forked processes under the control of @value{GDBN} by
+using the @w{@code{info inferiors}} command, and switch from one fork
+to another by using the @w{@code{inferior}} command (@pxref{Inferiors,
+,Debugging Multiple Inferiors}).
 
 To quit debugging one of the forked processes, you can either detach
-from it by using the @w{@code{detach fork}} command (allowing it to
-run independently), or delete (and kill) it using the
-@w{@code{delete fork}} command.
-
-@table @code
-@kindex detach fork @var{fork-id}
-@item detach fork @var{fork-id}
-Detach from the process identified by @value{GDBN} fork number
-@var{fork-id}, and remove it from the fork list.  The process will be
-allowed to run independently.
-
-@kindex delete fork @var{fork-id}
-@item delete fork @var{fork-id}
-Kill the process identified by @value{GDBN} fork number @var{fork-id},
-and remove it from the fork list.
-
-@end table
+from it by using the @w{@code{detach inferior}} command (allowing it
+to run independently), or kill it using the @w{@code{kill inferior}}
+command.  Again, @pxref{Inferiors, ,Debugging Multiple Inferiors},
 
 If you ask to debug a child process and a @code{vfork} is followed by an
 @code{exec}, @value{GDBN} executes the new target up to the first
@@ -2824,8 +2822,8 @@ breakpoint in the new target.  If you ha
 @code{main} in your original program, the breakpoint will also be set on
 the child process's @code{main}.
 
-When a child process is spawned by @code{vfork}, you cannot debug the
-child or parent until an @code{exec} call completes.
+On some systems, when a child process is spawned by @code{vfork}, you
+cannot debug the child or parent until an @code{exec} call completes.
 
 If you issue a @code{run} command to @value{GDBN} after an @code{exec}
 call executes, the new target restarts.  To restart the parent process,


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-05-30 23:13 Move the multi-forks support to the generic multi-inferiors support Pedro Alves
@ 2009-05-31 20:46 ` Tom Tromey
  2009-05-31 22:07   ` Pedro Alves
  2009-06-08 12:58 ` Pedro Alves
  2009-06-11 19:32 ` Michael Snyder
  2 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2009-05-31 20:46 UTC (permalink / raw)
  To: gdb-patches

>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:

Pedro> So, what I want to do is, to get rid of the linux-fork.c "multi-forks"
Pedro> support, replace it with generic "multi-inferior" support, but
Pedro> leave the checkpoint support there.  That is what the patch
Pedro> below does.

Your plan sounds good to me.  (I didn't read the patch yet, so no
comments on that.)

Pedro> In fact, I believe that the correct abstraction is for all
Pedro> these checkpoint forks to be associated with the same inferior
Pedro> id.

Pedro> | info forks      | delete, replaced by the             |
Pedro> |                 | generic "info inferiors"            |

I guess "info inferiors" would show all the checkpointed forks for a
given inferior?

BTW, I think the "info inferiors" output still needs cleaning up, a la
http://sourceware.org/ml/gdb-patches/2008-11/msg00666.html

Pedro> What do you think?

Do it.

Tom


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-05-31 20:46 ` Tom Tromey
@ 2009-05-31 22:07   ` Pedro Alves
  2009-05-31 22:23     ` Doug Evans
  2009-06-01 15:58     ` Tom Tromey
  0 siblings, 2 replies; 21+ messages in thread
From: Pedro Alves @ 2009-05-31 22:07 UTC (permalink / raw)
  To: gdb-patches, tromey

On Sunday 31 May 2009 21:45:33, Tom Tromey wrote:

> Pedro> So, what I want to do is, to get rid of the linux-fork.c "multi-forks"
> Pedro> support, replace it with generic "multi-inferior" support, but
> Pedro> leave the checkpoint support there.  That is what the patch
> Pedro> below does.
> 
> Your plan sounds good to me.  (I didn't read the patch yet, so no
> comments on that.)

Great.  Thanks for commenting.

> Pedro> In fact, I believe that the correct abstraction is for all
> Pedro> these checkpoint forks to be associated with the same inferior
> Pedro> id.
> 
> Pedro> | info forks      | delete, replaced by the             |
> Pedro> |                 | generic "info inferiors"            |
> 
> I guess "info inferiors" would show all the checkpointed forks for a
> given inferior?

That's one option, sure.  I haven't given much attention to user
interface details of checkpoints.  Another idea we be to just flag that
there are checkpoints available, and then the user can do the usual
"info checkpoints" to see them.  This avoids having a too crowded
"info inferiors" output.  Anyway, there's one issue that needs fixing
if you want that.  The current checkpoints implementation is all private
and pretty much hacked into linux-nat.c.  The core of GDB has really has
no clue of what a checkpoint is, hence neither does the "info inferiors"
part of gdb.

> BTW, I think the "info inferiors" output still needs cleaning up, a la
> http://sourceware.org/ml/gdb-patches/2008-11/msg00666.html

If you look at head's "info inferiors" output, it's not about cleaning
up, it's about cooking something.  :-)  When some multi-exec design
settles on head (the design/implementation I'm pushing has diverged too
much from what's in that branch; please consider that branch
closed, as it had serious limitations and design "problems"), we'll
certainly have more fields to output on "info inferiors".

> 
> Pedro> What do you think?
> 
> Do it.
> 

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors   support.
  2009-05-31 22:07   ` Pedro Alves
@ 2009-05-31 22:23     ` Doug Evans
  2009-06-01 15:58     ` Tom Tromey
  1 sibling, 0 replies; 21+ messages in thread
From: Doug Evans @ 2009-05-31 22:23 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, tromey

On Sun, May 31, 2009 at 3:08 PM, Pedro Alves <pedro@codesourcery.com> wrote:
>> I guess "info inferiors" would show all the checkpointed forks for a
>> given inferior?
>
> That's one option, sure.  I haven't given much attention to user
> interface details of checkpoints.  Another idea we be to just flag that
> there are checkpoints available, and then the user can do the usual
> "info checkpoints" to see them.  This avoids having a too crowded
> "info inferiors" output.

Other targets (e.g. simulators) may have checkpoints that have nothing
to do with multiple inferiors, so fwiw I think they should be kept
separate.


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-05-31 22:07   ` Pedro Alves
  2009-05-31 22:23     ` Doug Evans
@ 2009-06-01 15:58     ` Tom Tromey
  2009-06-06  0:09       ` Tom Tromey
  1 sibling, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2009-06-01 15:58 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:

Tom> I guess "info inferiors" would show all the checkpointed forks for a
Tom> given inferior?

Pedro> That's one option, sure.  I haven't given much attention to user
Pedro> interface details of checkpoints.  Another idea we be to just flag that
Pedro> there are checkpoints available, and then the user can do the usual
Pedro> "info checkpoints" to see them.

I completely forgot about 'info checkpoints'.  So, ignore all that,
sorry.

Tom> BTW, I think the "info inferiors" output still needs cleaning up, a la
Tom> http://sourceware.org/ml/gdb-patches/2008-11/msg00666.html

Pedro> If you look at head's "info inferiors" output, it's not about cleaning
Pedro> up, it's about cooking something.  :-)

Actually I hadn't looked at the code, I just tried it and saw that
there were still no column headings.  I'll try to update that old
patch soon.

Pedro> [...] please consider that branch closed, as it had serious
Pedro> limitations and design "problems")

Thanks for the heads up.

Tom


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-01 15:58     ` Tom Tromey
@ 2009-06-06  0:09       ` Tom Tromey
  2009-06-06 16:06         ` Pedro Alves
  0 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2009-06-06  0:09 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:

Pedro> If you look at head's "info inferiors" output, it's not about cleaning
Pedro> up, it's about cooking something.  :-)

Tom> Actually I hadn't looked at the code, I just tried it and saw that
Tom> there were still no column headings.  I'll try to update that old
Tom> patch soon.

How about this?  It adds the column headings and prints something when
there are no inferiors.

Built & regtested on x86-64 (compile farm).

Tom

2009-06-05  Tom Tromey  <tromey@redhat.com>

	* inferior.c (print_inferior): Make a table, not a list.  Emit
	table headers.

diff --git a/gdb/inferior.c b/gdb/inferior.c
index e060a1b..e74c7a4 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -294,8 +294,31 @@ print_inferior (struct ui_out *uiout, int requested_inferior)
 {
   struct inferior *inf;
   struct cleanup *old_chain;
+  int inf_count = 0;
 
-  old_chain = make_cleanup_ui_out_list_begin_end (uiout, "inferiors");
+  /* Compute number of inferiors we will print.  */
+  for (inf = inferior_list; inf; inf = inf->next)
+    {
+      struct cleanup *chain2;
+
+      if (requested_inferior != -1 && inf->num != requested_inferior)
+	continue;
+
+      ++inf_count;
+    }
+
+  if (inf_count == 0)
+    {
+      ui_out_message (uiout, 0, "No inferiors.\n");
+      return;
+    }
+
+  old_chain = make_cleanup_ui_out_table_begin_end (uiout, 3, inf_count,
+						   "inferiors");
+  ui_out_table_header (uiout, 3, ui_right, "current", "Cur");
+  ui_out_table_header (uiout, 4, ui_right, "id", "Id");
+  ui_out_table_header (uiout, 7, ui_right, "target-id", "PID");
+  ui_out_table_body (uiout);
 
   for (inf = inferior_list; inf; inf = inf->next)
     {
@@ -307,12 +330,11 @@ print_inferior (struct ui_out *uiout, int requested_inferior)
       chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
 
       if (inf->pid == ptid_get_pid (inferior_ptid))
-	ui_out_text (uiout, "* ");
+	ui_out_field_string (uiout, "current", "*");
       else
-	ui_out_text (uiout, "  ");
+	ui_out_field_skip (uiout, "current");
 
       ui_out_field_int (uiout, "id", inf->num);
-      ui_out_text (uiout, " ");
       ui_out_field_int (uiout, "target-id", inf->pid);
 
       ui_out_text (uiout, "\n");


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-06  0:09       ` Tom Tromey
@ 2009-06-06 16:06         ` Pedro Alves
  2009-06-08  1:34           ` Marc Khouzam
  2009-06-08 22:39           ` Tom Tromey
  0 siblings, 2 replies; 21+ messages in thread
From: Pedro Alves @ 2009-06-06 16:06 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Saturday 06 June 2009 01:07:29, Tom Tromey wrote:

> How about this?  It adds the column headings and prints something when
> there are no inferiors.

This is fine with me, thanks, but,

are you planning on doing something similar to "info threads" output?
IMO, both these commands should be consistent.  In that case, you
may have an issue with MI, as it calls print_thread_info --- I don't
know if using a table instead of a list changes MI output.

(I was a bit surprised to (re-)find that MI doesn't use
print_inferior)

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 21+ messages in thread

* RE: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-06 16:06         ` Pedro Alves
@ 2009-06-08  1:34           ` Marc Khouzam
  2009-06-08 22:43             ` Tom Tromey
  2009-06-08 22:39           ` Tom Tromey
  1 sibling, 1 reply; 21+ messages in thread
From: Marc Khouzam @ 2009-06-08  1:34 UTC (permalink / raw)
  To: Pedro Alves, Tom Tromey; +Cc: gdb-patches

 

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org 
> [mailto:gdb-patches-owner@sourceware.org] On Behalf Of Pedro Alves
> Sent: June-06-09 12:07 PM
> To: Tom Tromey
> Cc: gdb-patches@sourceware.org
> Subject: Re: Move the multi-forks support to the generic 
> multi-inferiors support.
> 
> On Saturday 06 June 2009 01:07:29, Tom Tromey wrote:
> 
> > How about this?  It adds the column headings and prints 
> something when
> > there are no inferiors.
> 
> This is fine with me, thanks, but,
> 
> are you planning on doing something similar to "info threads" output?
> IMO, both these commands should be consistent.  In that case, you
> may have an issue with MI, as it calls print_thread_info --- I don't
> know if using a table instead of a list changes MI output.

Just a note that up to GDB 6.8, we used the output of "info threads"
in the DSF-GDB Eclipse frontend.  With GDB 7.0 we moved to
"-thread-info"
So, I'm not affected by a change of output, but 
I'm saying this to point out that some frontend may still be using
it and may break when moving to GDB 7.0 if the format changes.
I think CDI-GDB (the older Eclipse GDB frontend) is in that 
category.
I'm not sure if you enforce CLI output backwards compatibility...

Marc

> 
> (I was a bit surprised to (re-)find that MI doesn't use
> print_inferior)
> 
> -- 
> Pedro Alves
> 


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-05-30 23:13 Move the multi-forks support to the generic multi-inferiors support Pedro Alves
  2009-05-31 20:46 ` Tom Tromey
@ 2009-06-08 12:58 ` Pedro Alves
  2009-06-11 19:32 ` Michael Snyder
  2 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2009-06-08 12:58 UTC (permalink / raw)
  To: gdb-patches; +Cc: Eli Zaretskii, Michael Snyder

I didn't hear objections to this so far, and I got one positive
review.

Here's below an updated patch that includes a NEWS entry.  I've fixed a
couple of minor issues with the docs (no need for @w for a single
word, AFAIK, and, replaced a spurious ',' by a proper '.'.  I've also
added a couple of comments more to linux-nat.c, mentioning why
to not report TARGET_WAITKIND_FORK to infrun when doing a
checkpoint (this actually fixes two current bugs).  Ok?

On Sunday 31 May 2009 00:13:38, Pedro Alves wrote:

> I was mentioning this earlier in the other thread that adds a new command
> to allow resuming all threads of all processes, or just of the
> current process.  I may have added confusion because I was talking
> about this change, while nobody else was seeing it, naturally.  So,
> I'm posting it now, in hopes of making things clearer.
> 
> The current "multi-forks" support, as implemented by linux-fork.c,
> serves as backend for two things.  Staying attached to parent and
> child forks simultaneously, and, for checkpoint support.  The former,
> is a limited implementation of multi-process/multi-exec.  If one of
> the child or parent execs, all hell breaks loose.  It doesn't really
> support multi-threaded inferiors.  It bypasses linux-nat.c for killing
> and detaching, leaving state inconsistent, and possibly mis-kills or
> mis-detaches, etc., etc.  This is what I want to fix.
> 
> The checkpoints support however, is a different thing altogether.
> The fact that it is implemented using forks for doing the grunt work,
> is an implementation detail.  In fact, I believe that the correct
> abstraction is for all these checkpoint forks to be associated with
> the same inferior id.  When switching between checkpoints, the process
> switching should be made transparent to most of GDB and the user (as
> already is as possible).  I'm not intersted at this point in improving
> the checkpoints implementation to support multi-threaded apps, or
> to make it behave if one of the checkpoints does an exec.  However,
> I don't want to break it more than it is currently --- it's useful
> as is.
> 
> So, what I want to do is, to get rid of the linux-fork.c "multi-forks"
> support, replace it with generic "multi-inferior" support, but
> leave the checkpoint support there.  That is what the patch
> below does.
> 
> With this in place, it is still possible to debug multiple
> forks at once, under the same GDB session.   With a few more patches,
> and with the patches I've been posting applied, it will be possible
> to follow different execs for each of these forks, all under
> the same GDB.  This will allow, for example, the
> mythical "gdb /usr/bin/make" -> follow all gcc, ld, as, sed, whatnot
> invocations make does until gcc crashes, and catch that SIGSEGV.  So,
> as you see, multi-forks is a subset of multi-process/multi-executable
> debugging.
> 
> While doing this, there are few a commands that don't make sense
> to have anymore in a generalized multi-inferior framework.  E.g.,
> "info forks" stops making sense if one of the forks has done
> an execl...
> 
> So, to do this, I'm proposing a few command replacements, similar
> to what I've described here:
> 
>  http://sourceware.org/gdb/wiki/MultiProcess
> 
> |-----------------+-------------------------------------|
> | Current command | New command                         |
> |-----------------+-------------------------------------|
> | delete fork NUM | kill inferior NUM                   |
> |-----------------+-------------------------------------|
> | detach fork NUM | detach inferior NUM                 |
> |-----------------+-------------------------------------|
> | restart NUM     | keep, but apply only to checkpoints |
> |-----------------+-------------------------------------|
> | checkpoint      | keep, adds a new checkpoint.        |
> |                 | make followfork child/parent have   |
> |                 | no effect on this command (always   |
> |                 | follow the parent)                  |
> |-----------------+-------------------------------------|
> | info forks      | delete, replaced by the             |
> |                 | generic "info inferiors"            |
> |-----------------+-------------------------------------|
> | fork NUM        | delete, replaced by the new generic |
> |                 | command 'inferior NUM'              |
> |-----------------+-------------------------------------|
> 
> Luckily, the multi-forks/checkpoints commands are marked
> experimental in GDB.
> 
> Note that this even fixes a couple of bugs.  E.g., doing
> a checkpoint with "set follow-fork-mode child" was broken
> currently.
> 
> This applies on top of this other patch,
> 
>  http://sourceware.org/ml/gdb-patches/2009-05/msg00650.html
> 
> ... and has no regressions.
> 
> I can now make the linux-nat.c target report multi-process
> support to the core of GDB with this (in fact, I have to,
> so that it only tries to resume threads of a single-inferior,
> since that support is only enabled if the target reports support
> for multi-process).
> 
> What do you think?  I know multi-inferior/multi-exec/multi-fork
> debugging needs better examples in the manual, however, we still
> don't have the necessary support to show that off yet.  I'm trying
> to push things incrementally without breaking much...
> 
> And, I'll need to write a NEWS entry in case this is good
> to go, I know.  ;-)
> 
> Eli, I hope this clears a few things up.  Michael, CCing you, since
> these checkpoints stuff is your baby.

-- 
Pedro Alves

gdb/
2009-06-08  Pedro Alves  <pedro@codesourcery.com>

	* linux-nat.c (linux_child_follow_fork): If we're staying attached
	to the child process, enable event reporting on it.  Don't handle
	checkpoints here.  Instead, add the child fork to the lwp thread
	and inferior lists without clobbering the previous inferior.  Let
	the thread_db layer learn about a new child process, even if
	following the parent.
	(linux_nat_switch_fork): Delete lwps of the current inferior only,
	instead of clearing the whole list.  Use thread_change_ptid to
	give the core the illusion the new checkpoint is still the same
	inferior.  Clear the register cache.
	(linux_handle_extended_wait): Handle checkpoints here.
	(linux_multi_process): Turn on.
	* linux-fork.c (struct fork_info) <pc>: Remove field.
	(init_fork_list): Do not delete the checkpoint from the inferior
	list (it is not there).
	(fork_load_infrun_state): Don't switch inferior_ptid here.  Pass
	the new checkpoint's ptid to linux_nat_switch_fork.
	(fork_save_infrun_state): Make static.  Don't stop the pc field of
	fork_info, it's gone.
	(linux_fork_mourn_inferior): Don't delete the checkpoint from the
	inferior list, it's not there.
	(linux_fork_detach): Ditto.
	(delete_fork_command): Replace mention of fork/checkpoint by
	checkpoint only.
	(detach_fork_command): Likewise.  Don't delete the checkpoint from
	the inferior list.
	(info_forks_command): Adjust.
	(restore_detach_fork): Delete.
	(checkpointing_pid): New.
	(linux_fork_checkpointing_p): New.
	(save_detach_fork): Delete.
	(checkpoint_command): Delete temp_detach_fork.  Don't remove
	breakpoints, that's a nop.  Store the pid of the process we're
	checkpointing, and use make_cleanup_restore_integer to restore it.
	Don't reinsert breakpoints here.
	(process_command, fork_command): Delete.
	(restart_command): Update comments to only mention checkpoints,
	not forks.
	(_initialize_linux_fork): Delete "fork", "process", "info forks"
	commands.
	* linux-fork.h (fork_save_infrun_state, fork_list): Delete
	declarations.
	(linux_fork_checkpointing_p): Declare.
	* cli/cli-cmds.c (killlist): New.
	* cli/cli-cmds.h (killlist): Declare.
	* gdbcmd.h (killlist): Declare.
	* inferior.c: Include "gdbthread.h".
	(detach_inferior_command, kill_inferior_command)
	(inferior_command): New.
	(info_inferiors_command): Allow specifying a specific inferior id.
	(_initialize_inferiors): Register "inferior", "kill inferior" and
	"detach inferior" commands.
	* infcmd.c (_initialize_infcmd): Make "kill" a prefix command.
	* gdbthread.h (any_thread_of_process): Declare.
	* thread.c (any_thread_of_process): New.

	* NEWS: Mention 'info inferiors', 'inferior', 'detach inferior'
	and 'kill inferior' as new commands.
	(Removed commands): New section, mentioning that 'info forks',
	'fork', 'process', 'delete fork' and 'detach fork' are now gone.

gdb/testsuite/
2009-06-08  Pedro Alves  <pedro@codesourcery.com>

	* gdb.base/multi-forks.exp: Only run detach-on-fork tests on
	linux.  Adjust to use "inferior", "info inferiors", "detach
	inferior" and "kill inferior" instead of "restart", "info fork",
	"detach fork" and "delete fork".
	* gdb.base/ending-run.exp: Spell out "info".
	* gdb.base/help.exp: Adjust to use test_prefix_command_help for
	the "kill" command.

gdb/doc/
2009-06-08  Pedro Alves  <pedro@codesourcery.com>

	* gdb.texinfo (Debugging multiple inferiors): Document the
	"inferior", "detach inferior" and "kill inferior" commands.
	(Debugging Programs with Multiple Processes): Adjust to mention
	generic "inferior" commands.  Delete mention of "detach fork" and
	"delete fork".  Cross reference to "Debugging multiple inferiors"
	section.

	
---
 gdb/NEWS                               |   44 +++++++++++
 gdb/cli/cli-cmds.c                     |    4 +
 gdb/cli/cli-cmds.h                     |    4 +
 gdb/doc/gdb.texinfo                    |   88 +++++++++++-----------
 gdb/gdbcmd.h                           |    4 +
 gdb/gdbthread.h                        |    3 
 gdb/infcmd.c                           |    5 -
 gdb/inferior.c                         |  120 ++++++++++++++++++++++++++++++
 gdb/linux-fork.c                       |  128 ++++++---------------------------
 gdb/linux-fork.h                       |    6 -
 gdb/linux-nat.c                        |  115 +++++++++++++++++++++--------
 gdb/testsuite/gdb.base/ending-run.exp  |    2 
 gdb/testsuite/gdb.base/help.exp        |    4 -
 gdb/testsuite/gdb.base/multi-forks.exp |   86 ++++++++++++----------
 gdb/thread.c                           |   12 +++
 15 files changed, 401 insertions(+), 224 deletions(-)

Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/linux-nat.c	2009-06-08 13:22:47.000000000 +0100
@@ -338,6 +338,12 @@ static int stop_callback (struct lwp_inf
 
 static void block_child_signals (sigset_t *prev_mask);
 static void restore_child_signals_mask (sigset_t *prev_mask);
+
+struct lwp_info;
+static struct lwp_info *add_lwp (ptid_t ptid);
+static void purge_lwp_list (int pid);
+static struct lwp_info *find_lwp_pid (ptid_t ptid);
+
 \f
 /* Trivial list manipulation functions to keep track of a list of
    new stopped processes.  */
@@ -587,6 +593,9 @@ linux_child_follow_fork (struct target_o
     parent_pid = ptid_get_pid (inferior_ptid);
   child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
 
+  if (!detach_fork)
+    linux_enable_event_reporting (pid_to_ptid (child_pid));
+
   if (! follow_child)
     {
       /* We're already attached to the parent, by default. */
@@ -617,8 +626,9 @@ linux_child_follow_fork (struct target_o
 	}
       else
 	{
-	  struct fork_info *fp;
 	  struct inferior *parent_inf, *child_inf;
+	  struct lwp_info *lp;
+	  struct cleanup *old_chain;
 
 	  /* Add process to GDB's tables.  */
 	  child_inf = add_inferior (child_pid);
@@ -627,11 +637,16 @@ linux_child_follow_fork (struct target_o
 	  child_inf->attach_flag = parent_inf->attach_flag;
 	  copy_terminal_info (child_inf, parent_inf);
 
-	  /* Retain child fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (child_pid);
-	  if (!fp)
-	    fp = add_fork (child_pid);
-	  fork_save_infrun_state (fp, 0);
+	  old_chain = save_inferior_ptid ();
+
+	  inferior_ptid = ptid_build (child_pid, child_pid, 0);
+	  add_thread (inferior_ptid);
+	  lp = add_lwp (inferior_ptid);
+	  lp->stopped = 1;
+
+	  check_for_thread_db ();
+
+	  do_cleanups (old_chain);
 	}
 
       if (has_vforked)
@@ -692,6 +707,7 @@ linux_child_follow_fork (struct target_o
     {
       struct thread_info *tp;
       struct inferior *parent_inf, *child_inf;
+      struct lwp_info *lp;
 
       /* Before detaching from the parent, remove all breakpoints from it. */
       remove_breakpoints ();
@@ -733,30 +749,33 @@ linux_child_follow_fork (struct target_o
 
       if (has_vforked)
 	{
+	  struct lwp_info *parent_lwp;
+
 	  linux_parent_pid = parent_pid;
+
+	  /* Get rid of the inferior on the core side as well.  */
+	  inferior_ptid = null_ptid;
 	  detach_inferior (parent_pid);
-	}
-      else if (!detach_fork)
-	{
-	  struct fork_info *fp;
-	  /* Retain parent fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (parent_pid);
-	  if (!fp)
-	    fp = add_fork (parent_pid);
-	  fork_save_infrun_state (fp, 0);
 
-	  /* Also add an entry for the child fork.  */
-	  fp = find_fork_pid (child_pid);
-	  if (!fp)
-	    fp = add_fork (child_pid);
-	  fork_save_infrun_state (fp, 0);
+	  /* Also get rid of all its lwps.  We will detach from this
+	     inferior soon-ish, but, we will still get an exit event
+	     reported through waitpid when it exits.  If we didn't get
+	     rid of the lwps from our list, we would end up reporting
+	     the inferior exit to the core, which would then try to
+	     mourn a non-existing (from the core's perspective)
+	     inferior.  */
+	  parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
+	  purge_lwp_list (GET_PID (parent_lwp->ptid));
+	  linux_parent_pid = parent_pid;
 	}
-      else
+      else if (detach_fork)
 	target_detach (NULL, 0);
 
       inferior_ptid = ptid_build (child_pid, child_pid, 0);
+      add_thread (inferior_ptid);
+      lp = add_lwp (inferior_ptid);
+      lp->stopped = 1;
 
-      linux_nat_switch_fork (inferior_ptid);
       check_for_thread_db ();
     }
 
@@ -1073,22 +1092,30 @@ iterate_over_lwps (ptid_t filter,
   return NULL;
 }
 
-/* Update our internal state when changing from one fork (checkpoint,
-   et cetera) to another indicated by NEW_PTID.  We can only switch
-   single-threaded applications, so we only create one new LWP, and
-   the previous list is discarded.  */
+/* Update our internal state when changing from one checkpoint to
+   another indicated by NEW_PTID.  We can only switch single-threaded
+   applications, so we only create one new LWP, and the previous list
+   is discarded.  */
 
 void
 linux_nat_switch_fork (ptid_t new_ptid)
 {
   struct lwp_info *lp;
 
-  init_lwp_list ();
+  purge_lwp_list (GET_PID (inferior_ptid));
+
   lp = add_lwp (new_ptid);
   lp->stopped = 1;
 
-  init_thread_list ();
-  add_thread_silent (new_ptid);
+  /* This changes the thread's ptid while preserving the gdb thread
+     num.  Also changes the inferior pid, while preserving the
+     inferior num.  */
+  thread_change_ptid (inferior_ptid, new_ptid);
+
+  /* We've just told GDB core that the thread changed target id, but,
+     in fact, it really is a different thread, with different register
+     contents.  */
+  registers_changed ();
 }
 
 /* Handle the exit of a single thread LP.  */
@@ -1815,6 +1842,34 @@ linux_handle_extended_wait (struct lwp_i
 
       ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
 
+      if (event == PTRACE_EVENT_FORK
+	  && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+	{
+	  struct fork_info *fp;
+
+	  /* Handle checkpointing by linux-fork.c here as a special
+	     case.  We don't want the follow-fork-mode or 'catch fork'
+	     to interfere with this.  */
+
+	  /* This won't actually modify the breakpoint list, but will
+	     physically remove the breakpoints from the child.  */
+	  detach_breakpoints (new_pid);
+
+	  /* Retain child fork in ptrace (stopped) state.  */
+	  fp = find_fork_pid (new_pid);
+	  if (!fp)
+	    fp = add_fork (new_pid);
+
+	  /* Report as spurious, so that infrun doesn't want to follow
+	     this fork.  We're actually doing an infcall in
+	     linux-fork.c.  */
+	  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+	  linux_enable_event_reporting (pid_to_ptid (new_pid));
+
+	  /* Report the stop to the core.  */
+	  return 0;
+	}
+
       if (event == PTRACE_EVENT_FORK)
 	ourstatus->kind = TARGET_WAITKIND_FORKED;
       else if (event == PTRACE_EVENT_VFORK)
@@ -4295,7 +4350,7 @@ linux_nat_supports_non_stop (void)
 /* True if we want to support multi-process.  To be removed when GDB
    supports multi-exec.  */
 
-int linux_multi_process = 0;
+int linux_multi_process = 1;
 
 static int
 linux_nat_supports_multi_process (void)
Index: src/gdb/linux-fork.c
===================================================================
--- src.orig/gdb/linux-fork.c	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/linux-fork.c	2009-06-08 13:45:56.000000000 +0100
@@ -52,7 +52,6 @@ struct fork_info
   struct regcache *savedregs;	/* Convenient for info fork, saves 
 				   having to actually switch contexts.  */
   int clobber_regs;		/* True if we should restore saved regs.  */
-  ULONGEST pc;			/* PC for info fork.  */
   off_t *filepos;		/* Set of open file descriptors' offsets.  */
   int maxfd;
 };
@@ -210,7 +209,6 @@ init_fork_list (void)
   for (fp = fork_list; fp; fp = fpnext)
     {
       fpnext = fp->next;
-      delete_inferior (ptid_get_pid (fp->ptid));
       free_fork (fp);
     }
 
@@ -240,9 +238,7 @@ fork_load_infrun_state (struct fork_info
   extern void nullify_last_target_wait_ptid ();
   int i;
 
-  inferior_ptid = fp->ptid;
-
-  linux_nat_switch_fork (inferior_ptid);
+  linux_nat_switch_fork (fp->ptid);
 
   if (fp->savedregs && fp->clobber_regs)
     regcache_cpy (get_current_regcache (), fp->savedregs);
@@ -268,7 +264,7 @@ fork_load_infrun_state (struct fork_info
 /* Save infrun state for the fork PTID.
    Exported for use by linux child_follow_fork.  */
 
-extern void
+static void
 fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
 {
   char path[MAXPATHLEN];
@@ -280,7 +276,6 @@ fork_save_infrun_state (struct fork_info
 
   fp->savedregs = regcache_dup (get_current_regcache ());
   fp->clobber_regs = clobber_regs;
-  fp->pc = regcache_read_pc (get_current_regcache ());
 
   if (clobber_regs)
     {
@@ -370,8 +365,6 @@ linux_fork_mourn_inferior (void)
      We need to delete that one from the fork_list, and switch
      to the next available fork.  */
   delete_fork (inferior_ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (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
@@ -402,8 +395,6 @@ linux_fork_detach (char *args, int from_
     error (_("Unable to detach %s"), target_pid_to_str (inferior_ptid));
 
   delete_fork (inferior_ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (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
@@ -429,14 +420,14 @@ delete_fork_command (char *args, int fro
   ptid_t ptid;
 
   if (!args || !*args)
-    error (_("Requires argument (fork/checkpoint id to delete)"));
+    error (_("Requires argument (checkpoint id to delete)"));
 
   ptid = fork_id_to_ptid (parse_and_eval_long (args));
   if (ptid_equal (ptid, minus_one_ptid))
-    error (_("No such fork/checkpoint id, %s"), args);
+    error (_("No such checkpoint id, %s"), args);
 
   if (ptid_equal (ptid, inferior_ptid))
-    error (_("Please switch to another fork/checkpoint before deleting the current one"));
+    error (_("Please switch to another checkpoint before deleting the current one"));
 
   if (ptrace (PTRACE_KILL, PIDGET (ptid), 0, 0))
     error (_("Unable to kill pid %s"), target_pid_to_str (ptid));
@@ -445,8 +436,6 @@ delete_fork_command (char *args, int fro
     printf_filtered (_("Killed %s\n"), target_pid_to_str (ptid));
 
   delete_fork (ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (ptid));
 }
 
 static void
@@ -455,14 +444,15 @@ detach_fork_command (char *args, int fro
   ptid_t ptid;
 
   if (!args || !*args)
-    error (_("Requires argument (fork id to detach)"));
+    error (_("Requires argument (checkpoint id to detach)"));
 
   ptid = fork_id_to_ptid (parse_and_eval_long (args));
   if (ptid_equal (ptid, minus_one_ptid))
-    error (_("No such fork id, %s"), args);
+    error (_("No such checkpoint id, %s"), args);
 
   if (ptid_equal (ptid, inferior_ptid))
-    error (_("Please switch to another fork before detaching the current one"));
+    error (_("\
+Please switch to another checkpoint before detaching the current one"));
 
   if (ptrace (PTRACE_DETACH, PIDGET (ptid), 0, 0))
     error (_("Unable to detach %s"), target_pid_to_str (ptid));
@@ -471,8 +461,6 @@ detach_fork_command (char *args, int fro
     printf_filtered (_("Detached %s\n"), target_pid_to_str (ptid));
 
   delete_fork (ptid);
-  /* Delete process from GDB's process table.  */
-  detach_inferior (ptid_get_pid (ptid));
 }
 
 /* Print information about currently known forks.  */
@@ -506,7 +494,7 @@ info_forks_command (char *arg, int from_
       else
 	{
 	  printf_filtered ("  ");
-	  pc = fp->pc;
+	  pc = regcache_read_pc (fp->savedregs);
 	}
       printf_filtered ("%d %s", fp->num, target_pid_to_str (fp->ptid));
       if (fp->num == 0)
@@ -546,25 +534,13 @@ info_forks_command (char *arg, int from_
     }
 }
 
-/* Save/restore mode variable 'detach_fork':
-   We need to temporarily take over this mode variable, while
-   preserving the user-specified state, and make sure that it 
-   gets restored in case of error.
+/* The PID of the process we're checkpointing.  */
+static int checkpointing_pid = 0;
 
-   The int pointer that we use comes from the caller, so we can
-   be called more than once (even though currently we don't need to).  */
-
-static void 
-restore_detach_fork (void *arg)
+int
+linux_fork_checkpointing_p (int pid)
 {
-  detach_fork = *(int *) arg;
-}
-
-static struct cleanup *
-save_detach_fork (int *saved_val)
-{
-  *saved_val = detach_fork;
-  return make_cleanup (restore_detach_fork, (void *) saved_val);
+  return (checkpointing_pid == pid);
 }
 
 static void
@@ -579,12 +555,6 @@ checkpoint_command (char *args, int from
   pid_t retpid;
   struct cleanup *old_chain;
   long i;
-  /* Make this temp var static, 'cause it's used in the error context.  */
-  static int temp_detach_fork;
-
-  /* Remove breakpoints, so that they are not inserted
-     in the forked process.  */
-  remove_breakpoints ();
 
   /* Make the inferior fork, record its (and gdb's) state.  */
 
@@ -598,8 +568,11 @@ checkpoint_command (char *args, int from
 
   gdbarch = get_objfile_arch (fork_objf);
   ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
-  old_chain = save_detach_fork (&temp_detach_fork);
-  detach_fork = 0;
+
+  /* Tell linux-nat.c that we're checkpointing this inferior.  */
+  old_chain = make_cleanup_restore_integer (&checkpointing_pid);
+  checkpointing_pid = PIDGET (inferior_ptid);
+
   ret = call_function_by_hand (fork_fn, 0, &ret);
   do_cleanups (old_chain);
   if (!ret)	/* Probably can't happen.  */
@@ -627,7 +600,6 @@ checkpoint_command (char *args, int from
   if (!fp)
     error (_("Failed to find new fork"));
   fork_save_infrun_state (fp, 1);
-  insert_breakpoints ();
 }
 
 static void
@@ -654,37 +626,7 @@ linux_fork_context (struct fork_info *ne
   print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
 }
 
-/* Switch inferior process (fork) context, by process id.  */
-static void
-process_command (char *args, int from_tty)
-{
-  struct fork_info *fp;
-
-  if (!args || !*args)
-    error (_("Requires argument (process id to switch to)"));
-
-  if ((fp = find_fork_pid (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: process id %s"), args);
-
-  linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by fork id.  */
-static void
-fork_command (char *args, int from_tty)
-{
-  struct fork_info *fp;
-
-  if (!args || !*args)
-    error (_("Requires argument (fork id to switch to)"));
-
-  if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: fork id %s"), args);
-
-  linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by checkpoint id.  */
+/* Switch inferior process (checkpoint) context, by checkpoint id.  */
 static void
 restart_command (char *args, int from_tty)
 {
@@ -725,9 +667,8 @@ Tells gdb whether to detach the child of
   add_com ("checkpoint", class_obscure, checkpoint_command, _("\
 Fork a duplicate process (experimental)."));
 
-  /* Restart command: restore the context of a specified fork
-     process.  May be used for "program forks" as well as for
-     "debugger forks" (checkpoints).  */
+  /* Restart command: restore the context of a specified checkpoint
+     process.  */
 
   add_com ("restart", class_obscure, restart_command, _("\
 restart <n>: restore program context from a checkpoint.\n\
@@ -737,36 +678,19 @@ Argument 'n' is checkpoint ID, as displa
      fork list.  */
 
   add_cmd ("checkpoint", class_obscure, delete_fork_command, _("\
-Delete a fork/checkpoint (experimental)."),
+Delete a checkpoint (experimental)."),
 	   &deletelist);
 
   /* Detach checkpoint command: release the process to run independently, 
      and remove it from the fork list.  */
 
   add_cmd ("checkpoint", class_obscure, detach_fork_command, _("\
-Detach from a fork/checkpoint (experimental)."),
+Detach from a checkpoint (experimental)."),
 	   &detachlist);
 
   /* Info checkpoints command: list all forks/checkpoints 
      currently under gdb's control.  */
 
   add_info ("checkpoints", info_forks_command,
-	    _("IDs of currently known forks/checkpoints."));
-
-  /* Command aliases (let "fork" and "checkpoint" be used 
-     interchangeably).  */
-
-  add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &deletelist);
-  add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &detachlist);
-  add_info_alias ("forks", "checkpoints", 0);
-
-  /* "fork <n>" (by analogy to "thread <n>").  */
-  add_com ("fork", class_obscure, fork_command, _("\
-fork <n>: Switch between forked processes.\n\
-Argument 'n' is fork ID, as displayed by 'info forks'."));
-
-  /* "process <proc id>" as opposed to "fork <fork id>".  */
-  add_com ("process", class_obscure, process_command, _("\
-process <pid>: Switch between forked processes.\n\
-Argument 'pid' is process ID, as displayed by 'info forks' or 'shell ps'."));
+	    _("IDs of currently known checkpoints."));
 }
Index: src/gdb/linux-fork.h
===================================================================
--- src.orig/gdb/linux-fork.h	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/linux-fork.h	2009-06-08 13:14:24.000000000 +0100
@@ -20,13 +20,11 @@
 struct fork_info;
 extern struct fork_info *add_fork (pid_t);
 extern struct fork_info *find_fork_pid (pid_t);
-extern void fork_save_infrun_state (struct fork_info *, int);
 extern void linux_fork_killall (void);
 extern void linux_fork_mourn_inferior (void);
 extern void linux_fork_detach (char *, int);
-extern int  forks_exist_p (void);
-
-struct fork_info *fork_list;
+extern int forks_exist_p (void);
+extern int linux_fork_checkpointing_p (int);
 
 extern int detach_fork;
 
Index: src/gdb/cli/cli-cmds.c
===================================================================
--- src.orig/gdb/cli/cli-cmds.c	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/cli/cli-cmds.c	2009-06-08 13:14:24.000000000 +0100
@@ -124,6 +124,10 @@ struct cmd_list_element *deletelist;
 
 struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands. */
+
+struct cmd_list_element *killlist;
+
 /* Chain containing all defined "enable breakpoint" subcommands. */
 
 struct cmd_list_element *enablebreaklist;
Index: src/gdb/cli/cli-cmds.h
===================================================================
--- src.orig/gdb/cli/cli-cmds.h	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/cli/cli-cmds.h	2009-06-08 13:14:24.000000000 +0100
@@ -41,6 +41,10 @@ extern struct cmd_list_element *deleteli
 
 extern struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands.  */
+
+extern struct cmd_list_element *killlist;
+
 /* Chain containing all defined toggle subcommands. */
 
 extern struct cmd_list_element *togglelist;
Index: src/gdb/gdbcmd.h
===================================================================
--- src.orig/gdb/gdbcmd.h	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/gdbcmd.h	2009-06-08 13:14:24.000000000 +0100
@@ -52,6 +52,10 @@ extern struct cmd_list_element *deleteli
 
 extern struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands.  */
+
+extern struct cmd_list_element *killlist;
+
 /* Chain containing all defined toggle subcommands.  */
 
 extern struct cmd_list_element *togglelist;
Index: src/gdb/inferior.c
===================================================================
--- src.orig/gdb/inferior.c	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/inferior.c	2009-06-08 13:14:24.000000000 +0100
@@ -25,6 +25,7 @@
 #include "gdbthread.h"
 #include "ui-out.h"
 #include "observer.h"
+#include "gdbthread.h"
 
 void _initialize_inferiors (void);
 
@@ -330,12 +331,114 @@ print_inferior (struct ui_out *uiout, in
   do_cleanups (old_chain);
 }
 
+static void
+detach_inferior_command (char *args, int from_tty)
+{
+  int num = -1;
+  int pid;
+  struct thread_info *tp;
+
+  if (!args || !*args)
+    error (_("Requires argument (inferior id to detach)"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  tp = any_thread_of_process (pid);
+  if (!tp)
+    error (_("Inferior has no threads."));
+
+  switch_to_thread (tp->ptid);
+
+  detach_command (NULL, from_tty);
+}
+
+static void
+kill_inferior_command (char *args, int from_tty)
+{
+  int num = -1;
+  int pid;
+  struct thread_info *tp;
+
+  if (!args || !*args)
+    error (_("Requires argument (inferior id to kill)"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  tp = any_thread_of_process (pid);
+  if (!tp)
+    error (_("Inferior has no threads."));
+
+  switch_to_thread (tp->ptid);
+
+  target_kill ();
+
+  bfd_cache_close_all ();
+}
+
+static void
+inferior_command (char *args, int from_tty)
+{
+  int num, pid;
+
+  if (!have_inferiors ())
+    error (_("No inferiors"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  if (pid != ptid_get_pid (inferior_ptid))
+    {
+      struct thread_info *tp;
+
+      tp = any_thread_of_process (pid);
+      if (!tp)
+	error (_("Inferior has no threads."));
+
+      switch_to_thread (tp->ptid);
+    }
+
+  printf_filtered (_("[Switching to thread %d (%s)] "),
+		   pid_to_thread_id (inferior_ptid),
+		   target_pid_to_str (inferior_ptid));
+
+  if (is_running (inferior_ptid))
+    ui_out_text (uiout, "(running)\n");
+  else
+    {
+      ui_out_text (uiout, "\n");
+      print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+    }
+}
+
 /* Print information about currently known inferiors.  */
 
 static void
-info_inferiors_command (char *arg, int from_tty)
+info_inferiors_command (char *args, int from_tty)
 {
-  print_inferior (uiout, -1);
+  int requested = -1;
+
+  if (args && *args)
+    {
+      requested = parse_and_eval_long (args);
+      if (!valid_gdb_inferior_id (requested))
+	error (_("Inferior ID %d not known."), requested);
+    }
+
+  print_inferior (uiout, requested);
 }
 
 /* Print notices when new inferiors are created and die.  */
@@ -359,4 +462,17 @@ Show printing of inferior events (e.g., 
          NULL,
          show_print_inferior_events,
          &setprintlist, &showprintlist);
+
+  add_cmd ("inferior", class_run, detach_inferior_command, _("\
+Detach from inferior ID."),
+	   &detachlist);
+
+  add_cmd ("inferior", class_run, kill_inferior_command, _("\
+Kill inferior ID."),
+	   &killlist);
+
+  add_cmd ("inferior", class_run, inferior_command, _("\
+Use this command to switch between inferiors.\n\
+The new inferior ID must be currently known."),
+	   &cmdlist);
 }
Index: src/gdb/testsuite/gdb.base/multi-forks.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/multi-forks.exp	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/testsuite/gdb.base/multi-forks.exp	2009-06-08 13:14:24.000000000 +0100
@@ -140,6 +140,19 @@ gdb_test "print pids\[0\]==0 || pids\[1\
 # Now test with detach-on-fork off.
 #
 
+# detach-on-fork isn't implemented on hpux.
+#
+if {![istarget "*-*-linux*"]} then {
+    continue
+}
+
+# Start with a fresh gdb
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
 runto_main
 gdb_breakpoint $exit_bp_loc
 
@@ -152,60 +165,59 @@ gdb_test "set detach off" "" "set detach
 
 #
 # We will now run every fork up to the exit bp, 
-# eventually winding up with 16 forks.
+# eventually winding up with 16 inferiors.
 #
 
 for {set i 1} {$i <= 15} {incr i} {
   gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit $i"
-  gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork $i"
-  gdb_test "restart $i" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
-      "restart $i"
+  gdb_test "info inferior" " 5 .* 4 .* 3 .* 2 .*" "info inferior $i"
+  gdb_test "inferior $i + 1" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
+      "inferior $i"
 }
 
 gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit 16"
-gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork 16"
-gdb_test "restart 0" " main .*" "restart final"
+gdb_test "info inferiors" " 5 .* 4 .* 3 .* 2 .*" "info inferior 16"
+gdb_test "inferior 2" " main .*" "restart final"
 
 #
 # Now we should examine all the pids.
 #
 
 # 
-# Test detach fork
+# Test detach inferior
 # 
 
-# [assumes we're at #0]
-gdb_test "detach fork 1" "Detached .*" "Detach 1"
-gdb_test "detach fork 2" "Detached .*" "Detach 2"
-gdb_test "detach fork 3" "Detached .*" "Detach 3"
-gdb_test "detach fork 4" "Detached .*" "Detach 4"
+# [assumes we're at #1]
+gdb_test "detach inferior 2" "Detaching .*" "Detach 2"
+gdb_test "detach inferior 3" "Detaching .*" "Detach 3"
+gdb_test "detach inferior 4" "Detaching .*" "Detach 4"
+gdb_test "detach inferior 5" "Detaching .*" "Detach 5"
 
 # 
-# Test delete fork
-# 
+# Test kill inferior
+#
 
-gdb_test "delete fork 5" "" "Delete 5"
-gdb_test "info fork 5"   "No fork number 5." "Did delete 5"
-gdb_test "delete fork 6" "" "Delete 6"
-gdb_test "info fork 6"   "No fork number 6." "Did delete 6"
-gdb_test "delete fork 7" "" "Delete 7"
-gdb_test "info fork 7"   "No fork number 7." "Did delete 7"
-gdb_test "delete fork 8" "" "Delete 8"
-gdb_test "info fork 8"   "No fork number 8." "Did delete 8"
-gdb_test "delete fork 9" "" "Delete 9"
-gdb_test "info fork 9"   "No fork number 9." "Did delete 9"
-gdb_test "delete fork 10" "" "Delete 10"
-gdb_test "info fork 10"   "No fork number 10." "Did delete 10"
-gdb_test "delete fork 11" "" "Delete 11"
-gdb_test "info fork 11"   "No fork number 11." "Did delete 11"
-gdb_test "delete fork 12" "" "Delete 12"
-gdb_test "info fork 12"   "No fork number 12." "Did delete 12"
-gdb_test "delete fork 13" "" "Delete 13"
-gdb_test "info fork 13"   "No fork number 13." "Did delete 13"
-gdb_test "delete fork 14" "" "Delete 14"
-gdb_test "info fork 14"   "No fork number 14." "Did delete 14"
-gdb_test "delete fork 15" "" "Delete 15"
-gdb_test "info fork 15"   "No fork number 15." "Did delete 15"
+gdb_test "kill inferior 6" "" "Kill 6"
+gdb_test "info inferior 6" "Inferior ID 6 not known." "Did kill 6"
+gdb_test "kill inferior 7" "" "Kill 7"
+gdb_test "info inferior 7" "Inferior ID 7 not known." "Did kill 7"
+gdb_test "kill inferior 8" "" "Kill 8"
+gdb_test "info inferior 8" "Inferior ID 8 not known." "Did kill 8"
+gdb_test "kill inferior 9" "" "Kill 9"
+gdb_test "info inferior 9" "Inferior ID 9 not known." "Did kill 9"
+gdb_test "kill inferior 10" "" "Kill 10"
+gdb_test "info inferior 10" "Inferior ID 10 not known." "Did kill 10"
+gdb_test "kill inferior 11" "" "Kill 11"
+gdb_test "info inferior 11" "Inferior ID 11 not known." "Did kill 11"
+gdb_test "kill inferior 12" "" "Kill 12"
+gdb_test "info inferior 12" "Inferior ID 12 not known." "Did kill 12"
+gdb_test "kill inferior 13" "" "Kill 13"
+gdb_test "info inferior 13" "Inferior ID 13 not known." "Did kill 13"
+gdb_test "kill inferior 14" "" "Kill 14"
+gdb_test "info inferior 14" "Inferior ID 14 not known." "Did kill 14"
+gdb_test "kill inferior 15" "" "Kill 15"
+gdb_test "info inferior 15" "Inferior ID 15 not known." "Did kill 15"
+gdb_test "kill inferior 16" "" "Kill 16"
+gdb_test "info inferior 16" "Inferior ID 16 not known." "Did kill 16"
 
 return 0
-
Index: src/gdb/testsuite/gdb.base/ending-run.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/ending-run.exp	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/testsuite/gdb.base/ending-run.exp	2009-06-08 13:14:24.000000000 +0100
@@ -71,7 +71,7 @@ gdb_test "b ending-run.c:14" ".*Note.*al
 gdb_test "cle ending-run.c:14" \
 	".*Deleted breakpoint 5.*" "Cleared 2 by line"
 
-send_gdb "inf line ending-run.c:14\n"
+send_gdb "info line ending-run.c:14\n"
 gdb_expect {
     -re ".*address (0x\[0-9a-fA-F]*).*$gdb_prompt $" {
         set line_nine $expect_out(1,string)
Index: src/gdb/testsuite/gdb.base/help.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/help.exp	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/testsuite/gdb.base/help.exp	2009-06-08 13:14:24.000000000 +0100
@@ -300,7 +300,9 @@ gdb_test "help inspect" "Same as \"print
 # test help jump
 gdb_test "help jump" "Continue program being debugged at specified line or address\.\[\r\n\]+Give as argument either LINENUM or \[*\]+ADDR, where ADDR is an expression\[\r\n\]+for an address to start at\." "help jump"
 # test help kill
-gdb_test "help kill" "Kill execution of program being debugged\." "help kill"
+test_prefix_command_help "kill" {
+    "Kill execution of program being debugged\.\[\r\n\]+"
+}
 # test help list "l" abbreviation
 gdb_test "help l" "List specified function or line\.\[\r\n\]+With no argument, lists ten more lines after or around previous listing\.\[\r\n\]+\"list -\" lists the ten lines before a previous ten-line listing\.\[\r\n\]+One argument specifies a line, and ten lines are listed around that line\.\[\r\n\]+Two arguments with comma between specify starting and ending lines to list\.\[\r\n\]+Lines can be specified in these ways:\[\r\n\]+  LINENUM, to list around that line in current file,\[\r\n\]+  FILE:LINENUM, to list around that line in that file,\[\r\n\]+  FUNCTION, to list around beginning of that function,\[\r\n\]+  FILE:FUNCTION, to distinguish among like-named static functions\.\[\r\n\]+  \[*\]ADDRESS, to list around the line containing that address\.\[\r\n\]+With two args if one is empty it stands for ten lines away from the other arg\." "help list \"l\" abbreviation"
 # test help list
Index: src/gdb/infcmd.c
===================================================================
--- src.orig/gdb/infcmd.c	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/infcmd.c	2009-06-08 13:14:24.000000000 +0100
@@ -2626,8 +2626,9 @@ fully linked executable files and separa
 	       &showlist);
   set_cmd_completer (c, noop_completer);
 
-  add_com ("kill", class_run, kill_command,
-	   _("Kill execution of program being debugged."));
+  add_prefix_cmd ("kill", class_run, kill_command,
+		  _("Kill execution of program being debugged."),
+		  &killlist, "kill ", 0, &cmdlist);
 
   add_com ("attach", class_run, attach_command, _("\
 Attach to a process or file outside of GDB.\n\
Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/gdbthread.h	2009-06-08 13:14:24.000000000 +0100
@@ -238,6 +238,9 @@ struct thread_info *find_thread_id (int 
    returns the first thread in the list.  */
 struct thread_info *first_thread_of_process (int pid);
 
+/* Returns any thread of process PID.  */
+extern struct thread_info *any_thread_of_process (int pid);
+
 /* Change the ptid of thread OLD_PTID to NEW_PTID.  */
 void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
 
Index: src/gdb/thread.c
===================================================================
--- src.orig/gdb/thread.c	2009-06-08 12:11:55.000000000 +0100
+++ src/gdb/thread.c	2009-06-08 13:14:24.000000000 +0100
@@ -428,6 +428,18 @@ first_thread_of_process (int pid)
   return ret;
 }
 
+struct thread_info *
+any_thread_of_process (int pid)
+{
+  struct thread_info *tp;
+
+  for (tp = thread_list; tp; tp = tp->next)
+    if (ptid_get_pid (tp->ptid) == pid)
+      return tp;
+
+  return NULL;
+}
+
 /* Print a list of thread ids currently known, and the total number of
    threads. To be used from within catch_errors. */
 static int
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo	2009-06-08 12:50:38.000000000 +0100
+++ src/gdb/doc/gdb.texinfo	2009-06-08 13:17:17.000000000 +0100
@@ -2388,7 +2388,39 @@ To find out what inferiors exist at any 
 @kindex info inferiors
 @item info inferiors
 Print a list of all inferiors currently being managed by @value{GDBN}.
+@end table
+
+To switch focus between inferiors, use the @code{inferior} command:
+
+@table @code
+@kindex inferior @var{inferior-id}
+@item inferior @var{inferior-id}
+Make inferior number @var{inferior-id} the current inferior.  The
+argument @var{inferior-id} is the internal inferior number assigned by
+@value{GDBN}, as shown in the first field of the @samp{info inferiors}
+display.
+@end table
+
+To quit debugging one of the inferiors, you can either detach from it
+by using the @w{@code{detach inferior}} command (allowing it to run
+independently), or kill it using the @w{@code{kill inferior}} command:
+
+@table @code
+@kindex detach inferior @var{inferior-id}
+@item detach inferior @var{inferior-id}
+Detach from the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+
+@kindex kill inferior @var{inferior-id}
+@item kill inferior @var{inferior-id}
+Kill the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+@end table
+
+To be notified when inferiors are started or exit under @value{GDBN}'s
+control use @w{@code{set print inferior-events}}:
 
+@table @code
 @kindex set print inferior-events
 @cindex print messages on inferior start and exit
 @item set print inferior-events
@@ -2772,51 +2804,17 @@ is held suspended.  
 Show whether detach-on-fork mode is on/off.
 @end table
 
-If you choose to set @samp{detach-on-fork} mode off, then
-@value{GDBN} will retain control of all forked processes (including
-nested forks).  You can list the forked processes under the control of
-@value{GDBN} by using the @w{@code{info forks}} command, and switch
-from one fork to another by using the @w{@code{fork}} command.
-
-@table @code
-@kindex info forks
-@item info forks
-Print a list of all forked processes under the control of @value{GDBN}.
-The listing will include a fork id, a process id, and the current 
-position (program counter) of the process.
-
-@kindex fork @var{fork-id}
-@item fork @var{fork-id}
-Make fork number @var{fork-id} the current process.  The argument
-@var{fork-id} is the internal fork number assigned by @value{GDBN},
-as shown in the first field of the @samp{info forks} display.
-
-@kindex process @var{process-id}
-@item process @var{process-id}
-Make process number @var{process-id} the current process.  The
-argument @var{process-id} must be one that is listed in the output of
-@samp{info forks}.
-
-@end table
+If you choose to set @samp{detach-on-fork} mode off, then @value{GDBN}
+will retain control of all forked processes (including nested forks).
+You can list the forked processes under the control of @value{GDBN} by
+using the @w{@code{info inferiors}} command, and switch from one fork
+to another by using the @code{inferior} command (@pxref{Inferiors,
+,Debugging Multiple Inferiors}).
 
 To quit debugging one of the forked processes, you can either detach
-from it by using the @w{@code{detach fork}} command (allowing it to
-run independently), or delete (and kill) it using the
-@w{@code{delete fork}} command.
-
-@table @code
-@kindex detach fork @var{fork-id}
-@item detach fork @var{fork-id}
-Detach from the process identified by @value{GDBN} fork number
-@var{fork-id}, and remove it from the fork list.  The process will be
-allowed to run independently.
-
-@kindex delete fork @var{fork-id}
-@item delete fork @var{fork-id}
-Kill the process identified by @value{GDBN} fork number @var{fork-id},
-and remove it from the fork list.
-
-@end table
+from it by using the @w{@code{detach inferior}} command (allowing it
+to run independently), or kill it using the @w{@code{kill inferior}}
+command.  Again, @pxref{Inferiors, ,Debugging Multiple Inferiors}.
 
 If you ask to debug a child process and a @code{vfork} is followed by an
 @code{exec}, @value{GDBN} executes the new target up to the first
@@ -2824,8 +2822,8 @@ breakpoint in the new target.  If you ha
 @code{main} in your original program, the breakpoint will also be set on
 the child process's @code{main}.
 
-When a child process is spawned by @code{vfork}, you cannot debug the
-child or parent until an @code{exec} call completes.
+On some systems, when a child process is spawned by @code{vfork}, you
+cannot debug the child or parent until an @code{exec} call completes.
 
 If you issue a @code{run} command to @value{GDBN} after an @code{exec}
 call executes, the new target restarts.  To restart the parent process,
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS	2009-06-08 13:25:28.000000000 +0100
+++ src/gdb/NEWS	2009-06-08 13:50:08.000000000 +0100
@@ -187,6 +187,18 @@ macro undef
 info os processes
   Show operating system information about processes.
 
+info inferiors
+  List the inferiors currently under GDB's control.
+
+inferior NUM
+  Switch focus to inferior number NUM.
+
+detach inferior NUM
+  Detach from inferior number NUM.
+
+kill inferior NUM
+  Kill inferior number NUM.
+
 * New options
 
 set sh calling-convention
@@ -310,6 +322,38 @@ show schedule-multiple
   Allow GDB to resume all threads of all processes or only threads of
   the current process.
 
+* Removed commands
+
+info forks
+
+  For program forks, this is replaced by the new more generic `info
+  inferiors' command.  To list checkpoints, you can still use the
+  `info checkpoints' command, which was an alias for the `info forks'
+  command.
+
+fork NUM
+
+  Replaced by the new `inferior' command.  To switch between
+  checkpoints, you can still use the `restart' command, which was an
+  alias for the `fork' command.
+
+process PID
+  Removed.
+
+delete fork NUM
+
+  For program forks, this is replaced by the new more generic `kill
+  inferior' command.  To delete a checkpoint, you can still use the
+  `delete checkpoint' command, which was an alias for the `delete
+  fork' command.
+
+detach fork NUM
+
+  For program forks, this is replaced by the new more generic `detach
+  inferior' command.  To detach a checkpoint, you can still use the
+  `detach checkpoint' command, which was an alias for the `detach
+  fork' command.
+
 * New native configurations
 
 x86/x86_64 Darwin		i[34567]86-*-darwin*


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-06 16:06         ` Pedro Alves
  2009-06-08  1:34           ` Marc Khouzam
@ 2009-06-08 22:39           ` Tom Tromey
  2009-06-08 23:16             ` Pedro Alves
  1 sibling, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2009-06-08 22:39 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:

Tom> How about this?  It adds the column headings and prints something when
Tom> there are no inferiors.

Pedro> This is fine with me, thanks, but,

Pedro> are you planning on doing something similar to "info threads"
Pedro> output?

I wasn't planning to.

Really I just wanted to get column headers for the otherwise-obscure
"info inferiors" columns...

Pedro> IMO, both these commands should be consistent.  In that case, you
Pedro> may have an issue with MI, as it calls print_thread_info --- I don't
Pedro> know if using a table instead of a list changes MI output.

I think it does, so changing it would not be ok.

Pedro> (I was a bit surprised to (re-)find that MI doesn't use
Pedro> print_inferior)

Naughty.  Though at a quick glance I didn't see where MI does any
printing of the inferiors...?

In this case I think the patch is ok anyway -- an error in "info
threads" does not, IMO, imply that we should propagate the mistake to
other commands.  Let me know what you think of this, I won't install
the patch if you feel differently.

Tom


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-08  1:34           ` Marc Khouzam
@ 2009-06-08 22:43             ` Tom Tromey
  2009-06-08 23:22               ` Pedro Alves
  0 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2009-06-08 22:43 UTC (permalink / raw)
  To: Marc Khouzam; +Cc: Pedro Alves, gdb-patches

>>>>> "Marc" == Marc Khouzam <marc.khouzam@ericsson.com> writes:

Marc> Just a note that up to GDB 6.8, we used the output of "info threads"
Marc> in the DSF-GDB Eclipse frontend.  With GDB 7.0 we moved to
Marc> "-thread-info"
Marc> So, I'm not affected by a change of output

Unfortunately both -thread-info and "info threads" just amount to a
call to print_thread_info.  So, if we fix that to use a table, it will
change the MI output, IIUC.

FWIW, using a table in these situations seems superior to me, because
it allows for column headings and because it handles some of the
formatting bits (lining up columns) automatically.

So, at least IMO, code like this should prefer tables over things like
emitting sequences of spaces.  If I'm missing some obvious drawback
here, please let me know, since I'd like to be informed...q

Marc> I'm not sure if you enforce CLI output backwards compatibility...

I think we definitely should not.

Tom


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-08 22:39           ` Tom Tromey
@ 2009-06-08 23:16             ` Pedro Alves
  2009-06-10 22:09               ` Tom Tromey
  0 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2009-06-08 23:16 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Monday 08 June 2009 23:37:12, Tom Tromey wrote:

> Pedro> (I was a bit surprised to (re-)find that MI doesn't use
> Pedro> print_inferior)
> 
> Naughty.  Though at a quick glance I didn't see where MI does any
> printing of the inferiors...?

It's in the -list-thread-groups command (mi_cmd_list_thread_groups/print_one_inferior).

> In this case I think the patch is ok anyway -- an error in "info
> threads" does not, IMO, imply that we should propagate the mistake to
> other commands.  Let me know what you think of this, I won't install
> the patch if you feel differently.

No, I agree with you.

My only concern is that the PID column may end up being
the wrong name for the target-id, due to targets that don't have a real
notion of PID.  I was planning of making that column's value print
something like target_pid_to_str (pid_to_ptid (PID)), just like the
corresponding column in info threads.  I was hoping that when
we'd get to name the columns in "info threads" we'd come up with
a nice name for it.  :-)

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-08 22:43             ` Tom Tromey
@ 2009-06-08 23:22               ` Pedro Alves
  0 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2009-06-08 23:22 UTC (permalink / raw)
  To: Tom Tromey; +Cc: Marc Khouzam, gdb-patches

On Monday 08 June 2009 23:40:59, Tom Tromey wrote:
> >>>>> "Marc" == Marc Khouzam <marc.khouzam@ericsson.com> writes:
> 
> Marc> Just a note that up to GDB 6.8, we used the output of "info threads"
> Marc> in the DSF-GDB Eclipse frontend.  With GDB 7.0 we moved to
> Marc> "-thread-info"
> Marc> So, I'm not affected by a change of output
> 
> Unfortunately both -thread-info and "info threads" just amount to a
> call to print_thread_info.  So, if we fix that to use a table, it will
> change the MI output, IIUC.

Yeah, one of the advantages (or not!) of using uiout is that it
allows sharing that code.  But that could be worked around
just by making a special version of that function for MI, but...

Marc is right...  -thread-info is too recent --- it's post-6.8
even.  It's likely that current frontends would get confused with the
column headers, probably mistaking it for a thread entry (my guess, atoi
on the thread num, and calling it thread 0) :-(

This probably includes emacs, so should be easy to try just for fun. :-)

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-08 23:16             ` Pedro Alves
@ 2009-06-10 22:09               ` Tom Tromey
  2009-06-10 22:13                 ` Pedro Alves
  0 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2009-06-10 22:09 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches

>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:

Tom> In this case I think the patch is ok anyway -- an error in "info
Tom> threads" does not, IMO, imply that we should propagate the mistake to
Tom> other commands.  Let me know what you think of this, I won't install
Tom> the patch if you feel differently.

Pedro> No, I agree with you.

Thanks.  I'm am going to commit this shortly.

Pedro> My only concern is that the PID column may end up being
Pedro> the wrong name for the target-id, due to targets that don't have a real
Pedro> notion of PID.  I was planning of making that column's value print
Pedro> something like target_pid_to_str (pid_to_ptid (PID)), just like the
Pedro> corresponding column in info threads.  I was hoping that when
Pedro> we'd get to name the columns in "info threads" we'd come up with
Pedro> a nice name for it.  :-)

I think we can change the column headers, as shown by the CLI (and not
MI?  I am not certain) at some later date, just by changing the
ui_out_table_header calls.  So I'm not too worried.

Tom


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-10 22:09               ` Tom Tromey
@ 2009-06-10 22:13                 ` Pedro Alves
  0 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2009-06-10 22:13 UTC (permalink / raw)
  To: tromey; +Cc: gdb-patches

On Wednesday 10 June 2009 23:07:19, Tom Tromey wrote:
> I think we can change the column headers, as shown by the CLI (and not
> MI?  I am not certain) at some later date, just by changing the
> ui_out_table_header calls.  So I'm not too worried.

Yeah.  MI is not using this code afterall, and this is
all new stuff (no frontend should be using it, and it better not
be any that does so in the future), so we can do whatever we
want at a later date.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-05-30 23:13 Move the multi-forks support to the generic multi-inferiors support Pedro Alves
  2009-05-31 20:46 ` Tom Tromey
  2009-06-08 12:58 ` Pedro Alves
@ 2009-06-11 19:32 ` Michael Snyder
  2009-07-01 18:27   ` Pedro Alves
  2 siblings, 1 reply; 21+ messages in thread
From: Michael Snyder @ 2009-06-11 19:32 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, Eli Zaretskii

Belatedly entering the discussion on behalf of checkpoints,
I think what you are doing makes sense, and I trust you, so
knock yourself out.   ;-)

Michael



^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-06-11 19:32 ` Michael Snyder
@ 2009-07-01 18:27   ` Pedro Alves
  2009-07-01 19:16     ` Eli Zaretskii
  0 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2009-07-01 18:27 UTC (permalink / raw)
  To: Michael Snyder; +Cc: gdb-patches, Eli Zaretskii

On Thursday 11 June 2009 20:27:27, Michael Snyder wrote:
> Belatedly entering the discussion on behalf of checkpoints,

:-)  My turn to be late.

> I think what you are doing makes sense, and I trust you, so
> knock yourself out.   ;-)

Thanks Michael.  Eli, what do you think of this (and of
the docs changes)?  Posting the same patch adjusted to apply
cleanly against current head below.

-- 
Pedro Alves

gdb/
2009-07-01  Pedro Alves  <pedro@codesourcery.com>

	* linux-nat.c (linux_child_follow_fork): If we're staying attached
	to the child process, enable event reporting on it.  Don't handle
	checkpoints here.  Instead, add the child fork to the lwp thread
	and inferior lists without clobbering the previous inferior.  Let
	the thread_db layer learn about a new child process, even if
	following the parent.
	(linux_nat_switch_fork): Delete lwps of the current inferior only,
	instead of clearing the whole list.  Use thread_change_ptid to
	give the core the illusion the new checkpoint is still the same
	inferior.  Clear the register cache.
	(linux_handle_extended_wait): Handle checkpoints here.
	(linux_multi_process): Turn on.
	* linux-fork.c (struct fork_info) <pc>: Remove field.
	(init_fork_list): Do not delete the checkpoint from the inferior
	list (it is not there).
	(fork_load_infrun_state): Don't switch inferior_ptid here.  Pass
	the new checkpoint's ptid to linux_nat_switch_fork.
	(fork_save_infrun_state): Make static.  Don't stop the pc field of
	fork_info, it's gone.
	(linux_fork_mourn_inferior): Don't delete the checkpoint from the
	inferior list, it's not there.
	(linux_fork_detach): Ditto.
	(delete_fork_command): Replace mention of fork/checkpoint by
	checkpoint only.
	(detach_fork_command): Likewise.  Don't delete the checkpoint from
	the inferior list.
	(info_forks_command): Adjust.
	(restore_detach_fork): Delete.
	(checkpointing_pid): New.
	(linux_fork_checkpointing_p): New.
	(save_detach_fork): Delete.
	(checkpoint_command): Delete temp_detach_fork.  Don't remove
	breakpoints, that's a nop.  Store the pid of the process we're
	checkpointing, and use make_cleanup_restore_integer to restore it.
	Don't reinsert breakpoints here.
	(process_command, fork_command): Delete.
	(restart_command): Update comments to only mention checkpoints,
	not forks.
	(_initialize_linux_fork): Delete "fork", "process", "info forks"
	commands.
	* linux-fork.h (fork_save_infrun_state, fork_list): Delete
	declarations.
	(linux_fork_checkpointing_p): Declare.
	* cli/cli-cmds.c (killlist): New.
	* cli/cli-cmds.h (killlist): Declare.
	* gdbcmd.h (killlist): Declare.
	* inferior.c: Include "gdbthread.h".
	(detach_inferior_command, kill_inferior_command)
	(inferior_command): New.
	(info_inferiors_command): Allow specifying a specific inferior id.
	(_initialize_inferiors): Register "inferior", "kill inferior" and
	"detach inferior" commands.
	* infcmd.c (_initialize_infcmd): Make "kill" a prefix command.
	* gdbthread.h (any_thread_of_process): Declare.
	* thread.c (any_thread_of_process): New.

	* NEWS: Mention 'info inferiors', 'inferior', 'detach inferior'
	and 'kill inferior' as new commands.
	(Removed commands): New section, mentioning that 'info forks',
	'fork', 'process', 'delete fork' and 'detach fork' are now gone.

gdb/testsuite/
2009-07-01  Pedro Alves  <pedro@codesourcery.com>

	* gdb.base/multi-forks.exp: Only run detach-on-fork tests on
	linux.  Adjust to use "inferior", "info inferiors", "detach
	inferior" and "kill inferior" instead of "restart", "info fork",
	"detach fork" and "delete fork".
	* gdb.base/ending-run.exp: Spell out "info".
	* gdb.base/help.exp: Adjust to use test_prefix_command_help for
	the "kill" command.

gdb/doc/
2009-07-01  Pedro Alves  <pedro@codesourcery.com>

	* gdb.texinfo (Debugging multiple inferiors): Document the
	"inferior", "detach inferior" and "kill inferior" commands.
	(Debugging Programs with Multiple Processes): Adjust to mention
	generic "inferior" commands.  Delete mention of "detach fork" and
	"delete fork".  Cross reference to "Debugging multiple inferiors"
	section.

	
---
 gdb/NEWS                               |   44 +++++++++++
 gdb/cli/cli-cmds.c                     |    4 +
 gdb/cli/cli-cmds.h                     |    4 +
 gdb/doc/gdb.texinfo                    |   88 +++++++++++-----------
 gdb/gdbcmd.h                           |    4 +
 gdb/gdbthread.h                        |    3 
 gdb/infcmd.c                           |    5 -
 gdb/inferior.c                         |  120 ++++++++++++++++++++++++++++++
 gdb/linux-fork.c                       |  128 ++++++---------------------------
 gdb/linux-fork.h                       |    6 -
 gdb/linux-nat.c                        |  115 +++++++++++++++++++++--------
 gdb/testsuite/gdb.base/ending-run.exp  |    2 
 gdb/testsuite/gdb.base/help.exp        |    4 -
 gdb/testsuite/gdb.base/multi-forks.exp |   86 ++++++++++++----------
 gdb/thread.c                           |   12 +++
 15 files changed, 401 insertions(+), 224 deletions(-)

Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/linux-nat.c	2009-07-01 19:17:08.000000000 +0100
@@ -338,6 +338,12 @@ static int stop_callback (struct lwp_inf
 
 static void block_child_signals (sigset_t *prev_mask);
 static void restore_child_signals_mask (sigset_t *prev_mask);
+
+struct lwp_info;
+static struct lwp_info *add_lwp (ptid_t ptid);
+static void purge_lwp_list (int pid);
+static struct lwp_info *find_lwp_pid (ptid_t ptid);
+
 \f
 /* Trivial list manipulation functions to keep track of a list of
    new stopped processes.  */
@@ -587,6 +593,9 @@ linux_child_follow_fork (struct target_o
     parent_pid = ptid_get_pid (inferior_ptid);
   child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
 
+  if (!detach_fork)
+    linux_enable_event_reporting (pid_to_ptid (child_pid));
+
   if (! follow_child)
     {
       /* We're already attached to the parent, by default. */
@@ -617,8 +626,9 @@ linux_child_follow_fork (struct target_o
 	}
       else
 	{
-	  struct fork_info *fp;
 	  struct inferior *parent_inf, *child_inf;
+	  struct lwp_info *lp;
+	  struct cleanup *old_chain;
 
 	  /* Add process to GDB's tables.  */
 	  child_inf = add_inferior (child_pid);
@@ -627,11 +637,16 @@ linux_child_follow_fork (struct target_o
 	  child_inf->attach_flag = parent_inf->attach_flag;
 	  copy_terminal_info (child_inf, parent_inf);
 
-	  /* Retain child fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (child_pid);
-	  if (!fp)
-	    fp = add_fork (child_pid);
-	  fork_save_infrun_state (fp, 0);
+	  old_chain = save_inferior_ptid ();
+
+	  inferior_ptid = ptid_build (child_pid, child_pid, 0);
+	  add_thread (inferior_ptid);
+	  lp = add_lwp (inferior_ptid);
+	  lp->stopped = 1;
+
+	  check_for_thread_db ();
+
+	  do_cleanups (old_chain);
 	}
 
       if (has_vforked)
@@ -692,6 +707,7 @@ linux_child_follow_fork (struct target_o
     {
       struct thread_info *tp;
       struct inferior *parent_inf, *child_inf;
+      struct lwp_info *lp;
 
       /* Before detaching from the parent, remove all breakpoints from it. */
       remove_breakpoints ();
@@ -733,30 +749,33 @@ linux_child_follow_fork (struct target_o
 
       if (has_vforked)
 	{
+	  struct lwp_info *parent_lwp;
+
 	  linux_parent_pid = parent_pid;
+
+	  /* Get rid of the inferior on the core side as well.  */
+	  inferior_ptid = null_ptid;
 	  detach_inferior (parent_pid);
-	}
-      else if (!detach_fork)
-	{
-	  struct fork_info *fp;
-	  /* Retain parent fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (parent_pid);
-	  if (!fp)
-	    fp = add_fork (parent_pid);
-	  fork_save_infrun_state (fp, 0);
 
-	  /* Also add an entry for the child fork.  */
-	  fp = find_fork_pid (child_pid);
-	  if (!fp)
-	    fp = add_fork (child_pid);
-	  fork_save_infrun_state (fp, 0);
+	  /* Also get rid of all its lwps.  We will detach from this
+	     inferior soon-ish, but, we will still get an exit event
+	     reported through waitpid when it exits.  If we didn't get
+	     rid of the lwps from our list, we would end up reporting
+	     the inferior exit to the core, which would then try to
+	     mourn a non-existing (from the core's perspective)
+	     inferior.  */
+	  parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
+	  purge_lwp_list (GET_PID (parent_lwp->ptid));
+	  linux_parent_pid = parent_pid;
 	}
-      else
+      else if (detach_fork)
 	target_detach (NULL, 0);
 
       inferior_ptid = ptid_build (child_pid, child_pid, 0);
+      add_thread (inferior_ptid);
+      lp = add_lwp (inferior_ptid);
+      lp->stopped = 1;
 
-      linux_nat_switch_fork (inferior_ptid);
       check_for_thread_db ();
     }
 
@@ -1073,22 +1092,30 @@ iterate_over_lwps (ptid_t filter,
   return NULL;
 }
 
-/* Update our internal state when changing from one fork (checkpoint,
-   et cetera) to another indicated by NEW_PTID.  We can only switch
-   single-threaded applications, so we only create one new LWP, and
-   the previous list is discarded.  */
+/* Update our internal state when changing from one checkpoint to
+   another indicated by NEW_PTID.  We can only switch single-threaded
+   applications, so we only create one new LWP, and the previous list
+   is discarded.  */
 
 void
 linux_nat_switch_fork (ptid_t new_ptid)
 {
   struct lwp_info *lp;
 
-  init_lwp_list ();
+  purge_lwp_list (GET_PID (inferior_ptid));
+
   lp = add_lwp (new_ptid);
   lp->stopped = 1;
 
-  init_thread_list ();
-  add_thread_silent (new_ptid);
+  /* This changes the thread's ptid while preserving the gdb thread
+     num.  Also changes the inferior pid, while preserving the
+     inferior num.  */
+  thread_change_ptid (inferior_ptid, new_ptid);
+
+  /* We've just told GDB core that the thread changed target id, but,
+     in fact, it really is a different thread, with different register
+     contents.  */
+  registers_changed ();
 }
 
 /* Handle the exit of a single thread LP.  */
@@ -1815,6 +1842,34 @@ linux_handle_extended_wait (struct lwp_i
 
       ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
 
+      if (event == PTRACE_EVENT_FORK
+	  && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+	{
+	  struct fork_info *fp;
+
+	  /* Handle checkpointing by linux-fork.c here as a special
+	     case.  We don't want the follow-fork-mode or 'catch fork'
+	     to interfere with this.  */
+
+	  /* This won't actually modify the breakpoint list, but will
+	     physically remove the breakpoints from the child.  */
+	  detach_breakpoints (new_pid);
+
+	  /* Retain child fork in ptrace (stopped) state.  */
+	  fp = find_fork_pid (new_pid);
+	  if (!fp)
+	    fp = add_fork (new_pid);
+
+	  /* Report as spurious, so that infrun doesn't want to follow
+	     this fork.  We're actually doing an infcall in
+	     linux-fork.c.  */
+	  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+	  linux_enable_event_reporting (pid_to_ptid (new_pid));
+
+	  /* Report the stop to the core.  */
+	  return 0;
+	}
+
       if (event == PTRACE_EVENT_FORK)
 	ourstatus->kind = TARGET_WAITKIND_FORKED;
       else if (event == PTRACE_EVENT_VFORK)
@@ -4295,7 +4350,7 @@ linux_nat_supports_non_stop (void)
 /* True if we want to support multi-process.  To be removed when GDB
    supports multi-exec.  */
 
-int linux_multi_process = 0;
+int linux_multi_process = 1;
 
 static int
 linux_nat_supports_multi_process (void)
Index: src/gdb/linux-fork.c
===================================================================
--- src.orig/gdb/linux-fork.c	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/linux-fork.c	2009-07-01 19:17:08.000000000 +0100
@@ -52,7 +52,6 @@ struct fork_info
   struct regcache *savedregs;	/* Convenient for info fork, saves 
 				   having to actually switch contexts.  */
   int clobber_regs;		/* True if we should restore saved regs.  */
-  ULONGEST pc;			/* PC for info fork.  */
   off_t *filepos;		/* Set of open file descriptors' offsets.  */
   int maxfd;
 };
@@ -210,7 +209,6 @@ init_fork_list (void)
   for (fp = fork_list; fp; fp = fpnext)
     {
       fpnext = fp->next;
-      delete_inferior (ptid_get_pid (fp->ptid));
       free_fork (fp);
     }
 
@@ -240,9 +238,7 @@ fork_load_infrun_state (struct fork_info
   extern void nullify_last_target_wait_ptid ();
   int i;
 
-  inferior_ptid = fp->ptid;
-
-  linux_nat_switch_fork (inferior_ptid);
+  linux_nat_switch_fork (fp->ptid);
 
   if (fp->savedregs && fp->clobber_regs)
     regcache_cpy (get_current_regcache (), fp->savedregs);
@@ -268,7 +264,7 @@ fork_load_infrun_state (struct fork_info
 /* Save infrun state for the fork PTID.
    Exported for use by linux child_follow_fork.  */
 
-extern void
+static void
 fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
 {
   char path[MAXPATHLEN];
@@ -280,7 +276,6 @@ fork_save_infrun_state (struct fork_info
 
   fp->savedregs = regcache_dup (get_current_regcache ());
   fp->clobber_regs = clobber_regs;
-  fp->pc = regcache_read_pc (get_current_regcache ());
 
   if (clobber_regs)
     {
@@ -370,8 +365,6 @@ linux_fork_mourn_inferior (void)
      We need to delete that one from the fork_list, and switch
      to the next available fork.  */
   delete_fork (inferior_ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (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
@@ -402,8 +395,6 @@ linux_fork_detach (char *args, int from_
     error (_("Unable to detach %s"), target_pid_to_str (inferior_ptid));
 
   delete_fork (inferior_ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (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
@@ -429,14 +420,14 @@ delete_fork_command (char *args, int fro
   ptid_t ptid;
 
   if (!args || !*args)
-    error (_("Requires argument (fork/checkpoint id to delete)"));
+    error (_("Requires argument (checkpoint id to delete)"));
 
   ptid = fork_id_to_ptid (parse_and_eval_long (args));
   if (ptid_equal (ptid, minus_one_ptid))
-    error (_("No such fork/checkpoint id, %s"), args);
+    error (_("No such checkpoint id, %s"), args);
 
   if (ptid_equal (ptid, inferior_ptid))
-    error (_("Please switch to another fork/checkpoint before deleting the current one"));
+    error (_("Please switch to another checkpoint before deleting the current one"));
 
   if (ptrace (PTRACE_KILL, PIDGET (ptid), 0, 0))
     error (_("Unable to kill pid %s"), target_pid_to_str (ptid));
@@ -445,8 +436,6 @@ delete_fork_command (char *args, int fro
     printf_filtered (_("Killed %s\n"), target_pid_to_str (ptid));
 
   delete_fork (ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (ptid));
 }
 
 static void
@@ -455,14 +444,15 @@ detach_fork_command (char *args, int fro
   ptid_t ptid;
 
   if (!args || !*args)
-    error (_("Requires argument (fork id to detach)"));
+    error (_("Requires argument (checkpoint id to detach)"));
 
   ptid = fork_id_to_ptid (parse_and_eval_long (args));
   if (ptid_equal (ptid, minus_one_ptid))
-    error (_("No such fork id, %s"), args);
+    error (_("No such checkpoint id, %s"), args);
 
   if (ptid_equal (ptid, inferior_ptid))
-    error (_("Please switch to another fork before detaching the current one"));
+    error (_("\
+Please switch to another checkpoint before detaching the current one"));
 
   if (ptrace (PTRACE_DETACH, PIDGET (ptid), 0, 0))
     error (_("Unable to detach %s"), target_pid_to_str (ptid));
@@ -471,8 +461,6 @@ detach_fork_command (char *args, int fro
     printf_filtered (_("Detached %s\n"), target_pid_to_str (ptid));
 
   delete_fork (ptid);
-  /* Delete process from GDB's process table.  */
-  detach_inferior (ptid_get_pid (ptid));
 }
 
 /* Print information about currently known forks.  */
@@ -506,7 +494,7 @@ info_forks_command (char *arg, int from_
       else
 	{
 	  printf_filtered ("  ");
-	  pc = fp->pc;
+	  pc = regcache_read_pc (fp->savedregs);
 	}
       printf_filtered ("%d %s", fp->num, target_pid_to_str (fp->ptid));
       if (fp->num == 0)
@@ -546,25 +534,13 @@ info_forks_command (char *arg, int from_
     }
 }
 
-/* Save/restore mode variable 'detach_fork':
-   We need to temporarily take over this mode variable, while
-   preserving the user-specified state, and make sure that it 
-   gets restored in case of error.
+/* The PID of the process we're checkpointing.  */
+static int checkpointing_pid = 0;
 
-   The int pointer that we use comes from the caller, so we can
-   be called more than once (even though currently we don't need to).  */
-
-static void 
-restore_detach_fork (void *arg)
+int
+linux_fork_checkpointing_p (int pid)
 {
-  detach_fork = *(int *) arg;
-}
-
-static struct cleanup *
-save_detach_fork (int *saved_val)
-{
-  *saved_val = detach_fork;
-  return make_cleanup (restore_detach_fork, (void *) saved_val);
+  return (checkpointing_pid == pid);
 }
 
 static void
@@ -579,12 +555,6 @@ checkpoint_command (char *args, int from
   pid_t retpid;
   struct cleanup *old_chain;
   long i;
-  /* Make this temp var static, 'cause it's used in the error context.  */
-  static int temp_detach_fork;
-
-  /* Remove breakpoints, so that they are not inserted
-     in the forked process.  */
-  remove_breakpoints ();
 
   /* Make the inferior fork, record its (and gdb's) state.  */
 
@@ -598,8 +568,11 @@ checkpoint_command (char *args, int from
 
   gdbarch = get_objfile_arch (fork_objf);
   ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
-  old_chain = save_detach_fork (&temp_detach_fork);
-  detach_fork = 0;
+
+  /* Tell linux-nat.c that we're checkpointing this inferior.  */
+  old_chain = make_cleanup_restore_integer (&checkpointing_pid);
+  checkpointing_pid = PIDGET (inferior_ptid);
+
   ret = call_function_by_hand (fork_fn, 0, &ret);
   do_cleanups (old_chain);
   if (!ret)	/* Probably can't happen.  */
@@ -627,7 +600,6 @@ checkpoint_command (char *args, int from
   if (!fp)
     error (_("Failed to find new fork"));
   fork_save_infrun_state (fp, 1);
-  insert_breakpoints ();
 }
 
 static void
@@ -654,37 +626,7 @@ linux_fork_context (struct fork_info *ne
   print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
 }
 
-/* Switch inferior process (fork) context, by process id.  */
-static void
-process_command (char *args, int from_tty)
-{
-  struct fork_info *fp;
-
-  if (!args || !*args)
-    error (_("Requires argument (process id to switch to)"));
-
-  if ((fp = find_fork_pid (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: process id %s"), args);
-
-  linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by fork id.  */
-static void
-fork_command (char *args, int from_tty)
-{
-  struct fork_info *fp;
-
-  if (!args || !*args)
-    error (_("Requires argument (fork id to switch to)"));
-
-  if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: fork id %s"), args);
-
-  linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by checkpoint id.  */
+/* Switch inferior process (checkpoint) context, by checkpoint id.  */
 static void
 restart_command (char *args, int from_tty)
 {
@@ -725,9 +667,8 @@ Tells gdb whether to detach the child of
   add_com ("checkpoint", class_obscure, checkpoint_command, _("\
 Fork a duplicate process (experimental)."));
 
-  /* Restart command: restore the context of a specified fork
-     process.  May be used for "program forks" as well as for
-     "debugger forks" (checkpoints).  */
+  /* Restart command: restore the context of a specified checkpoint
+     process.  */
 
   add_com ("restart", class_obscure, restart_command, _("\
 restart <n>: restore program context from a checkpoint.\n\
@@ -737,36 +678,19 @@ Argument 'n' is checkpoint ID, as displa
      fork list.  */
 
   add_cmd ("checkpoint", class_obscure, delete_fork_command, _("\
-Delete a fork/checkpoint (experimental)."),
+Delete a checkpoint (experimental)."),
 	   &deletelist);
 
   /* Detach checkpoint command: release the process to run independently, 
      and remove it from the fork list.  */
 
   add_cmd ("checkpoint", class_obscure, detach_fork_command, _("\
-Detach from a fork/checkpoint (experimental)."),
+Detach from a checkpoint (experimental)."),
 	   &detachlist);
 
   /* Info checkpoints command: list all forks/checkpoints 
      currently under gdb's control.  */
 
   add_info ("checkpoints", info_forks_command,
-	    _("IDs of currently known forks/checkpoints."));
-
-  /* Command aliases (let "fork" and "checkpoint" be used 
-     interchangeably).  */
-
-  add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &deletelist);
-  add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &detachlist);
-  add_info_alias ("forks", "checkpoints", 0);
-
-  /* "fork <n>" (by analogy to "thread <n>").  */
-  add_com ("fork", class_obscure, fork_command, _("\
-fork <n>: Switch between forked processes.\n\
-Argument 'n' is fork ID, as displayed by 'info forks'."));
-
-  /* "process <proc id>" as opposed to "fork <fork id>".  */
-  add_com ("process", class_obscure, process_command, _("\
-process <pid>: Switch between forked processes.\n\
-Argument 'pid' is process ID, as displayed by 'info forks' or 'shell ps'."));
+	    _("IDs of currently known checkpoints."));
 }
Index: src/gdb/linux-fork.h
===================================================================
--- src.orig/gdb/linux-fork.h	2009-07-01 19:15:40.000000000 +0100
+++ src/gdb/linux-fork.h	2009-07-01 19:17:08.000000000 +0100
@@ -20,13 +20,11 @@
 struct fork_info;
 extern struct fork_info *add_fork (pid_t);
 extern struct fork_info *find_fork_pid (pid_t);
-extern void fork_save_infrun_state (struct fork_info *, int);
 extern void linux_fork_killall (void);
 extern void linux_fork_mourn_inferior (void);
 extern void linux_fork_detach (char *, int);
-extern int  forks_exist_p (void);
-
-struct fork_info *fork_list;
+extern int forks_exist_p (void);
+extern int linux_fork_checkpointing_p (int);
 
 extern int detach_fork;
 
Index: src/gdb/cli/cli-cmds.c
===================================================================
--- src.orig/gdb/cli/cli-cmds.c	2009-07-01 19:15:40.000000000 +0100
+++ src/gdb/cli/cli-cmds.c	2009-07-01 19:17:08.000000000 +0100
@@ -124,6 +124,10 @@ struct cmd_list_element *deletelist;
 
 struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands. */
+
+struct cmd_list_element *killlist;
+
 /* Chain containing all defined "enable breakpoint" subcommands. */
 
 struct cmd_list_element *enablebreaklist;
Index: src/gdb/cli/cli-cmds.h
===================================================================
--- src.orig/gdb/cli/cli-cmds.h	2009-07-01 19:15:40.000000000 +0100
+++ src/gdb/cli/cli-cmds.h	2009-07-01 19:17:08.000000000 +0100
@@ -41,6 +41,10 @@ extern struct cmd_list_element *deleteli
 
 extern struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands.  */
+
+extern struct cmd_list_element *killlist;
+
 /* Chain containing all defined toggle subcommands. */
 
 extern struct cmd_list_element *togglelist;
Index: src/gdb/gdbcmd.h
===================================================================
--- src.orig/gdb/gdbcmd.h	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/gdbcmd.h	2009-07-01 19:17:08.000000000 +0100
@@ -52,6 +52,10 @@ extern struct cmd_list_element *deleteli
 
 extern struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands.  */
+
+extern struct cmd_list_element *killlist;
+
 /* Chain containing all defined toggle subcommands.  */
 
 extern struct cmd_list_element *togglelist;
Index: src/gdb/inferior.c
===================================================================
--- src.orig/gdb/inferior.c	2009-07-01 19:15:40.000000000 +0100
+++ src/gdb/inferior.c	2009-07-01 19:17:08.000000000 +0100
@@ -25,6 +25,7 @@
 #include "gdbthread.h"
 #include "ui-out.h"
 #include "observer.h"
+#include "gdbthread.h"
 
 void _initialize_inferiors (void);
 
@@ -352,12 +353,114 @@ print_inferior (struct ui_out *uiout, in
   do_cleanups (old_chain);
 }
 
+static void
+detach_inferior_command (char *args, int from_tty)
+{
+  int num = -1;
+  int pid;
+  struct thread_info *tp;
+
+  if (!args || !*args)
+    error (_("Requires argument (inferior id to detach)"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  tp = any_thread_of_process (pid);
+  if (!tp)
+    error (_("Inferior has no threads."));
+
+  switch_to_thread (tp->ptid);
+
+  detach_command (NULL, from_tty);
+}
+
+static void
+kill_inferior_command (char *args, int from_tty)
+{
+  int num = -1;
+  int pid;
+  struct thread_info *tp;
+
+  if (!args || !*args)
+    error (_("Requires argument (inferior id to kill)"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  tp = any_thread_of_process (pid);
+  if (!tp)
+    error (_("Inferior has no threads."));
+
+  switch_to_thread (tp->ptid);
+
+  target_kill ();
+
+  bfd_cache_close_all ();
+}
+
+static void
+inferior_command (char *args, int from_tty)
+{
+  int num, pid;
+
+  if (!have_inferiors ())
+    error (_("No inferiors"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  if (pid != ptid_get_pid (inferior_ptid))
+    {
+      struct thread_info *tp;
+
+      tp = any_thread_of_process (pid);
+      if (!tp)
+	error (_("Inferior has no threads."));
+
+      switch_to_thread (tp->ptid);
+    }
+
+  printf_filtered (_("[Switching to thread %d (%s)] "),
+		   pid_to_thread_id (inferior_ptid),
+		   target_pid_to_str (inferior_ptid));
+
+  if (is_running (inferior_ptid))
+    ui_out_text (uiout, "(running)\n");
+  else
+    {
+      ui_out_text (uiout, "\n");
+      print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+    }
+}
+
 /* Print information about currently known inferiors.  */
 
 static void
-info_inferiors_command (char *arg, int from_tty)
+info_inferiors_command (char *args, int from_tty)
 {
-  print_inferior (uiout, -1);
+  int requested = -1;
+
+  if (args && *args)
+    {
+      requested = parse_and_eval_long (args);
+      if (!valid_gdb_inferior_id (requested))
+	error (_("Inferior ID %d not known."), requested);
+    }
+
+  print_inferior (uiout, requested);
 }
 
 /* Print notices when new inferiors are created and die.  */
@@ -381,4 +484,17 @@ Show printing of inferior events (e.g., 
          NULL,
          show_print_inferior_events,
          &setprintlist, &showprintlist);
+
+  add_cmd ("inferior", class_run, detach_inferior_command, _("\
+Detach from inferior ID."),
+	   &detachlist);
+
+  add_cmd ("inferior", class_run, kill_inferior_command, _("\
+Kill inferior ID."),
+	   &killlist);
+
+  add_cmd ("inferior", class_run, inferior_command, _("\
+Use this command to switch between inferiors.\n\
+The new inferior ID must be currently known."),
+	   &cmdlist);
 }
Index: src/gdb/testsuite/gdb.base/multi-forks.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/multi-forks.exp	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/testsuite/gdb.base/multi-forks.exp	2009-07-01 19:17:08.000000000 +0100
@@ -140,6 +140,19 @@ gdb_test "print pids\[0\]==0 || pids\[1\
 # Now test with detach-on-fork off.
 #
 
+# detach-on-fork isn't implemented on hpux.
+#
+if {![istarget "*-*-linux*"]} then {
+    continue
+}
+
+# Start with a fresh gdb
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
 runto_main
 gdb_breakpoint $exit_bp_loc
 
@@ -152,60 +165,59 @@ gdb_test "set detach off" "" "set detach
 
 #
 # We will now run every fork up to the exit bp, 
-# eventually winding up with 16 forks.
+# eventually winding up with 16 inferiors.
 #
 
 for {set i 1} {$i <= 15} {incr i} {
   gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit $i"
-  gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork $i"
-  gdb_test "restart $i" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
-      "restart $i"
+  gdb_test "info inferior" " 5 .* 4 .* 3 .* 2 .*" "info inferior $i"
+  gdb_test "inferior $i + 1" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
+      "inferior $i"
 }
 
 gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit 16"
-gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork 16"
-gdb_test "restart 0" " main .*" "restart final"
+gdb_test "info inferiors" " 5 .* 4 .* 3 .* 2 .*" "info inferior 16"
+gdb_test "inferior 2" " main .*" "restart final"
 
 #
 # Now we should examine all the pids.
 #
 
 # 
-# Test detach fork
+# Test detach inferior
 # 
 
-# [assumes we're at #0]
-gdb_test "detach fork 1" "Detached .*" "Detach 1"
-gdb_test "detach fork 2" "Detached .*" "Detach 2"
-gdb_test "detach fork 3" "Detached .*" "Detach 3"
-gdb_test "detach fork 4" "Detached .*" "Detach 4"
+# [assumes we're at #1]
+gdb_test "detach inferior 2" "Detaching .*" "Detach 2"
+gdb_test "detach inferior 3" "Detaching .*" "Detach 3"
+gdb_test "detach inferior 4" "Detaching .*" "Detach 4"
+gdb_test "detach inferior 5" "Detaching .*" "Detach 5"
 
 # 
-# Test delete fork
-# 
+# Test kill inferior
+#
 
-gdb_test "delete fork 5" "" "Delete 5"
-gdb_test "info fork 5"   "No fork number 5." "Did delete 5"
-gdb_test "delete fork 6" "" "Delete 6"
-gdb_test "info fork 6"   "No fork number 6." "Did delete 6"
-gdb_test "delete fork 7" "" "Delete 7"
-gdb_test "info fork 7"   "No fork number 7." "Did delete 7"
-gdb_test "delete fork 8" "" "Delete 8"
-gdb_test "info fork 8"   "No fork number 8." "Did delete 8"
-gdb_test "delete fork 9" "" "Delete 9"
-gdb_test "info fork 9"   "No fork number 9." "Did delete 9"
-gdb_test "delete fork 10" "" "Delete 10"
-gdb_test "info fork 10"   "No fork number 10." "Did delete 10"
-gdb_test "delete fork 11" "" "Delete 11"
-gdb_test "info fork 11"   "No fork number 11." "Did delete 11"
-gdb_test "delete fork 12" "" "Delete 12"
-gdb_test "info fork 12"   "No fork number 12." "Did delete 12"
-gdb_test "delete fork 13" "" "Delete 13"
-gdb_test "info fork 13"   "No fork number 13." "Did delete 13"
-gdb_test "delete fork 14" "" "Delete 14"
-gdb_test "info fork 14"   "No fork number 14." "Did delete 14"
-gdb_test "delete fork 15" "" "Delete 15"
-gdb_test "info fork 15"   "No fork number 15." "Did delete 15"
+gdb_test "kill inferior 6" "" "Kill 6"
+gdb_test "info inferior 6" "Inferior ID 6 not known." "Did kill 6"
+gdb_test "kill inferior 7" "" "Kill 7"
+gdb_test "info inferior 7" "Inferior ID 7 not known." "Did kill 7"
+gdb_test "kill inferior 8" "" "Kill 8"
+gdb_test "info inferior 8" "Inferior ID 8 not known." "Did kill 8"
+gdb_test "kill inferior 9" "" "Kill 9"
+gdb_test "info inferior 9" "Inferior ID 9 not known." "Did kill 9"
+gdb_test "kill inferior 10" "" "Kill 10"
+gdb_test "info inferior 10" "Inferior ID 10 not known." "Did kill 10"
+gdb_test "kill inferior 11" "" "Kill 11"
+gdb_test "info inferior 11" "Inferior ID 11 not known." "Did kill 11"
+gdb_test "kill inferior 12" "" "Kill 12"
+gdb_test "info inferior 12" "Inferior ID 12 not known." "Did kill 12"
+gdb_test "kill inferior 13" "" "Kill 13"
+gdb_test "info inferior 13" "Inferior ID 13 not known." "Did kill 13"
+gdb_test "kill inferior 14" "" "Kill 14"
+gdb_test "info inferior 14" "Inferior ID 14 not known." "Did kill 14"
+gdb_test "kill inferior 15" "" "Kill 15"
+gdb_test "info inferior 15" "Inferior ID 15 not known." "Did kill 15"
+gdb_test "kill inferior 16" "" "Kill 16"
+gdb_test "info inferior 16" "Inferior ID 16 not known." "Did kill 16"
 
 return 0
-
Index: src/gdb/testsuite/gdb.base/ending-run.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/ending-run.exp	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/testsuite/gdb.base/ending-run.exp	2009-07-01 19:17:08.000000000 +0100
@@ -71,7 +71,7 @@ gdb_test "b ending-run.c:14" ".*Note.*al
 gdb_test "cle ending-run.c:14" \
 	".*Deleted breakpoint 5.*" "Cleared 2 by line"
 
-send_gdb "inf line ending-run.c:14\n"
+send_gdb "info line ending-run.c:14\n"
 gdb_expect {
     -re ".*address (0x\[0-9a-fA-F]*).*$gdb_prompt $" {
         set line_nine $expect_out(1,string)
Index: src/gdb/testsuite/gdb.base/help.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/help.exp	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/testsuite/gdb.base/help.exp	2009-07-01 19:17:08.000000000 +0100
@@ -300,7 +300,9 @@ gdb_test "help inspect" "Same as \"print
 # test help jump
 gdb_test "help jump" "Continue program being debugged at specified line or address\.\[\r\n\]+Give as argument either LINENUM or \[*\]+ADDR, where ADDR is an expression\[\r\n\]+for an address to start at\." "help jump"
 # test help kill
-gdb_test "help kill" "Kill execution of program being debugged\." "help kill"
+test_prefix_command_help "kill" {
+    "Kill execution of program being debugged\.\[\r\n\]+"
+}
 # test help list "l" abbreviation
 gdb_test "help l" "List specified function or line\.\[\r\n\]+With no argument, lists ten more lines after or around previous listing\.\[\r\n\]+\"list -\" lists the ten lines before a previous ten-line listing\.\[\r\n\]+One argument specifies a line, and ten lines are listed around that line\.\[\r\n\]+Two arguments with comma between specify starting and ending lines to list\.\[\r\n\]+Lines can be specified in these ways:\[\r\n\]+  LINENUM, to list around that line in current file,\[\r\n\]+  FILE:LINENUM, to list around that line in that file,\[\r\n\]+  FUNCTION, to list around beginning of that function,\[\r\n\]+  FILE:FUNCTION, to distinguish among like-named static functions\.\[\r\n\]+  \[*\]ADDRESS, to list around the line containing that address\.\[\r\n\]+With two args if one is empty it stands for ten lines away from the other arg\." "help list \"l\" abbreviation"
 # test help list
Index: src/gdb/infcmd.c
===================================================================
--- src.orig/gdb/infcmd.c	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/infcmd.c	2009-07-01 19:17:08.000000000 +0100
@@ -2690,8 +2690,9 @@ fully linked executable files and separa
 	       &showlist);
   set_cmd_completer (c, noop_completer);
 
-  add_com ("kill", class_run, kill_command,
-	   _("Kill execution of program being debugged."));
+  add_prefix_cmd ("kill", class_run, kill_command,
+		  _("Kill execution of program being debugged."),
+		  &killlist, "kill ", 0, &cmdlist);
 
   add_com ("attach", class_run, attach_command, _("\
 Attach to a process or file outside of GDB.\n\
Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/gdbthread.h	2009-07-01 19:17:08.000000000 +0100
@@ -243,6 +243,9 @@ struct thread_info *find_thread_id (int 
    returns the first thread in the list.  */
 struct thread_info *first_thread_of_process (int pid);
 
+/* Returns any thread of process PID.  */
+extern struct thread_info *any_thread_of_process (int pid);
+
 /* Change the ptid of thread OLD_PTID to NEW_PTID.  */
 void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
 
Index: src/gdb/thread.c
===================================================================
--- src.orig/gdb/thread.c	2009-07-01 19:15:39.000000000 +0100
+++ src/gdb/thread.c	2009-07-01 19:17:08.000000000 +0100
@@ -428,6 +428,18 @@ first_thread_of_process (int pid)
   return ret;
 }
 
+struct thread_info *
+any_thread_of_process (int pid)
+{
+  struct thread_info *tp;
+
+  for (tp = thread_list; tp; tp = tp->next)
+    if (ptid_get_pid (tp->ptid) == pid)
+      return tp;
+
+  return NULL;
+}
+
 /* Print a list of thread ids currently known, and the total number of
    threads. To be used from within catch_errors. */
 static int
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo	2009-07-01 19:15:40.000000000 +0100
+++ src/gdb/doc/gdb.texinfo	2009-07-01 19:17:08.000000000 +0100
@@ -2374,7 +2374,39 @@ To find out what inferiors exist at any 
 @kindex info inferiors
 @item info inferiors
 Print a list of all inferiors currently being managed by @value{GDBN}.
+@end table
+
+To switch focus between inferiors, use the @code{inferior} command:
+
+@table @code
+@kindex inferior @var{inferior-id}
+@item inferior @var{inferior-id}
+Make inferior number @var{inferior-id} the current inferior.  The
+argument @var{inferior-id} is the internal inferior number assigned by
+@value{GDBN}, as shown in the first field of the @samp{info inferiors}
+display.
+@end table
+
+To quit debugging one of the inferiors, you can either detach from it
+by using the @w{@code{detach inferior}} command (allowing it to run
+independently), or kill it using the @w{@code{kill inferior}} command:
+
+@table @code
+@kindex detach inferior @var{inferior-id}
+@item detach inferior @var{inferior-id}
+Detach from the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+
+@kindex kill inferior @var{inferior-id}
+@item kill inferior @var{inferior-id}
+Kill the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+@end table
+
+To be notified when inferiors are started or exit under @value{GDBN}'s
+control use @w{@code{set print inferior-events}}:
 
+@table @code
 @kindex set print inferior-events
 @cindex print messages on inferior start and exit
 @item set print inferior-events
@@ -2758,51 +2790,17 @@ is held suspended.  
 Show whether detach-on-fork mode is on/off.
 @end table
 
-If you choose to set @samp{detach-on-fork} mode off, then
-@value{GDBN} will retain control of all forked processes (including
-nested forks).  You can list the forked processes under the control of
-@value{GDBN} by using the @w{@code{info forks}} command, and switch
-from one fork to another by using the @w{@code{fork}} command.
-
-@table @code
-@kindex info forks
-@item info forks
-Print a list of all forked processes under the control of @value{GDBN}.
-The listing will include a fork id, a process id, and the current 
-position (program counter) of the process.
-
-@kindex fork @var{fork-id}
-@item fork @var{fork-id}
-Make fork number @var{fork-id} the current process.  The argument
-@var{fork-id} is the internal fork number assigned by @value{GDBN},
-as shown in the first field of the @samp{info forks} display.
-
-@kindex process @var{process-id}
-@item process @var{process-id}
-Make process number @var{process-id} the current process.  The
-argument @var{process-id} must be one that is listed in the output of
-@samp{info forks}.
-
-@end table
+If you choose to set @samp{detach-on-fork} mode off, then @value{GDBN}
+will retain control of all forked processes (including nested forks).
+You can list the forked processes under the control of @value{GDBN} by
+using the @w{@code{info inferiors}} command, and switch from one fork
+to another by using the @code{inferior} command (@pxref{Inferiors,
+,Debugging Multiple Inferiors}).
 
 To quit debugging one of the forked processes, you can either detach
-from it by using the @w{@code{detach fork}} command (allowing it to
-run independently), or delete (and kill) it using the
-@w{@code{delete fork}} command.
-
-@table @code
-@kindex detach fork @var{fork-id}
-@item detach fork @var{fork-id}
-Detach from the process identified by @value{GDBN} fork number
-@var{fork-id}, and remove it from the fork list.  The process will be
-allowed to run independently.
-
-@kindex delete fork @var{fork-id}
-@item delete fork @var{fork-id}
-Kill the process identified by @value{GDBN} fork number @var{fork-id},
-and remove it from the fork list.
-
-@end table
+from it by using the @w{@code{detach inferior}} command (allowing it
+to run independently), or kill it using the @w{@code{kill inferior}}
+command.  Again, @pxref{Inferiors, ,Debugging Multiple Inferiors}.
 
 If you ask to debug a child process and a @code{vfork} is followed by an
 @code{exec}, @value{GDBN} executes the new target up to the first
@@ -2810,8 +2808,8 @@ breakpoint in the new target.  If you ha
 @code{main} in your original program, the breakpoint will also be set on
 the child process's @code{main}.
 
-When a child process is spawned by @code{vfork}, you cannot debug the
-child or parent until an @code{exec} call completes.
+On some systems, when a child process is spawned by @code{vfork}, you
+cannot debug the child or parent until an @code{exec} call completes.
 
 If you issue a @code{run} command to @value{GDBN} after an @code{exec}
 call executes, the new target restarts.  To restart the parent process,
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS	2009-07-01 19:16:33.000000000 +0100
+++ src/gdb/NEWS	2009-07-01 19:17:08.000000000 +0100
@@ -197,6 +197,18 @@ macro undef
 info os processes
   Show operating system information about processes.
 
+info inferiors
+  List the inferiors currently under GDB's control.
+
+inferior NUM
+  Switch focus to inferior number NUM.
+
+detach inferior NUM
+  Detach from inferior number NUM.
+
+kill inferior NUM
+  Kill inferior number NUM.
+
 * New options
 
 set sh calling-convention
@@ -320,6 +332,38 @@ show schedule-multiple
   Allow GDB to resume all threads of all processes or only threads of
   the current process.
 
+* Removed commands
+
+info forks
+
+  For program forks, this is replaced by the new more generic `info
+  inferiors' command.  To list checkpoints, you can still use the
+  `info checkpoints' command, which was an alias for the `info forks'
+  command.
+
+fork NUM
+
+  Replaced by the new `inferior' command.  To switch between
+  checkpoints, you can still use the `restart' command, which was an
+  alias for the `fork' command.
+
+process PID
+  Removed.
+
+delete fork NUM
+
+  For program forks, this is replaced by the new more generic `kill
+  inferior' command.  To delete a checkpoint, you can still use the
+  `delete checkpoint' command, which was an alias for the `delete
+  fork' command.
+
+detach fork NUM
+
+  For program forks, this is replaced by the new more generic `detach
+  inferior' command.  To detach a checkpoint, you can still use the
+  `detach checkpoint' command, which was an alias for the `detach
+  fork' command.
+
 * New native configurations
 
 x86/x86_64 Darwin		i[34567]86-*-darwin*


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-07-01 18:27   ` Pedro Alves
@ 2009-07-01 19:16     ` Eli Zaretskii
  2009-07-02 14:32       ` Pedro Alves
  0 siblings, 1 reply; 21+ messages in thread
From: Eli Zaretskii @ 2009-07-01 19:16 UTC (permalink / raw)
  To: Pedro Alves; +Cc: msnyder, gdb-patches

> From: Pedro Alves <pedro@codesourcery.com>
> Date: Wed, 1 Jul 2009 19:27:59 +0100
> Cc: "gdb-patches@sourceware.org" <gdb-patches@sourceware.org>,
>  Eli Zaretskii <eliz@gnu.org>
> 
> Eli, what do you think of this (and of the docs changes)?

I'm fine with it, but I have a couple of comments about the docs.

> +from it by using the @w{@code{detach inferior}} command (allowing it
> +to run independently), or kill it using the @w{@code{kill inferior}}
> +command.  Again, @pxref{Inferiors, ,Debugging Multiple Inferiors}.

@pxref is inappropriate here, since it is designed to be used inside
parentheses.  You want "see @ref" instead.

And I would lose the "Again" part (in which case you can simply use
@xref).

> +info inferiors
> +  List the inferiors currently under GDB's control.
> +
> +inferior NUM
> +  Switch focus to inferior number NUM.
> +
> +detach inferior NUM
> +  Detach from inferior number NUM.
> +
> +kill inferior NUM
> +  Kill inferior number NUM.

I think this deserves a short intro, like one or two sentences saying
something about multi-inferior debugging.

> +process PID
> +  Removed.

Perhaps say something about replacement command(s), or why none is
needed.

Thanks.


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-07-01 19:16     ` Eli Zaretskii
@ 2009-07-02 14:32       ` Pedro Alves
  2009-07-02 19:00         ` Eli Zaretskii
  0 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2009-07-02 14:32 UTC (permalink / raw)
  To: gdb-patches, Eli Zaretskii; +Cc: msnyder

On Wednesday 01 July 2009 20:15:51, Eli Zaretskii wrote:
> > From: Pedro Alves <pedro@codesourcery.com>

> > +from it by using the @w{@code{detach inferior}} command (allowing it
> > +to run independently), or kill it using the @w{@code{kill inferior}}
> > +command.  Again, @pxref{Inferiors, ,Debugging Multiple Inferiors}.
> 
> @pxref is inappropriate here, since it is designed to be used inside
> parentheses.  You want "see @ref" instead.
> 
> And I would lose the "Again" part (in which case you can simply use
> @xref).

Thanks.  I've read about @ref vs @xref vs @pxref now.  Always
learning.

> > +info inferiors
> > +  List the inferiors currently under GDB's control.
> > +
> > +inferior NUM
> > +  Switch focus to inferior number NUM.
> > +
> > +detach inferior NUM
> > +  Detach from inferior number NUM.
> > +
> > +kill inferior NUM
> > +  Kill inferior number NUM.
> 
> I think this deserves a short intro, like one or two sentences saying
> something about multi-inferior debugging.

Okay.  Let me know what you think of the new hunks (gdb.texinfo and NEWS)
below.

> > +process PID
> > +  Removed.
> 
> Perhaps say something about replacement command(s), or why none is
> needed.

Indeed.  Sorry I had to make you point at that.  :-)

-- 
Pedro Alves

Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo	2009-07-01 19:15:40.000000000 +0100
+++ src/gdb/doc/gdb.texinfo	2009-07-02 14:40:05.000000000 +0100
@@ -2374,7 +2374,39 @@ To find out what inferiors exist at any 
 @kindex info inferiors
 @item info inferiors
 Print a list of all inferiors currently being managed by @value{GDBN}.
+@end table
+
+To switch focus between inferiors, use the @code{inferior} command:
+
+@table @code
+@kindex inferior @var{inferior-id}
+@item inferior @var{inferior-id}
+Make inferior number @var{inferior-id} the current inferior.  The
+argument @var{inferior-id} is the internal inferior number assigned by
+@value{GDBN}, as shown in the first field of the @samp{info inferiors}
+display.
+@end table
+
+To quit debugging one of the inferiors, you can either detach from it
+by using the @w{@code{detach inferior}} command (allowing it to run
+independently), or kill it using the @w{@code{kill inferior}} command:
+
+@table @code
+@kindex detach inferior @var{inferior-id}
+@item detach inferior @var{inferior-id}
+Detach from the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+
+@kindex kill inferior @var{inferior-id}
+@item kill inferior @var{inferior-id}
+Kill the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+@end table
+
+To be notified when inferiors are started or exit under @value{GDBN}'s
+control use @w{@code{set print inferior-events}}:
 
+@table @code
 @kindex set print inferior-events
 @cindex print messages on inferior start and exit
 @item set print inferior-events
@@ -2758,51 +2790,17 @@ is held suspended.  
 Show whether detach-on-fork mode is on/off.
 @end table
 
-If you choose to set @samp{detach-on-fork} mode off, then
-@value{GDBN} will retain control of all forked processes (including
-nested forks).  You can list the forked processes under the control of
-@value{GDBN} by using the @w{@code{info forks}} command, and switch
-from one fork to another by using the @w{@code{fork}} command.
-
-@table @code
-@kindex info forks
-@item info forks
-Print a list of all forked processes under the control of @value{GDBN}.
-The listing will include a fork id, a process id, and the current 
-position (program counter) of the process.
-
-@kindex fork @var{fork-id}
-@item fork @var{fork-id}
-Make fork number @var{fork-id} the current process.  The argument
-@var{fork-id} is the internal fork number assigned by @value{GDBN},
-as shown in the first field of the @samp{info forks} display.
-
-@kindex process @var{process-id}
-@item process @var{process-id}
-Make process number @var{process-id} the current process.  The
-argument @var{process-id} must be one that is listed in the output of
-@samp{info forks}.
-
-@end table
+If you choose to set @samp{detach-on-fork} mode off, then @value{GDBN}
+will retain control of all forked processes (including nested forks).
+You can list the forked processes under the control of @value{GDBN} by
+using the @w{@code{info inferiors}} command, and switch from one fork
+to another by using the @code{inferior} command (@pxref{Inferiors,
+,Debugging Multiple Inferiors}).
 
 To quit debugging one of the forked processes, you can either detach
-from it by using the @w{@code{detach fork}} command (allowing it to
-run independently), or delete (and kill) it using the
-@w{@code{delete fork}} command.
-
-@table @code
-@kindex detach fork @var{fork-id}
-@item detach fork @var{fork-id}
-Detach from the process identified by @value{GDBN} fork number
-@var{fork-id}, and remove it from the fork list.  The process will be
-allowed to run independently.
-
-@kindex delete fork @var{fork-id}
-@item delete fork @var{fork-id}
-Kill the process identified by @value{GDBN} fork number @var{fork-id},
-and remove it from the fork list.
-
-@end table
+from it by using the @w{@code{detach inferior}} command (allowing it
+to run independently), or kill it using the @w{@code{kill inferior}}
+command.  @xref{Inferiors, ,Debugging Multiple Inferiors}.
 
 If you ask to debug a child process and a @code{vfork} is followed by an
 @code{exec}, @value{GDBN} executes the new target up to the first
@@ -2810,8 +2808,8 @@ breakpoint in the new target.  If you ha
 @code{main} in your original program, the breakpoint will also be set on
 the child process's @code{main}.
 
-When a child process is spawned by @code{vfork}, you cannot debug the
-child or parent until an @code{exec} call completes.
+On some systems, when a child process is spawned by @code{vfork}, you
+cannot debug the child or parent until an @code{exec} call completes.
 
 If you issue a @code{run} command to @value{GDBN} after an @code{exec}
 call executes, the new target restarts.  To restart the parent process,
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS	2009-07-01 19:16:33.000000000 +0100
+++ src/gdb/NEWS	2009-07-02 15:31:35.000000000 +0100
@@ -175,6 +175,16 @@ GDB will now correctly handle all of:
 * Support for user-defined prefixed commands.  The "define" command can
 add new commands to existing prefixes, e.g. "target".
 
+* Multi-inferior, multi-process debugging.
+
+  GDB now has generalized support for multi-inferior debugging.  See
+  "Debugging Multiple Inferiors" in the manual for more information.
+  Although support availability still depends on target support, the
+  command set has been uniformalised.  The GNU/Linux specific
+  multi-forks support has been migrated to this new framework.  This
+  implied some user visible changes; see "New commands" and also
+  "Removed commands" below.
+
 * New commands (for set/show, see "New options" below)
 
 find [/size-char] [/max-count] start-address, end-address|+search-space-size,
@@ -197,6 +207,18 @@ macro undef
 info os processes
   Show operating system information about processes.
 
+info inferiors
+  List the inferiors currently under GDB's control.
+
+inferior NUM
+  Switch focus to inferior number NUM.
+
+detach inferior NUM
+  Detach from inferior number NUM.
+
+kill inferior NUM
+  Kill inferior number NUM.
+
 * New options
 
 set sh calling-convention
@@ -320,6 +342,36 @@ show schedule-multiple
   Allow GDB to resume all threads of all processes or only threads of
   the current process.
 
+* Removed commands
+
+info forks
+  For program forks, this is replaced by the new more generic `info
+  inferiors' command.  To list checkpoints, you can still use the
+  `info checkpoints' command, which was an alias for the `info forks'
+  command.
+
+fork NUM
+  Replaced by the new `inferior' command.  To switch between
+  checkpoints, you can still use the `restart' command, which was an
+  alias for the `fork' command.
+
+process PID
+  This is removed, since some targets don't have a notion of
+  processes.  To switch between processes, you can still use the
+  `inferior' command using GDB's own inferior number.
+
+delete fork NUM
+  For program forks, this is replaced by the new more generic `kill
+  inferior' command.  To delete a checkpoint, you can still use the
+  `delete checkpoint' command, which was an alias for the `delete
+  fork' command.
+
+detach fork NUM
+  For program forks, this is replaced by the new more generic `detach
+  inferior' command.  To detach a checkpoint, you can still use the
+  `detach checkpoint' command, which was an alias for the `detach
+  fork' command.
+
 * New native configurations
 
 x86/x86_64 Darwin		i[34567]86-*-darwin*


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-07-02 14:32       ` Pedro Alves
@ 2009-07-02 19:00         ` Eli Zaretskii
  2009-07-02 21:59           ` Pedro Alves
  0 siblings, 1 reply; 21+ messages in thread
From: Eli Zaretskii @ 2009-07-02 19:00 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gdb-patches, msnyder

> From: Pedro Alves <pedro@codesourcery.com>
> Date: Thu, 2 Jul 2009 15:33:31 +0100
> Cc: msnyder@vmware.com
> 
> Okay.  Let me know what you think of the new hunks (gdb.texinfo and NEWS)
> below.

gdb.texinfo is fine.  I have a couple of comments for NEWS:

> +  Although support availability still depends on target support, the

"support" used twice in the same sentence.  How about losing the first
instance?

> +  command set has been uniformalised.  The GNU/Linux specific
                          ^^^^^^^^^^^^^
We use the US spelling, with a z, not s.  But I think "command set is
more uniform now" is a better way of saying the same and avoid the
problem in the first place.

Okay with those changes.

Thanks.


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: Move the multi-forks support to the generic multi-inferiors support.
  2009-07-02 19:00         ` Eli Zaretskii
@ 2009-07-02 21:59           ` Pedro Alves
  0 siblings, 0 replies; 21+ messages in thread
From: Pedro Alves @ 2009-07-02 21:59 UTC (permalink / raw)
  To: gdb-patches, Eli Zaretskii; +Cc: msnyder

On Thursday 02 July 2009 19:59:44, Eli Zaretskii wrote:
> > From: Pedro Alves <pedro@codesourcery.com>
> > Date: Thu, 2 Jul 2009 15:33:31 +0100
> > Cc: msnyder@vmware.com
> > 
> > Okay.  Let me know what you think of the new hunks (gdb.texinfo and NEWS)
> > below.
> 
> gdb.texinfo is fine.  I have a couple of comments for NEWS:
> 
> > +  Although support availability still depends on target support, the
> 
> "support" used twice in the same sentence.  How about losing the first
> instance?

Perfect.  Thanks.

> 
> > +  command set has been uniformalised.  The GNU/Linux specific
>                           ^^^^^^^^^^^^^
> We use the US spelling, with a z, not s.  

I never know which is which.  :-)

> But I think "command set is 
> more uniform now" is a better way of saying the same and avoid the
> problem in the first place.

Perfect.

> Okay with those changes.

Thank you.  Applied with those changes, as below.

-- 
Pedro Alves

gdb/
2009-07-02  Pedro Alves  <pedro@codesourcery.com>

	* linux-nat.c (linux_child_follow_fork): If we're staying attached
	to the child process, enable event reporting on it.  Don't handle
	checkpoints here.  Instead, add the child fork to the lwp thread
	and inferior lists without clobbering the previous inferior.  Let
	the thread_db layer learn about a new child process, even if
	following the parent.
	(linux_nat_switch_fork): Delete lwps of the current inferior only,
	instead of clearing the whole list.  Use thread_change_ptid to
	give the core the illusion the new checkpoint is still the same
	inferior.  Clear the register cache.
	(linux_handle_extended_wait): Handle checkpoints here.
	(linux_multi_process): Turn on.
	* linux-fork.c (struct fork_info) <pc>: Remove field.
	(init_fork_list): Do not delete the checkpoint from the inferior
	list (it is not there).
	(fork_load_infrun_state): Don't switch inferior_ptid here.  Pass
	the new checkpoint's ptid to linux_nat_switch_fork.
	(fork_save_infrun_state): Make static.  Don't stop the pc field of
	fork_info, it's gone.
	(linux_fork_mourn_inferior): Don't delete the checkpoint from the
	inferior list, it's not there.
	(linux_fork_detach): Ditto.
	(delete_fork_command): Replace mention of fork/checkpoint by
	checkpoint only.
	(detach_fork_command): Likewise.  Don't delete the checkpoint from
	the inferior list.
	(info_forks_command): Adjust.
	(restore_detach_fork): Delete.
	(checkpointing_pid): New.
	(linux_fork_checkpointing_p): New.
	(save_detach_fork): Delete.
	(checkpoint_command): Delete temp_detach_fork.  Don't remove
	breakpoints, that's a nop.  Store the pid of the process we're
	checkpointing, and use make_cleanup_restore_integer to restore it.
	Don't reinsert breakpoints here.
	(process_command, fork_command): Delete.
	(restart_command): Update comments to only mention checkpoints,
	not forks.
	(_initialize_linux_fork): Delete "fork", "process", "info forks"
	commands.
	* linux-fork.h (fork_save_infrun_state, fork_list): Delete
	declarations.
	(linux_fork_checkpointing_p): Declare.
	* cli/cli-cmds.c (killlist): New.
	* cli/cli-cmds.h (killlist): Declare.
	* gdbcmd.h (killlist): Declare.
	* inferior.c: Include "gdbthread.h".
	(detach_inferior_command, kill_inferior_command)
	(inferior_command): New.
	(info_inferiors_command): Allow specifying a specific inferior id.
	(_initialize_inferiors): Register "inferior", "kill inferior" and
	"detach inferior" commands.
	* infcmd.c (_initialize_infcmd): Make "kill" a prefix command.
	* gdbthread.h (any_thread_of_process): Declare.
	* thread.c (any_thread_of_process): New.

	* NEWS: Mention multi-inferior debugging.  Mention 'info
	inferiors', 'inferior', 'detach inferior' and 'kill inferior' as
	new commands.
	(Removed commands): New section, mentioning that 'info forks',
	'fork', 'process', 'delete fork' and 'detach fork' are now gone.

gdb/testsuite/
2009-07-02  Pedro Alves  <pedro@codesourcery.com>

	* gdb.base/multi-forks.exp: Only run detach-on-fork tests on
	linux.  Adjust to use "inferior", "info inferiors", "detach
	inferior" and "kill inferior" instead of "restart", "info fork",
	"detach fork" and "delete fork".
	* gdb.base/ending-run.exp: Spell out "info".
	* gdb.base/help.exp: Adjust to use test_prefix_command_help for
	the "kill" command.

gdb/doc/
2009-07-02  Pedro Alves  <pedro@codesourcery.com>

	* gdb.texinfo (Debugging multiple inferiors): Document the
	"inferior", "detach inferior" and "kill inferior" commands.
	(Debugging Programs with Multiple Processes): Adjust to mention
	generic "inferior" commands.  Delete mention of "detach fork" and
	"delete fork".  Cross reference to "Debugging multiple inferiors"
	section.

	
---
 gdb/NEWS                               |   52 +++++++++++++
 gdb/cli/cli-cmds.c                     |    4 +
 gdb/cli/cli-cmds.h                     |    4 +
 gdb/doc/gdb.texinfo                    |   88 ++++++++++------------
 gdb/gdbcmd.h                           |    4 +
 gdb/gdbthread.h                        |    3 
 gdb/infcmd.c                           |    5 -
 gdb/inferior.c                         |  118 +++++++++++++++++++++++++++++
 gdb/linux-fork.c                       |  132 +++++++--------------------------
 gdb/linux-fork.h                       |    6 -
 gdb/linux-nat.c                        |  115 +++++++++++++++++++++-------
 gdb/testsuite/gdb.base/ending-run.exp  |    2 
 gdb/testsuite/gdb.base/help.exp        |    4 -
 gdb/testsuite/gdb.base/multi-forks.exp |   86 ++++++++++++---------
 gdb/thread.c                           |   12 +++
 15 files changed, 409 insertions(+), 226 deletions(-)

Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c	2009-07-02 21:10:26.000000000 +0100
+++ src/gdb/linux-nat.c	2009-07-02 22:47:48.000000000 +0100
@@ -338,6 +338,12 @@ static int stop_callback (struct lwp_inf
 
 static void block_child_signals (sigset_t *prev_mask);
 static void restore_child_signals_mask (sigset_t *prev_mask);
+
+struct lwp_info;
+static struct lwp_info *add_lwp (ptid_t ptid);
+static void purge_lwp_list (int pid);
+static struct lwp_info *find_lwp_pid (ptid_t ptid);
+
 \f
 /* Trivial list manipulation functions to keep track of a list of
    new stopped processes.  */
@@ -587,6 +593,9 @@ linux_child_follow_fork (struct target_o
     parent_pid = ptid_get_pid (inferior_ptid);
   child_pid = PIDGET (inferior_thread ()->pending_follow.value.related_pid);
 
+  if (!detach_fork)
+    linux_enable_event_reporting (pid_to_ptid (child_pid));
+
   if (! follow_child)
     {
       /* We're already attached to the parent, by default. */
@@ -617,8 +626,9 @@ linux_child_follow_fork (struct target_o
 	}
       else
 	{
-	  struct fork_info *fp;
 	  struct inferior *parent_inf, *child_inf;
+	  struct lwp_info *lp;
+	  struct cleanup *old_chain;
 
 	  /* Add process to GDB's tables.  */
 	  child_inf = add_inferior (child_pid);
@@ -627,11 +637,16 @@ linux_child_follow_fork (struct target_o
 	  child_inf->attach_flag = parent_inf->attach_flag;
 	  copy_terminal_info (child_inf, parent_inf);
 
-	  /* Retain child fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (child_pid);
-	  if (!fp)
-	    fp = add_fork (child_pid);
-	  fork_save_infrun_state (fp, 0);
+	  old_chain = save_inferior_ptid ();
+
+	  inferior_ptid = ptid_build (child_pid, child_pid, 0);
+	  add_thread (inferior_ptid);
+	  lp = add_lwp (inferior_ptid);
+	  lp->stopped = 1;
+
+	  check_for_thread_db ();
+
+	  do_cleanups (old_chain);
 	}
 
       if (has_vforked)
@@ -692,6 +707,7 @@ linux_child_follow_fork (struct target_o
     {
       struct thread_info *tp;
       struct inferior *parent_inf, *child_inf;
+      struct lwp_info *lp;
 
       /* Before detaching from the parent, remove all breakpoints from it. */
       remove_breakpoints ();
@@ -733,30 +749,33 @@ linux_child_follow_fork (struct target_o
 
       if (has_vforked)
 	{
+	  struct lwp_info *parent_lwp;
+
 	  linux_parent_pid = parent_pid;
+
+	  /* Get rid of the inferior on the core side as well.  */
+	  inferior_ptid = null_ptid;
 	  detach_inferior (parent_pid);
-	}
-      else if (!detach_fork)
-	{
-	  struct fork_info *fp;
-	  /* Retain parent fork in ptrace (stopped) state.  */
-	  fp = find_fork_pid (parent_pid);
-	  if (!fp)
-	    fp = add_fork (parent_pid);
-	  fork_save_infrun_state (fp, 0);
 
-	  /* Also add an entry for the child fork.  */
-	  fp = find_fork_pid (child_pid);
-	  if (!fp)
-	    fp = add_fork (child_pid);
-	  fork_save_infrun_state (fp, 0);
+	  /* Also get rid of all its lwps.  We will detach from this
+	     inferior soon-ish, but, we will still get an exit event
+	     reported through waitpid when it exits.  If we didn't get
+	     rid of the lwps from our list, we would end up reporting
+	     the inferior exit to the core, which would then try to
+	     mourn a non-existing (from the core's perspective)
+	     inferior.  */
+	  parent_lwp = find_lwp_pid (pid_to_ptid (parent_pid));
+	  purge_lwp_list (GET_PID (parent_lwp->ptid));
+	  linux_parent_pid = parent_pid;
 	}
-      else
+      else if (detach_fork)
 	target_detach (NULL, 0);
 
       inferior_ptid = ptid_build (child_pid, child_pid, 0);
+      add_thread (inferior_ptid);
+      lp = add_lwp (inferior_ptid);
+      lp->stopped = 1;
 
-      linux_nat_switch_fork (inferior_ptid);
       check_for_thread_db ();
     }
 
@@ -1073,22 +1092,30 @@ iterate_over_lwps (ptid_t filter,
   return NULL;
 }
 
-/* Update our internal state when changing from one fork (checkpoint,
-   et cetera) to another indicated by NEW_PTID.  We can only switch
-   single-threaded applications, so we only create one new LWP, and
-   the previous list is discarded.  */
+/* Update our internal state when changing from one checkpoint to
+   another indicated by NEW_PTID.  We can only switch single-threaded
+   applications, so we only create one new LWP, and the previous list
+   is discarded.  */
 
 void
 linux_nat_switch_fork (ptid_t new_ptid)
 {
   struct lwp_info *lp;
 
-  init_lwp_list ();
+  purge_lwp_list (GET_PID (inferior_ptid));
+
   lp = add_lwp (new_ptid);
   lp->stopped = 1;
 
-  init_thread_list ();
-  add_thread_silent (new_ptid);
+  /* This changes the thread's ptid while preserving the gdb thread
+     num.  Also changes the inferior pid, while preserving the
+     inferior num.  */
+  thread_change_ptid (inferior_ptid, new_ptid);
+
+  /* We've just told GDB core that the thread changed target id, but,
+     in fact, it really is a different thread, with different register
+     contents.  */
+  registers_changed ();
 }
 
 /* Handle the exit of a single thread LP.  */
@@ -1815,6 +1842,34 @@ linux_handle_extended_wait (struct lwp_i
 
       ourstatus->value.related_pid = ptid_build (new_pid, new_pid, 0);
 
+      if (event == PTRACE_EVENT_FORK
+	  && linux_fork_checkpointing_p (GET_PID (lp->ptid)))
+	{
+	  struct fork_info *fp;
+
+	  /* Handle checkpointing by linux-fork.c here as a special
+	     case.  We don't want the follow-fork-mode or 'catch fork'
+	     to interfere with this.  */
+
+	  /* This won't actually modify the breakpoint list, but will
+	     physically remove the breakpoints from the child.  */
+	  detach_breakpoints (new_pid);
+
+	  /* Retain child fork in ptrace (stopped) state.  */
+	  fp = find_fork_pid (new_pid);
+	  if (!fp)
+	    fp = add_fork (new_pid);
+
+	  /* Report as spurious, so that infrun doesn't want to follow
+	     this fork.  We're actually doing an infcall in
+	     linux-fork.c.  */
+	  ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+	  linux_enable_event_reporting (pid_to_ptid (new_pid));
+
+	  /* Report the stop to the core.  */
+	  return 0;
+	}
+
       if (event == PTRACE_EVENT_FORK)
 	ourstatus->kind = TARGET_WAITKIND_FORKED;
       else if (event == PTRACE_EVENT_VFORK)
@@ -4295,7 +4350,7 @@ linux_nat_supports_non_stop (void)
 /* True if we want to support multi-process.  To be removed when GDB
    supports multi-exec.  */
 
-int linux_multi_process = 0;
+int linux_multi_process = 1;
 
 static int
 linux_nat_supports_multi_process (void)
Index: src/gdb/linux-fork.c
===================================================================
--- src.orig/gdb/linux-fork.c	2009-07-02 21:10:26.000000000 +0100
+++ src/gdb/linux-fork.c	2009-07-02 22:47:48.000000000 +0100
@@ -53,7 +53,6 @@ struct fork_info
   struct regcache *savedregs;	/* Convenient for info fork, saves 
 				   having to actually switch contexts.  */
   int clobber_regs;		/* True if we should restore saved regs.  */
-  ULONGEST pc;			/* PC for info fork.  */
   off_t *filepos;		/* Set of open file descriptors' offsets.  */
   int maxfd;
 };
@@ -211,7 +210,6 @@ init_fork_list (void)
   for (fp = fork_list; fp; fp = fpnext)
     {
       fpnext = fp->next;
-      delete_inferior (ptid_get_pid (fp->ptid));
       free_fork (fp);
     }
 
@@ -241,9 +239,7 @@ fork_load_infrun_state (struct fork_info
   extern void nullify_last_target_wait_ptid ();
   int i;
 
-  inferior_ptid = fp->ptid;
-
-  linux_nat_switch_fork (inferior_ptid);
+  linux_nat_switch_fork (fp->ptid);
 
   if (fp->savedregs && fp->clobber_regs)
     regcache_cpy (get_current_regcache (), fp->savedregs);
@@ -269,7 +265,7 @@ fork_load_infrun_state (struct fork_info
 /* Save infrun state for the fork PTID.
    Exported for use by linux child_follow_fork.  */
 
-extern void
+static void
 fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
 {
   char path[MAXPATHLEN];
@@ -281,7 +277,6 @@ fork_save_infrun_state (struct fork_info
 
   fp->savedregs = regcache_dup (get_current_regcache ());
   fp->clobber_regs = clobber_regs;
-  fp->pc = regcache_read_pc (get_current_regcache ());
 
   if (clobber_regs)
     {
@@ -371,8 +366,6 @@ linux_fork_mourn_inferior (void)
      We need to delete that one from the fork_list, and switch
      to the next available fork.  */
   delete_fork (inferior_ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (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
@@ -403,8 +396,6 @@ linux_fork_detach (char *args, int from_
     error (_("Unable to detach %s"), target_pid_to_str (inferior_ptid));
 
   delete_fork (inferior_ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (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
@@ -430,14 +421,14 @@ delete_fork_command (char *args, int fro
   ptid_t ptid;
 
   if (!args || !*args)
-    error (_("Requires argument (fork/checkpoint id to delete)"));
+    error (_("Requires argument (checkpoint id to delete)"));
 
   ptid = fork_id_to_ptid (parse_and_eval_long (args));
   if (ptid_equal (ptid, minus_one_ptid))
-    error (_("No such fork/checkpoint id, %s"), args);
+    error (_("No such checkpoint id, %s"), args);
 
   if (ptid_equal (ptid, inferior_ptid))
-    error (_("Please switch to another fork/checkpoint before deleting the current one"));
+    error (_("Please switch to another checkpoint before deleting the current one"));
 
   if (ptrace (PTRACE_KILL, PIDGET (ptid), 0, 0))
     error (_("Unable to kill pid %s"), target_pid_to_str (ptid));
@@ -446,8 +437,6 @@ delete_fork_command (char *args, int fro
     printf_filtered (_("Killed %s\n"), target_pid_to_str (ptid));
 
   delete_fork (ptid);
-  /* Delete process from GDB's inferior list.  */
-  delete_inferior (ptid_get_pid (ptid));
 }
 
 static void
@@ -456,14 +445,15 @@ detach_fork_command (char *args, int fro
   ptid_t ptid;
 
   if (!args || !*args)
-    error (_("Requires argument (fork id to detach)"));
+    error (_("Requires argument (checkpoint id to detach)"));
 
   ptid = fork_id_to_ptid (parse_and_eval_long (args));
   if (ptid_equal (ptid, minus_one_ptid))
-    error (_("No such fork id, %s"), args);
+    error (_("No such checkpoint id, %s"), args);
 
   if (ptid_equal (ptid, inferior_ptid))
-    error (_("Please switch to another fork before detaching the current one"));
+    error (_("\
+Please switch to another checkpoint before detaching the current one"));
 
   if (ptrace (PTRACE_DETACH, PIDGET (ptid), 0, 0))
     error (_("Unable to detach %s"), target_pid_to_str (ptid));
@@ -472,8 +462,6 @@ detach_fork_command (char *args, int fro
     printf_filtered (_("Detached %s\n"), target_pid_to_str (ptid));
 
   delete_fork (ptid);
-  /* Delete process from GDB's process table.  */
-  detach_inferior (ptid_get_pid (ptid));
 }
 
 /* Print information about currently known forks.  */
@@ -508,7 +496,7 @@ info_forks_command (char *arg, int from_
       else
 	{
 	  printf_filtered ("  ");
-	  pc = fp->pc;
+	  pc = regcache_read_pc (fp->savedregs);
 	}
       printf_filtered ("%d %s", fp->num, target_pid_to_str (fp->ptid));
       if (fp->num == 0)
@@ -542,31 +530,19 @@ info_forks_command (char *arg, int from_
   if (printed == NULL)
     {
       if (requested > 0)
-	printf_filtered (_("No fork number %d.\n"), requested);
+	printf_filtered (_("No checkpoint number %d.\n"), requested);
       else
-	printf_filtered (_("No forks.\n"));
+	printf_filtered (_("No checkpoints.\n"));
     }
 }
 
-/* Save/restore mode variable 'detach_fork':
-   We need to temporarily take over this mode variable, while
-   preserving the user-specified state, and make sure that it 
-   gets restored in case of error.
+/* The PID of the process we're checkpointing.  */
+static int checkpointing_pid = 0;
 
-   The int pointer that we use comes from the caller, so we can
-   be called more than once (even though currently we don't need to).  */
-
-static void 
-restore_detach_fork (void *arg)
+int
+linux_fork_checkpointing_p (int pid)
 {
-  detach_fork = *(int *) arg;
-}
-
-static struct cleanup *
-save_detach_fork (int *saved_val)
-{
-  *saved_val = detach_fork;
-  return make_cleanup (restore_detach_fork, (void *) saved_val);
+  return (checkpointing_pid == pid);
 }
 
 static void
@@ -581,12 +557,6 @@ checkpoint_command (char *args, int from
   pid_t retpid;
   struct cleanup *old_chain;
   long i;
-  /* Make this temp var static, 'cause it's used in the error context.  */
-  static int temp_detach_fork;
-
-  /* Remove breakpoints, so that they are not inserted
-     in the forked process.  */
-  remove_breakpoints ();
 
   /* Make the inferior fork, record its (and gdb's) state.  */
 
@@ -600,8 +570,11 @@ checkpoint_command (char *args, int from
 
   gdbarch = get_objfile_arch (fork_objf);
   ret = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
-  old_chain = save_detach_fork (&temp_detach_fork);
-  detach_fork = 0;
+
+  /* Tell linux-nat.c that we're checkpointing this inferior.  */
+  old_chain = make_cleanup_restore_integer (&checkpointing_pid);
+  checkpointing_pid = PIDGET (inferior_ptid);
+
   ret = call_function_by_hand (fork_fn, 0, &ret);
   do_cleanups (old_chain);
   if (!ret)	/* Probably can't happen.  */
@@ -629,7 +602,6 @@ checkpoint_command (char *args, int from
   if (!fp)
     error (_("Failed to find new fork"));
   fork_save_infrun_state (fp, 1);
-  insert_breakpoints ();
 }
 
 static void
@@ -656,37 +628,7 @@ linux_fork_context (struct fork_info *ne
   print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
 }
 
-/* Switch inferior process (fork) context, by process id.  */
-static void
-process_command (char *args, int from_tty)
-{
-  struct fork_info *fp;
-
-  if (!args || !*args)
-    error (_("Requires argument (process id to switch to)"));
-
-  if ((fp = find_fork_pid (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: process id %s"), args);
-
-  linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by fork id.  */
-static void
-fork_command (char *args, int from_tty)
-{
-  struct fork_info *fp;
-
-  if (!args || !*args)
-    error (_("Requires argument (fork id to switch to)"));
-
-  if ((fp = find_fork_id (parse_and_eval_long (args))) == NULL)
-    error (_("Not found: fork id %s"), args);
-
-  linux_fork_context (fp, from_tty);
-}
-
-/* Switch inferior process (fork) context, by checkpoint id.  */
+/* Switch inferior process (checkpoint) context, by checkpoint id.  */
 static void
 restart_command (char *args, int from_tty)
 {
@@ -727,9 +669,8 @@ Tells gdb whether to detach the child of
   add_com ("checkpoint", class_obscure, checkpoint_command, _("\
 Fork a duplicate process (experimental)."));
 
-  /* Restart command: restore the context of a specified fork
-     process.  May be used for "program forks" as well as for
-     "debugger forks" (checkpoints).  */
+  /* Restart command: restore the context of a specified checkpoint
+     process.  */
 
   add_com ("restart", class_obscure, restart_command, _("\
 restart <n>: restore program context from a checkpoint.\n\
@@ -739,36 +680,19 @@ Argument 'n' is checkpoint ID, as displa
      fork list.  */
 
   add_cmd ("checkpoint", class_obscure, delete_fork_command, _("\
-Delete a fork/checkpoint (experimental)."),
+Delete a checkpoint (experimental)."),
 	   &deletelist);
 
   /* Detach checkpoint command: release the process to run independently, 
      and remove it from the fork list.  */
 
   add_cmd ("checkpoint", class_obscure, detach_fork_command, _("\
-Detach from a fork/checkpoint (experimental)."),
+Detach from a checkpoint (experimental)."),
 	   &detachlist);
 
   /* Info checkpoints command: list all forks/checkpoints 
      currently under gdb's control.  */
 
   add_info ("checkpoints", info_forks_command,
-	    _("IDs of currently known forks/checkpoints."));
-
-  /* Command aliases (let "fork" and "checkpoint" be used 
-     interchangeably).  */
-
-  add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &deletelist);
-  add_alias_cmd ("fork", "checkpoint", class_obscure, 1, &detachlist);
-  add_info_alias ("forks", "checkpoints", 0);
-
-  /* "fork <n>" (by analogy to "thread <n>").  */
-  add_com ("fork", class_obscure, fork_command, _("\
-fork <n>: Switch between forked processes.\n\
-Argument 'n' is fork ID, as displayed by 'info forks'."));
-
-  /* "process <proc id>" as opposed to "fork <fork id>".  */
-  add_com ("process", class_obscure, process_command, _("\
-process <pid>: Switch between forked processes.\n\
-Argument 'pid' is process ID, as displayed by 'info forks' or 'shell ps'."));
+	    _("IDs of currently known checkpoints."));
 }
Index: src/gdb/linux-fork.h
===================================================================
--- src.orig/gdb/linux-fork.h	2009-07-02 21:10:26.000000000 +0100
+++ src/gdb/linux-fork.h	2009-07-02 22:47:48.000000000 +0100
@@ -20,13 +20,11 @@
 struct fork_info;
 extern struct fork_info *add_fork (pid_t);
 extern struct fork_info *find_fork_pid (pid_t);
-extern void fork_save_infrun_state (struct fork_info *, int);
 extern void linux_fork_killall (void);
 extern void linux_fork_mourn_inferior (void);
 extern void linux_fork_detach (char *, int);
-extern int  forks_exist_p (void);
-
-struct fork_info *fork_list;
+extern int forks_exist_p (void);
+extern int linux_fork_checkpointing_p (int);
 
 extern int detach_fork;
 
Index: src/gdb/cli/cli-cmds.c
===================================================================
--- src.orig/gdb/cli/cli-cmds.c	2009-07-02 21:10:27.000000000 +0100
+++ src/gdb/cli/cli-cmds.c	2009-07-02 22:47:48.000000000 +0100
@@ -125,6 +125,10 @@ struct cmd_list_element *deletelist;
 
 struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands. */
+
+struct cmd_list_element *killlist;
+
 /* Chain containing all defined "enable breakpoint" subcommands. */
 
 struct cmd_list_element *enablebreaklist;
Index: src/gdb/cli/cli-cmds.h
===================================================================
--- src.orig/gdb/cli/cli-cmds.h	2009-07-02 21:10:27.000000000 +0100
+++ src/gdb/cli/cli-cmds.h	2009-07-02 22:47:48.000000000 +0100
@@ -41,6 +41,10 @@ extern struct cmd_list_element *deleteli
 
 extern struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands.  */
+
+extern struct cmd_list_element *killlist;
+
 /* Chain containing all defined toggle subcommands. */
 
 extern struct cmd_list_element *togglelist;
Index: src/gdb/gdbcmd.h
===================================================================
--- src.orig/gdb/gdbcmd.h	2009-07-02 21:10:25.000000000 +0100
+++ src/gdb/gdbcmd.h	2009-07-02 22:47:48.000000000 +0100
@@ -52,6 +52,10 @@ extern struct cmd_list_element *deleteli
 
 extern struct cmd_list_element *detachlist;
 
+/* Chain containing all defined kill subcommands.  */
+
+extern struct cmd_list_element *killlist;
+
 /* Chain containing all defined toggle subcommands.  */
 
 extern struct cmd_list_element *togglelist;
Index: src/gdb/inferior.c
===================================================================
--- src.orig/gdb/inferior.c	2009-07-02 21:10:25.000000000 +0100
+++ src/gdb/inferior.c	2009-07-02 22:49:53.000000000 +0100
@@ -25,6 +25,7 @@
 #include "gdbthread.h"
 #include "ui-out.h"
 #include "observer.h"
+#include "gdbthread.h"
 
 void _initialize_inferiors (void);
 
@@ -352,12 +353,112 @@ print_inferior (struct ui_out *uiout, in
   do_cleanups (old_chain);
 }
 
+static void
+detach_inferior_command (char *args, int from_tty)
+{
+  int num, pid;
+  struct thread_info *tp;
+
+  if (!args || !*args)
+    error (_("Requires argument (inferior id to detach)"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  tp = any_thread_of_process (pid);
+  if (!tp)
+    error (_("Inferior has no threads."));
+
+  switch_to_thread (tp->ptid);
+
+  detach_command (NULL, from_tty);
+}
+
+static void
+kill_inferior_command (char *args, int from_tty)
+{
+  int num, pid;
+  struct thread_info *tp;
+
+  if (!args || !*args)
+    error (_("Requires argument (inferior id to kill)"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  tp = any_thread_of_process (pid);
+  if (!tp)
+    error (_("Inferior has no threads."));
+
+  switch_to_thread (tp->ptid);
+
+  target_kill ();
+
+  bfd_cache_close_all ();
+}
+
+static void
+inferior_command (char *args, int from_tty)
+{
+  int num, pid;
+
+  if (!have_inferiors ())
+    error (_("No inferiors"));
+
+  num = parse_and_eval_long (args);
+
+  if (!valid_gdb_inferior_id (num))
+    error (_("Inferior ID %d not known."), num);
+
+  pid = gdb_inferior_id_to_pid (num);
+
+  if (pid != ptid_get_pid (inferior_ptid))
+    {
+      struct thread_info *tp;
+
+      tp = any_thread_of_process (pid);
+      if (!tp)
+	error (_("Inferior has no threads."));
+
+      switch_to_thread (tp->ptid);
+    }
+
+  printf_filtered (_("[Switching to thread %d (%s)] "),
+		   pid_to_thread_id (inferior_ptid),
+		   target_pid_to_str (inferior_ptid));
+
+  if (is_running (inferior_ptid))
+    ui_out_text (uiout, "(running)\n");
+  else
+    {
+      ui_out_text (uiout, "\n");
+      print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+    }
+}
+
 /* Print information about currently known inferiors.  */
 
 static void
-info_inferiors_command (char *arg, int from_tty)
+info_inferiors_command (char *args, int from_tty)
 {
-  print_inferior (uiout, -1);
+  int requested = -1;
+
+  if (args && *args)
+    {
+      requested = parse_and_eval_long (args);
+      if (!valid_gdb_inferior_id (requested))
+	error (_("Inferior ID %d not known."), requested);
+    }
+
+  print_inferior (uiout, requested);
 }
 
 /* Print notices when new inferiors are created and die.  */
@@ -381,4 +482,17 @@ Show printing of inferior events (e.g., 
          NULL,
          show_print_inferior_events,
          &setprintlist, &showprintlist);
+
+  add_cmd ("inferior", class_run, detach_inferior_command, _("\
+Detach from inferior ID."),
+	   &detachlist);
+
+  add_cmd ("inferior", class_run, kill_inferior_command, _("\
+Kill inferior ID."),
+	   &killlist);
+
+  add_cmd ("inferior", class_run, inferior_command, _("\
+Use this command to switch between inferiors.\n\
+The new inferior ID must be currently known."),
+	   &cmdlist);
 }
Index: src/gdb/testsuite/gdb.base/multi-forks.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/multi-forks.exp	2009-07-02 21:10:29.000000000 +0100
+++ src/gdb/testsuite/gdb.base/multi-forks.exp	2009-07-02 22:47:48.000000000 +0100
@@ -140,6 +140,19 @@ gdb_test "print pids\[0\]==0 || pids\[1\
 # Now test with detach-on-fork off.
 #
 
+# detach-on-fork isn't implemented on hpux.
+#
+if {![istarget "*-*-linux*"]} then {
+    continue
+}
+
+# Start with a fresh gdb
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
 runto_main
 gdb_breakpoint $exit_bp_loc
 
@@ -152,60 +165,59 @@ gdb_test "set detach off" "" "set detach
 
 #
 # We will now run every fork up to the exit bp, 
-# eventually winding up with 16 forks.
+# eventually winding up with 16 inferiors.
 #
 
 for {set i 1} {$i <= 15} {incr i} {
   gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit $i"
-  gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork $i"
-  gdb_test "restart $i" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
-      "restart $i"
+  gdb_test "info inferior" " 5 .* 4 .* 3 .* 2 .*" "info inferior $i"
+  gdb_test "inferior $i + 1" "(_dl_sysinfo_int80|fork|__kernel_(v|)syscall).*" \
+      "inferior $i"
 }
 
 gdb_test "continue" "Breakpoint .* main .*exit.*" "Run to exit 16"
-gdb_test "info fork" " 4 .* 3 .* 2 .* 1 .*" "info fork 16"
-gdb_test "restart 0" " main .*" "restart final"
+gdb_test "info inferiors" " 5 .* 4 .* 3 .* 2 .*" "info inferior 16"
+gdb_test "inferior 2" " main .*" "restart final"
 
 #
 # Now we should examine all the pids.
 #
 
 # 
-# Test detach fork
+# Test detach inferior
 # 
 
-# [assumes we're at #0]
-gdb_test "detach fork 1" "Detached .*" "Detach 1"
-gdb_test "detach fork 2" "Detached .*" "Detach 2"
-gdb_test "detach fork 3" "Detached .*" "Detach 3"
-gdb_test "detach fork 4" "Detached .*" "Detach 4"
+# [assumes we're at #1]
+gdb_test "detach inferior 2" "Detaching .*" "Detach 2"
+gdb_test "detach inferior 3" "Detaching .*" "Detach 3"
+gdb_test "detach inferior 4" "Detaching .*" "Detach 4"
+gdb_test "detach inferior 5" "Detaching .*" "Detach 5"
 
 # 
-# Test delete fork
-# 
+# Test kill inferior
+#
 
-gdb_test "delete fork 5" "" "Delete 5"
-gdb_test "info fork 5"   "No fork number 5." "Did delete 5"
-gdb_test "delete fork 6" "" "Delete 6"
-gdb_test "info fork 6"   "No fork number 6." "Did delete 6"
-gdb_test "delete fork 7" "" "Delete 7"
-gdb_test "info fork 7"   "No fork number 7." "Did delete 7"
-gdb_test "delete fork 8" "" "Delete 8"
-gdb_test "info fork 8"   "No fork number 8." "Did delete 8"
-gdb_test "delete fork 9" "" "Delete 9"
-gdb_test "info fork 9"   "No fork number 9." "Did delete 9"
-gdb_test "delete fork 10" "" "Delete 10"
-gdb_test "info fork 10"   "No fork number 10." "Did delete 10"
-gdb_test "delete fork 11" "" "Delete 11"
-gdb_test "info fork 11"   "No fork number 11." "Did delete 11"
-gdb_test "delete fork 12" "" "Delete 12"
-gdb_test "info fork 12"   "No fork number 12." "Did delete 12"
-gdb_test "delete fork 13" "" "Delete 13"
-gdb_test "info fork 13"   "No fork number 13." "Did delete 13"
-gdb_test "delete fork 14" "" "Delete 14"
-gdb_test "info fork 14"   "No fork number 14." "Did delete 14"
-gdb_test "delete fork 15" "" "Delete 15"
-gdb_test "info fork 15"   "No fork number 15." "Did delete 15"
+gdb_test "kill inferior 6" "" "Kill 6"
+gdb_test "info inferior 6" "Inferior ID 6 not known." "Did kill 6"
+gdb_test "kill inferior 7" "" "Kill 7"
+gdb_test "info inferior 7" "Inferior ID 7 not known." "Did kill 7"
+gdb_test "kill inferior 8" "" "Kill 8"
+gdb_test "info inferior 8" "Inferior ID 8 not known." "Did kill 8"
+gdb_test "kill inferior 9" "" "Kill 9"
+gdb_test "info inferior 9" "Inferior ID 9 not known." "Did kill 9"
+gdb_test "kill inferior 10" "" "Kill 10"
+gdb_test "info inferior 10" "Inferior ID 10 not known." "Did kill 10"
+gdb_test "kill inferior 11" "" "Kill 11"
+gdb_test "info inferior 11" "Inferior ID 11 not known." "Did kill 11"
+gdb_test "kill inferior 12" "" "Kill 12"
+gdb_test "info inferior 12" "Inferior ID 12 not known." "Did kill 12"
+gdb_test "kill inferior 13" "" "Kill 13"
+gdb_test "info inferior 13" "Inferior ID 13 not known." "Did kill 13"
+gdb_test "kill inferior 14" "" "Kill 14"
+gdb_test "info inferior 14" "Inferior ID 14 not known." "Did kill 14"
+gdb_test "kill inferior 15" "" "Kill 15"
+gdb_test "info inferior 15" "Inferior ID 15 not known." "Did kill 15"
+gdb_test "kill inferior 16" "" "Kill 16"
+gdb_test "info inferior 16" "Inferior ID 16 not known." "Did kill 16"
 
 return 0
-
Index: src/gdb/testsuite/gdb.base/ending-run.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/ending-run.exp	2009-07-02 21:10:29.000000000 +0100
+++ src/gdb/testsuite/gdb.base/ending-run.exp	2009-07-02 22:47:48.000000000 +0100
@@ -71,7 +71,7 @@ gdb_test "b ending-run.c:14" ".*Note.*al
 gdb_test "cle ending-run.c:14" \
 	".*Deleted breakpoint 5.*" "Cleared 2 by line"
 
-send_gdb "inf line ending-run.c:14\n"
+send_gdb "info line ending-run.c:14\n"
 gdb_expect {
     -re ".*address (0x\[0-9a-fA-F]*).*$gdb_prompt $" {
         set line_nine $expect_out(1,string)
Index: src/gdb/testsuite/gdb.base/help.exp
===================================================================
--- src.orig/gdb/testsuite/gdb.base/help.exp	2009-07-02 21:10:29.000000000 +0100
+++ src/gdb/testsuite/gdb.base/help.exp	2009-07-02 22:47:48.000000000 +0100
@@ -300,7 +300,9 @@ gdb_test "help inspect" "Same as \"print
 # test help jump
 gdb_test "help jump" "Continue program being debugged at specified line or address\.\[\r\n\]+Give as argument either LINENUM or \[*\]+ADDR, where ADDR is an expression\[\r\n\]+for an address to start at\." "help jump"
 # test help kill
-gdb_test "help kill" "Kill execution of program being debugged\." "help kill"
+test_prefix_command_help "kill" {
+    "Kill execution of program being debugged\.\[\r\n\]+"
+}
 # test help list "l" abbreviation
 gdb_test "help l" "List specified function or line\.\[\r\n\]+With no argument, lists ten more lines after or around previous listing\.\[\r\n\]+\"list -\" lists the ten lines before a previous ten-line listing\.\[\r\n\]+One argument specifies a line, and ten lines are listed around that line\.\[\r\n\]+Two arguments with comma between specify starting and ending lines to list\.\[\r\n\]+Lines can be specified in these ways:\[\r\n\]+  LINENUM, to list around that line in current file,\[\r\n\]+  FILE:LINENUM, to list around that line in that file,\[\r\n\]+  FUNCTION, to list around beginning of that function,\[\r\n\]+  FILE:FUNCTION, to distinguish among like-named static functions\.\[\r\n\]+  \[*\]ADDRESS, to list around the line containing that address\.\[\r\n\]+With two args if one is empty it stands for ten lines away from the other arg\." "help list \"l\" abbreviation"
 # test help list
Index: src/gdb/infcmd.c
===================================================================
--- src.orig/gdb/infcmd.c	2009-07-02 21:10:25.000000000 +0100
+++ src/gdb/infcmd.c	2009-07-02 22:47:48.000000000 +0100
@@ -2698,8 +2698,9 @@ fully linked executable files and separa
 	       &showlist);
   set_cmd_completer (c, noop_completer);
 
-  add_com ("kill", class_run, kill_command,
-	   _("Kill execution of program being debugged."));
+  add_prefix_cmd ("kill", class_run, kill_command,
+		  _("Kill execution of program being debugged."),
+		  &killlist, "kill ", 0, &cmdlist);
 
   add_com ("attach", class_run, attach_command, _("\
 Attach to a process or file outside of GDB.\n\
Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h	2009-07-02 21:10:25.000000000 +0100
+++ src/gdb/gdbthread.h	2009-07-02 22:47:48.000000000 +0100
@@ -243,6 +243,9 @@ struct thread_info *find_thread_id (int 
    returns the first thread in the list.  */
 struct thread_info *first_thread_of_process (int pid);
 
+/* Returns any thread of process PID.  */
+extern struct thread_info *any_thread_of_process (int pid);
+
 /* Change the ptid of thread OLD_PTID to NEW_PTID.  */
 void thread_change_ptid (ptid_t old_ptid, ptid_t new_ptid);
 
Index: src/gdb/thread.c
===================================================================
--- src.orig/gdb/thread.c	2009-07-02 21:10:27.000000000 +0100
+++ src/gdb/thread.c	2009-07-02 22:47:48.000000000 +0100
@@ -428,6 +428,18 @@ first_thread_of_process (int pid)
   return ret;
 }
 
+struct thread_info *
+any_thread_of_process (int pid)
+{
+  struct thread_info *tp;
+
+  for (tp = thread_list; tp; tp = tp->next)
+    if (ptid_get_pid (tp->ptid) == pid)
+      return tp;
+
+  return NULL;
+}
+
 /* Print a list of thread ids currently known, and the total number of
    threads. To be used from within catch_errors. */
 static int
Index: src/gdb/doc/gdb.texinfo
===================================================================
--- src.orig/gdb/doc/gdb.texinfo	2009-07-02 21:10:28.000000000 +0100
+++ src/gdb/doc/gdb.texinfo	2009-07-02 22:47:48.000000000 +0100
@@ -2374,7 +2374,39 @@ To find out what inferiors exist at any 
 @kindex info inferiors
 @item info inferiors
 Print a list of all inferiors currently being managed by @value{GDBN}.
+@end table
+
+To switch focus between inferiors, use the @code{inferior} command:
+
+@table @code
+@kindex inferior @var{inferior-id}
+@item inferior @var{inferior-id}
+Make inferior number @var{inferior-id} the current inferior.  The
+argument @var{inferior-id} is the internal inferior number assigned by
+@value{GDBN}, as shown in the first field of the @samp{info inferiors}
+display.
+@end table
+
+To quit debugging one of the inferiors, you can either detach from it
+by using the @w{@code{detach inferior}} command (allowing it to run
+independently), or kill it using the @w{@code{kill inferior}} command:
+
+@table @code
+@kindex detach inferior @var{inferior-id}
+@item detach inferior @var{inferior-id}
+Detach from the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+
+@kindex kill inferior @var{inferior-id}
+@item kill inferior @var{inferior-id}
+Kill the inferior identified by @value{GDBN} inferior number
+@var{inferior-id}, and remove it from the inferior list.
+@end table
+
+To be notified when inferiors are started or exit under @value{GDBN}'s
+control use @w{@code{set print inferior-events}}:
 
+@table @code
 @kindex set print inferior-events
 @cindex print messages on inferior start and exit
 @item set print inferior-events
@@ -2758,51 +2790,17 @@ is held suspended.  
 Show whether detach-on-fork mode is on/off.
 @end table
 
-If you choose to set @samp{detach-on-fork} mode off, then
-@value{GDBN} will retain control of all forked processes (including
-nested forks).  You can list the forked processes under the control of
-@value{GDBN} by using the @w{@code{info forks}} command, and switch
-from one fork to another by using the @w{@code{fork}} command.
-
-@table @code
-@kindex info forks
-@item info forks
-Print a list of all forked processes under the control of @value{GDBN}.
-The listing will include a fork id, a process id, and the current 
-position (program counter) of the process.
-
-@kindex fork @var{fork-id}
-@item fork @var{fork-id}
-Make fork number @var{fork-id} the current process.  The argument
-@var{fork-id} is the internal fork number assigned by @value{GDBN},
-as shown in the first field of the @samp{info forks} display.
-
-@kindex process @var{process-id}
-@item process @var{process-id}
-Make process number @var{process-id} the current process.  The
-argument @var{process-id} must be one that is listed in the output of
-@samp{info forks}.
-
-@end table
+If you choose to set @samp{detach-on-fork} mode off, then @value{GDBN}
+will retain control of all forked processes (including nested forks).
+You can list the forked processes under the control of @value{GDBN} by
+using the @w{@code{info inferiors}} command, and switch from one fork
+to another by using the @code{inferior} command (@pxref{Inferiors,
+,Debugging Multiple Inferiors}).
 
 To quit debugging one of the forked processes, you can either detach
-from it by using the @w{@code{detach fork}} command (allowing it to
-run independently), or delete (and kill) it using the
-@w{@code{delete fork}} command.
-
-@table @code
-@kindex detach fork @var{fork-id}
-@item detach fork @var{fork-id}
-Detach from the process identified by @value{GDBN} fork number
-@var{fork-id}, and remove it from the fork list.  The process will be
-allowed to run independently.
-
-@kindex delete fork @var{fork-id}
-@item delete fork @var{fork-id}
-Kill the process identified by @value{GDBN} fork number @var{fork-id},
-and remove it from the fork list.
-
-@end table
+from it by using the @w{@code{detach inferior}} command (allowing it
+to run independently), or kill it using the @w{@code{kill inferior}}
+command.  @xref{Inferiors, ,Debugging Multiple Inferiors}.
 
 If you ask to debug a child process and a @code{vfork} is followed by an
 @code{exec}, @value{GDBN} executes the new target up to the first
@@ -2810,8 +2808,8 @@ breakpoint in the new target.  If you ha
 @code{main} in your original program, the breakpoint will also be set on
 the child process's @code{main}.
 
-When a child process is spawned by @code{vfork}, you cannot debug the
-child or parent until an @code{exec} call completes.
+On some systems, when a child process is spawned by @code{vfork}, you
+cannot debug the child or parent until an @code{exec} call completes.
 
 If you issue a @code{run} command to @value{GDBN} after an @code{exec}
 call executes, the new target restarts.  To restart the parent process,
Index: src/gdb/NEWS
===================================================================
--- src.orig/gdb/NEWS	2009-07-02 21:10:24.000000000 +0100
+++ src/gdb/NEWS	2009-07-02 22:47:48.000000000 +0100
@@ -175,6 +175,16 @@ GDB will now correctly handle all of:
 * Support for user-defined prefixed commands.  The "define" command can
 add new commands to existing prefixes, e.g. "target".
 
+* Multi-inferior, multi-process debugging.
+
+  GDB now has generalized support for multi-inferior debugging.  See
+  "Debugging Multiple Inferiors" in the manual for more information.
+  Although availability still depends on target support, the command
+  set is more uniform now.  The GNU/Linux specific multi-forks support
+  has been migrated to this new framework.  This implied some user
+  visible changes; see "New commands" and also "Removed commands"
+  below.
+
 * New commands (for set/show, see "New options" below)
 
 find [/size-char] [/max-count] start-address, end-address|+search-space-size,
@@ -197,6 +207,18 @@ macro undef
 info os processes
   Show operating system information about processes.
 
+info inferiors
+  List the inferiors currently under GDB's control.
+
+inferior NUM
+  Switch focus to inferior number NUM.
+
+detach inferior NUM
+  Detach from inferior number NUM.
+
+kill inferior NUM
+  Kill inferior number NUM.
+
 * New options
 
 set sh calling-convention
@@ -320,6 +342,36 @@ show schedule-multiple
   Allow GDB to resume all threads of all processes or only threads of
   the current process.
 
+* Removed commands
+
+info forks
+  For program forks, this is replaced by the new more generic `info
+  inferiors' command.  To list checkpoints, you can still use the
+  `info checkpoints' command, which was an alias for the `info forks'
+  command.
+
+fork NUM
+  Replaced by the new `inferior' command.  To switch between
+  checkpoints, you can still use the `restart' command, which was an
+  alias for the `fork' command.
+
+process PID
+  This is removed, since some targets don't have a notion of
+  processes.  To switch between processes, you can still use the
+  `inferior' command using GDB's own inferior number.
+
+delete fork NUM
+  For program forks, this is replaced by the new more generic `kill
+  inferior' command.  To delete a checkpoint, you can still use the
+  `delete checkpoint' command, which was an alias for the `delete
+  fork' command.
+
+detach fork NUM
+  For program forks, this is replaced by the new more generic `detach
+  inferior' command.  To detach a checkpoint, you can still use the
+  `detach checkpoint' command, which was an alias for the `detach
+  fork' command.
+
 * New native configurations
 
 x86/x86_64 Darwin		i[34567]86-*-darwin*


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2009-07-02 21:59 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-05-30 23:13 Move the multi-forks support to the generic multi-inferiors support Pedro Alves
2009-05-31 20:46 ` Tom Tromey
2009-05-31 22:07   ` Pedro Alves
2009-05-31 22:23     ` Doug Evans
2009-06-01 15:58     ` Tom Tromey
2009-06-06  0:09       ` Tom Tromey
2009-06-06 16:06         ` Pedro Alves
2009-06-08  1:34           ` Marc Khouzam
2009-06-08 22:43             ` Tom Tromey
2009-06-08 23:22               ` Pedro Alves
2009-06-08 22:39           ` Tom Tromey
2009-06-08 23:16             ` Pedro Alves
2009-06-10 22:09               ` Tom Tromey
2009-06-10 22:13                 ` Pedro Alves
2009-06-08 12:58 ` Pedro Alves
2009-06-11 19:32 ` Michael Snyder
2009-07-01 18:27   ` Pedro Alves
2009-07-01 19:16     ` Eli Zaretskii
2009-07-02 14:32       ` Pedro Alves
2009-07-02 19:00         ` Eli Zaretskii
2009-07-02 21:59           ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox