]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
2005-11-28 Michael Snyder <msnyder@redhat.com>
authorMichael Snyder <msnyder@vmware.com>
Mon, 28 Nov 2005 22:28:33 +0000 (22:28 +0000)
committerMichael Snyder <msnyder@vmware.com>
Mon, 28 Nov 2005 22:28:33 +0000 (22:28 +0000)
* linux-fork.c: New file.  Move fork list and fork commands here.
* linux-fork.h: Now acts as interface between linux-nat and linux-fork.
* linux-nat.c: Move all fork-related functions to linux-fork.c.
(child_mourn_inferior): Use new API to linux-fork, instead of
manipulating the fork list directly.
(kill_inferior): Ditto.

gdb/ChangeLog
gdb/linux-fork.c [new file with mode: 0644]
gdb/linux-fork.h [new file with mode: 0644]
gdb/linux-nat.c

index 0c49ef3e91389edaef5df4b9fa788ce6db8ec093..66cf2341307c7b6385fa9469f46a4e7eecfd1366 100644 (file)
@@ -1,3 +1,12 @@
+2005-11-28  Michael Snyder  <msnyder@redhat.com>
+
+       * linux-fork.c: New file.  Move fork list and fork commands here.
+       * linux-fork.h: Now acts as interface between linux-nat and linux-fork.
+       * linux-nat.c: Move all fork-related functions to linux-fork.c.
+       (child_mourn_inferior): Use new API to linux-fork, instead of
+       manipulating the fork list directly.
+       (kill_inferior): Ditto.
+
 2005-11-26  Michael Snyder  <msnyder@redhat.com>
 
        * linux-nat.c (super_mourn_inferior): New function pointer.
diff --git a/gdb/linux-fork.c b/gdb/linux-fork.c
new file mode 100644 (file)
index 0000000..a262238
--- /dev/null
@@ -0,0 +1,545 @@
+/* GNU/Linux native-dependent code for debugging multiple forks.
+
+   Copyright 2005 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+#include "defs.h"              /* Standard includes */
+#include "inferior.h"
+#include "regcache.h"          /* For regcache copy/restore */
+#include "gdbcmd.h"
+#include "infcall.h"           /* For call_function_by_hand */
+
+#include "linux-fork.h"                /* External interface */
+
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+struct fork_info *fork_list;
+static int highest_fork_num;
+
+int detach_fork = 1;           /* Default behavior is to detach
+                                  newly forked processes (legacy).  */
+
+/* Fork list data structure:  */
+struct fork_info
+{
+  struct fork_info *next;
+  ptid_t ptid;                 /* "Actual process id";
+                                   In fact, this may be overloaded with 
+                                   kernel thread id, etc.  */
+  int num;                     /* Convenient handle (GDB fork id) */
+  struct regcache *savedregs;  /* Convenient for info fork, saves 
+                                  having to actually switch contexts.  */
+  int clobber_regs;            /* True if we should restore saved regs.  */
+  int been_restarted;          /* One time flag.  */
+};
+
+/*
+ * Fork list methods:
+ */
+
+/* Add a fork to internal fork list.
+   Called from linux child_follow_fork.  */
+
+extern struct fork_info *
+add_fork (pid_t pid)
+{
+  struct fork_info *fp;
+
+  if (fork_list == NULL &&
+      pid != PIDGET (inferior_ptid))
+    {
+      /* Special case -- if this is the first fork in the list
+        (the list is hitherto empty), and if this new fork is
+        NOT the current inferior_ptid, then add inferior_ptid
+        first, as a special zeroeth fork id.  */
+      highest_fork_num = -1;
+      add_fork (PIDGET (inferior_ptid));       /* safe recursion */
+    }
+
+  fp = XZALLOC (struct fork_info);
+  fp->ptid = pid_to_ptid (pid);
+  fp->num = ++highest_fork_num;
+  fp->next = fork_list;
+  fork_list = fp;
+  return fp;
+}
+
+static void
+free_fork (struct fork_info *fp)
+{
+  /* FIXME: take care of any left-over step_resume breakpoints.  */
+  if (fp)
+    {
+      if (fp->savedregs)
+       regcache_xfree (fp->savedregs);
+      xfree (fp);
+    }
+}
+
+static void
+delete_fork (ptid_t ptid)
+{
+  struct fork_info *fp, *fpprev;
+
+  fpprev = NULL;
+
+  for (fp = fork_list; fp; fpprev = fp, fp = fp->next)
+    if (ptid_equal (fp->ptid, ptid))
+      break;
+
+  if (!fp)
+    return;
+
+  if (fpprev)
+    fpprev->next = fp->next;
+  else
+    fork_list = fp->next;
+
+  free_fork (fp);
+
+  /* Special case: if there is now only one process in the list, 
+     and if it is (hopefully!) the current inferior_ptid, then
+     remove it, leaving the list empty -- we're now down to the
+     default case of debugging a single process.  */
+  if (fork_list != NULL && fork_list->next == NULL &&
+      ptid_equal (fork_list->ptid, inferior_ptid))
+    {
+      /* Last fork -- delete from list and handle as solo process.  */
+      /* (Should be a safe recursion).  */
+      delete_fork (inferior_ptid);
+    }
+}
+
+/* Find a fork_info by matching PTID.  */
+static struct fork_info *
+find_fork_ptid (ptid_t ptid)
+{
+  struct fork_info *fp;
+
+  for (fp = fork_list; fp; fp = fp->next)
+    if (ptid_equal (fp->ptid, ptid))
+      return fp;
+
+  return NULL;
+}
+
+/* Find a fork_info by matching ID.  */
+static struct fork_info *
+find_fork_id (int num)
+{
+  struct fork_info *fp;
+
+  for (fp = fork_list; fp; fp = fp->next)
+    if (fp->num == num)
+      return fp;
+
+  return NULL;
+}
+
+/* Find a fork_info by matching pid.  */
+extern struct fork_info *
+find_fork_pid (pid_t pid)
+{
+  struct fork_info *fp;
+
+  for (fp = fork_list; fp; fp = fp->next)
+    if (pid == ptid_get_pid (fp->ptid))
+      return fp;
+
+  return NULL;
+}
+
+static ptid_t
+fork_id_to_ptid (int num)
+{
+  struct fork_info *fork = find_fork_id (num);
+  if (fork)
+    return fork->ptid;
+  else
+    return pid_to_ptid (-1);
+}
+
+/* FIXME: */
+static void
+init_fork_list (void)
+{
+  struct fork_info *fp, *fpnext;
+
+  highest_fork_num = 0;
+  if (!fork_list)
+    return;
+
+  for (fp = fork_list; fp; fp = fpnext)
+    {
+      fpnext = fp->next;
+      free_fork (fp);
+    }
+
+  fork_list = NULL;
+}
+
+/*
+ * Fork list <-> gdb interface:
+ */
+
+/* Load infrun state for the fork PTID.  */
+
+static void
+fork_load_infrun_state (struct fork_info *fp)
+{
+  extern void nullify_last_target_wait_ptid ();
+
+  if (fp->savedregs && fp->clobber_regs)
+    regcache_cpy (current_regcache, fp->savedregs);
+
+  nullify_last_target_wait_ptid ();
+}
+
+/* Save infrun state for the fork PTID.
+ * Exported for use by linux child_follow_fork.
+ */
+
+extern void
+fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
+{
+  if (fp->savedregs)
+    regcache_xfree (fp->savedregs);
+
+  fp->savedregs = regcache_dup (current_regcache);
+  fp->clobber_regs = clobber_regs;
+}
+
+/* linux_fork_killall.  Let God sort 'em out...  */
+
+extern void
+linux_fork_killall (void)
+{
+  /* Walk list and kill every pid.  No need to treat the
+     current inferior_ptid as special (we do not return a
+     status for it) -- however any process may be a child
+     or a parent, so may get a SIGCHLD from a previously
+     killed child.  Wait them all out.  */
+  pid_t pid, ret;
+  int status;
+
+  do {
+    pid = PIDGET (fork_list->ptid);
+    do {
+      ptrace (PT_KILL, pid, 0, 0);
+      ret = waitpid (pid, &status, 0);
+    } while (ret == pid && WIFSTOPPED (status));
+    delete_fork (fork_list->ptid);
+  } while (fork_list != NULL);
+}
+
+/* linux_fork_mourn_inferior.  The current inferior_ptid has exited,
+   but there are other viable forks to debug.  Delete the exiting one
+   and context-switch to the first available.
+*/
+extern void
+linux_fork_mourn_inferior (void)
+{
+  /* Wait just one more time to collect the inferior's exit status.
+     Do not check whether this succeeds though, since we may be
+     dealing with a process that we attached to.  Such a process will
+     only report its exit status to its origional parent.  */
+  int status;
+
+  waitpid (ptid_get_pid (inferior_ptid), &status, 0);
+
+  /* OK, presumably inferior_ptid is the one who has exited.
+     We need to delete that one from the fork_list, and switch
+     to the next available fork.  FIXME safety?  */
+  delete_fork (inferior_ptid);
+  inferior_ptid = fork_list[0].ptid;
+  printf_filtered ("[Switching to %s]\n", 
+                  target_pid_to_str (inferior_ptid));
+}
+
+/*
+ * Fork list <-> user interface:
+ */
+
+static void
+delete_fork_command (char *args, int from_tty)
+{
+  ptid_t ptid;
+
+  if (!args || !*args)
+    error ("Requires argument (checkpoint id to delete, see info checkpoint)");
+
+  /* FIXME: check for not-found!  */
+  /* FIXME: we can do better than strtol, too... */
+  ptid = fork_id_to_ptid (strtol (args, NULL, 0));
+  if (ptrace (PTRACE_KILL, ptid, 0, 0))
+    error ("Unable to kill pid %s", target_tid_to_str (ptid));
+
+  delete_fork (ptid);
+}
+
+static void
+detach_fork_command (char *args, int from_tty)
+{
+  ptid_t ptid;
+
+  if (!args || !*args)
+    error ("Requires argument (fork id to delete, see info fork)");
+
+  /* FIXME: check for not-found!  */
+  /* FIXME: we can do better than strtol, too... */
+  ptid = fork_id_to_ptid (strtol (args, NULL, 0));
+  if (ptid_equal (ptid, inferior_ptid))
+    error ("Please switch to another fork before detaching the current fork");
+
+  if (ptrace (PTRACE_DETACH, ptid, 0, 0))
+    error ("Unable to detach %s", target_pid_to_str (ptid));
+
+  if (from_tty)
+    printf_filtered ("Detached %s\n", target_pid_to_str (ptid));
+
+  delete_fork (ptid);
+}
+
+/* Print information about currently known forks.
+ */
+
+static void
+info_forks_command (char *arg, int from_tty)
+{
+  struct frame_info *cur_frame;
+  struct symtab_and_line sal;
+  struct symtab *cur_symtab;
+  struct fork_info *fp;
+  int cur_line;
+  ULONGEST pc;
+
+  for (fp = fork_list; fp; fp = fp->next)
+    {
+      if (ptid_equal (fp->ptid, inferior_ptid))
+       {
+         printf_filtered ("* ");
+         pc = read_pc ();
+       }
+      else
+       {
+         printf_filtered ("  ");
+         regcache_raw_read_unsigned (fp->savedregs, PC_REGNUM, &pc);
+       }
+      printf_filtered ("%d %s", fp->num, target_tid_to_str (fp->ptid));
+      if (fp->num == 0)
+       printf_filtered (" (main process)");
+      printf_filtered (" at ");
+      deprecated_print_address_numeric (pc, 1, gdb_stdout);
+
+      sal = find_pc_line (pc, 0);
+      if (sal.symtab)
+       printf_filtered (", file %s", sal.symtab->filename);
+      if (sal.line)
+       printf_filtered (", line %d", sal.line);
+      if (!sal.symtab && !sal.line)
+       {
+         struct minimal_symbol *msym;
+
+         msym = lookup_minimal_symbol_by_pc (pc);
+         if (msym)
+           printf_filtered (", <%s>", SYMBOL_LINKAGE_NAME (msym));
+       }
+
+      putchar_filtered ('\n');
+    }
+}
+
+static void
+checkpoint_command (char *args, int from_tty)
+{
+  struct target_waitstatus last_target_waitstatus;
+  ptid_t last_target_ptid;
+  struct value *fork_fn = NULL, *ret;
+  struct fork_info *fp;
+  pid_t retpid;
+  int save_detach_fork;
+  long i;
+
+  /* Make the inferior fork, record its (and gdb's) state.  */
+
+  if (lookup_minimal_symbol ("fork", NULL, NULL) != NULL)
+    fork_fn = find_function_in_inferior ("fork");
+  if (!fork_fn)
+    if (lookup_minimal_symbol ("_fork", NULL, NULL) != NULL)
+      fork_fn = find_function_in_inferior ("fork");
+  if (!fork_fn)
+    error ("checkpoint: can't find fork function in inferior.");
+
+  ret = value_from_longest (builtin_type_int, 0);
+  save_detach_fork = detach_fork;
+  detach_fork = 0;
+  ret = call_function_by_hand (fork_fn, 0, &ret);
+  detach_fork = save_detach_fork;
+  if (!ret)    /* Probably can't happen.  */
+    error ("checkpoint: call_function_by_hand returned null.");
+
+  retpid = value_as_long (ret);
+  get_last_target_status (&last_target_ptid, &last_target_waitstatus);
+  if (from_tty)
+    {
+      int parent_pid;
+
+      printf_filtered ("checkpoint: fork returned %ld.\n", (long) retpid);
+      parent_pid = ptid_get_lwp (last_target_ptid);
+      if (parent_pid == 0)
+       parent_pid = ptid_get_pid (last_target_ptid);
+      printf_filtered ("   gdb says parent = %ld.\n", (long) parent_pid);
+    }
+
+  fp = find_fork_pid (retpid);
+  if (!fp)
+    error ("Failed to find new fork");
+  fork_save_infrun_state (fp, 1);
+
+  if (info_verbose && from_tty)
+    {
+      printf_filtered ("retpid registers:\n");
+      errno = 0;
+      for (i = 0; errno == 0; i += 4)
+       printf_filtered ("0x%08lx\n", 
+                        ptrace (PTRACE_PEEKUSER, retpid, i, 0));
+      errno = 0;
+    }
+}
+
+#include "string.h"
+
+static int restart_auto_finish;
+
+static void
+restart_command (char *args, int from_tty)
+{
+  /* Now we attempt to switch processes.  */
+  struct fork_info *oldfp = find_fork_ptid (inferior_ptid);
+  struct fork_info *newfp;
+  ptid_t ptid;
+  int id, i;
+
+  if (!args || !*args)
+    error ("Requires argument (checkpoint or fork id, see info checkpoint)");
+
+  id = strtol (args, NULL, 0);
+  newfp = find_fork_id (id);
+  if (!newfp)
+    error ("No such checkpoint id: %d\n", id);
+
+  if (!oldfp)
+    {
+      oldfp = add_fork (ptid_get_pid (inferior_ptid));
+    }
+
+  fork_save_infrun_state (oldfp, 1);
+  oldfp->been_restarted = 1;
+  inferior_ptid = newfp->ptid;
+  fork_load_infrun_state (newfp);
+  registers_changed ();
+  reinit_frame_cache ();
+  stop_pc = read_pc ();
+  select_frame (get_current_frame ());
+
+  if (!newfp->been_restarted)
+    for (i = 0; i < restart_auto_finish; i++)
+      {
+       execute_command ("finish", from_tty);
+      }
+
+  newfp->been_restarted = 1;
+  printf_filtered ("Switching to %s\n", 
+                  target_pid_to_str (inferior_ptid));
+
+  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+}
+
+
+/* Extern because called from core gdb.  */
+extern void
+_initialize_linux_fork (void)
+{
+  init_fork_list ();
+
+  /* Set/show detach-on-fork: user-settable mode.  */
+
+  add_setshow_boolean_cmd ("detach-on-fork", class_obscure, &detach_fork, _("\
+Set whether gdb will detach the child of a fork."), _("\
+Show whether gdb will detach the child of a fork."), _("\
+Tells gdb whether to detach the child of a fork."), 
+                          NULL, NULL, &setlist, &showlist);
+
+  /* Set/show restart-auto-finish: user-settable count.  Causes the
+     first "restart" of a fork to do some number of "finish" commands
+     before returning to user.
+
+     Useful because otherwise the virgin fork process will be stopped
+     somewhere in the un-interesting fork system call.  */
+
+  add_setshow_integer_cmd ("restart-auto-finish", class_obscure, 
+                          &restart_auto_finish, _("\
+Set number of finish commands gdb should do on restart of a fork."), _("\
+Show number of finish commands gdb should do on restart of a fork."), _("\
+Tells gdb how many finish commands to do on restart of a fork."),
+                          NULL, NULL, &setlist, &showlist);
+
+  /* Checkpoint command: create a fork of the inferior process
+     and set it aside for later debugging.  */
+
+  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).  */
+
+  add_com ("restart", class_obscure, restart_command, _("\
+Switch between parent and child fork (experimental)."));
+
+  /* Delete-checkpoint command: kill the process and remove it from
+     fork list.  */
+
+  add_com ("delete-checkpoint", class_obscure, delete_fork_command, _("\
+Delete a fork/checkpoint (experimental)."));
+
+  /* Detach-checkpoint command: release the process to run independantly, 
+     and remove it from the fork list.  */
+
+  add_com ("detach-checkpoint", class_obscure, detach_fork_command, _("\
+Detach from a fork/checkpoint (experimental)."));
+
+  /* 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_com_alias ("delete-fork", "delete-checkpoint", class_obscure, 1);
+  add_com_alias ("detach-fork", "detach-checkpoint", class_obscure, 1);
+  add_info_alias ("forks", "checkpoints", 0);
+
+  /* "fork <n>" (by analogy to "thread <n>").  */
+
+  add_com_alias ("fork", "restart", class_obscure, 1);
+}
diff --git a/gdb/linux-fork.h b/gdb/linux-fork.h
new file mode 100644 (file)
index 0000000..c4c93ae
--- /dev/null
@@ -0,0 +1,33 @@
+/* GNU/Linux native-dependent code for debugging multiple forks.
+
+   Copyright 2005 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+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);
+
+struct fork_info *fork_list;
+#define FORKS_EXIST()  (fork_list != NULL)
+
+extern int detach_fork;
+
index ebba1b1b3f8c5db1686cb140e5ee0dd68c8cec89..74a48f1bcc1e9fbd4c6697a0e388a5c19541f183 100644 (file)
@@ -47,8 +47,6 @@
 #include "gdb_stat.h"          /* for struct stat */
 #include <fcntl.h>             /* for O_RDONLY */
 
-#include "infcall.h"           /* for call_function_by_hand */
-
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
 #endif
@@ -346,8 +344,6 @@ linux_child_post_startup_inferior (ptid_t ptid)
   linux_enable_event_reporting (ptid);
 }
 
-static int detach_fork = 1;
-
 int
 child_follow_fork (struct target_ops *ops, int follow_child)
 {
@@ -374,7 +370,7 @@ child_follow_fork (struct target_ops *ops, int follow_child)
         also, but they'll be reinserted below.  */
       detach_breakpoints (child_pid);
 
-      /* Don't detach if doing forky command.  */
+      /* Detach new forked process?  */
       if (detach_fork)
        {
          if (1/*debug_linux_nat*/)
@@ -621,22 +617,9 @@ kill_inferior (void)
     return;
 
   /* First cut -- let's crudely do everything inline.  */
-  if (fork_list)
+  if (FORKS_EXIST ())
     {
-      /* Walk list and kill every pid.  No need to treat the
-        current inferior_ptid as special (we do not return a
-        status for it) -- however any process may be a child
-        or a parent, so may get a SIGCHLD from a previously
-        killed child.  Wait them all out.  */
-      
-      do {
-       pid = PIDGET (fork_list->ptid);
-       do {
-         ptrace (PT_KILL, pid, 0, 0);
-         ret = waitpid (pid, &status, 0);
-       } while (ret == pid && WIFSTOPPED (status));
-       delete_fork (fork_list->ptid);
-      } while (fork_list != NULL);
+      linux_fork_killall ();
     }
   else
     {
@@ -1800,37 +1783,20 @@ child_mourn_inferior (void)
 {
   int status;
 
-  if (fork_list && 
-      fork_list->next == NULL &&
-      ptid_equal (fork_list->ptid, inferior_ptid))
-    {
-      /* Last fork -- delete from list and handle as solo process.  */
-      delete_fork (inferior_ptid);
-    }
-
-  if (fork_list == NULL)
+  if (! FORKS_EXIST ())
     {
       /* Normal case, no other forks available.  */
       super_mourn_inferior ();
       return;
     }
-
-  /* Multi-fork case.  */
-  /* Wait just one more time to collect the inferior's exit status.
-     Do not check whether this succeeds though, since we may be
-     dealing with a process that we attached to.  Such a process will
-     only report its exit status to its origional parent.  */
-  waitpid (ptid_get_pid (inferior_ptid), &status, 0);
-
-  /* OK, presumably inferior_ptid is the one who has exited.
-     We need to delete that one from the fork_list, and switch
-     to the next available fork.  FIXME safety?  */
-  delete_fork (inferior_ptid);
-  inferior_ptid = fork_list[0].ptid;
-  printf_filtered ("[Switching to %s]\n", 
-                  target_pid_to_str (inferior_ptid));
-
-  /* Is that enough?  Maybe infrun will take care of everything else... */
+  else
+    {
+      /* Multi-fork case.  The current inferior_ptid has exited,
+        but there are other viable forks to debug.  Delete the
+         exiting one and context-switch to the first available.  
+      */
+      linux_fork_mourn_inferior ();
+    }
 }
 
 /* We need to override child_wait to support attaching to cloned
@@ -3315,7 +3281,6 @@ linux_target (void)
 void
 _initialize_linux_nat (void)
 {
-  static void checkpoint_init (void);
   struct sigaction action;
   extern void thread_db_init (struct target_ops *);
 
@@ -3353,7 +3318,6 @@ Enables printf debugging output."),
                            NULL,
                            show_debug_linux_nat,
                            &setdebuglist, &showdebuglist);
-  checkpoint_init ();
 }
 \f
 
@@ -3422,453 +3386,3 @@ lin_thread_get_thread_signals (sigset_t *set)
   sigdelset (&suspend_mask, cancel);
 }
 
-/* Hack and slash, steal code from all over the place, 
-   and just try stuff out.  */
-
-/* Load infrun state for the fork PTID.  */
-
-static void
-fork_load_infrun_state (struct fork_info *fp)
-{
-  extern void nullify_last_target_wait_ptid ();
-
-  if (fp->savedregs && fp->clobber_regs)
-    regcache_cpy (current_regcache, fp->savedregs);
-
-  nullify_last_target_wait_ptid ();
-}
-
-/* Save infrun state for the fork PTID.  */
-
-extern void
-fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
-{
-  if (fp->savedregs)
-    regcache_xfree (fp->savedregs);
-
-  fp->savedregs = regcache_dup (current_regcache);
-  fp->clobber_regs = clobber_regs;
-}
-
-struct fork_info *fork_list = NULL;
-static int highest_fork_num;
-
-extern struct fork_info *
-add_fork (pid_t pid)
-{
-  struct fork_info *fp;
-
-  if (fork_list == NULL &&
-      pid != PIDGET (inferior_ptid))
-    {
-      /* Special case -- if this is the first fork in the list
-        (the list is hitherto empty), and if this new fork is
-        NOT the current inferior_ptid, then add inferior_ptid
-        first, as a special zeroeth fork id.  */
-      highest_fork_num = -1;
-      add_fork (PIDGET (inferior_ptid));       /* safe recursion */
-    }
-
-  fp = XZALLOC (struct fork_info);
-  fp->ptid = pid_to_ptid (pid);
-  fp->num = ++highest_fork_num;
-  fp->next = fork_list;
-  fork_list = fp;
-  return fp;
-}
-
-static void
-free_fork (struct fork_info *fp)
-{
-  /* FIXME: take care of any left-over step_resume breakpoints.  */
-  if (fp)
-    {
-      if (fp->savedregs)
-       regcache_xfree (fp->savedregs);
-      xfree (fp);
-    }
-}
-
-extern void
-delete_fork (ptid_t ptid)
-{
-  struct fork_info *fp, *fpprev;
-
-  fpprev = NULL;
-
-  for (fp = fork_list; fp; fpprev = fp, fp = fp->next)
-    if (ptid_equal (fp->ptid, ptid))
-      break;
-
-  if (!fp)
-    return;
-
-  if (fpprev)
-    fpprev->next = fp->next;
-  else
-    fork_list = fp->next;
-
-  free_fork (fp);
-}
-
-static struct fork_info *
-find_fork_id (int num)
-{
-  struct fork_info *fp;
-
-  for (fp = fork_list; fp; fp = fp->next)
-    if (fp->num == num)
-      return fp;
-
-  return NULL;
-}
-
-void
-init_fork_list (void)
-{
-  struct fork_info *fp, *fpnext;
-
-  highest_fork_num = 0;
-  if (!fork_list)
-    return;
-
-  for (fp = fork_list; fp; fp = fpnext)
-    {
-      fpnext = fp->next;
-      free_fork (fp);
-    }
-
-  fork_list = NULL;
-}
-
-/* Find a fork_info by matching PTID.  */
-static struct fork_info *
-find_fork_ptid (ptid_t ptid)
-{
-  struct fork_info *fp;
-
-  for (fp = fork_list; fp; fp = fp->next)
-    if (ptid_equal (fp->ptid, ptid))
-      return fp;
-
-  return NULL;
-}
-
-/* Find a fork_info by matching pid.  */
-extern struct fork_info *
-find_fork_pid (pid_t pid)
-{
-  struct fork_info *fp;
-
-  for (fp = fork_list; fp; fp = fp->next)
-    if (pid == ptid_get_pid (fp->ptid))
-      return fp;
-
-  return NULL;
-}
-
-
-
-/*
- * Fork iterator function.
- *
- * Calls a callback function once for each fork, so long as
- * the callback function returns false.  If the callback function
- * returns true, the iteration will end and the current fork
- * will be returned.  This can be useful for implementing a 
- * search for a fork with arbitrary attributes, or for applying
- * some operation to every fork.
- *
- * FIXME: some of the existing functionality, such as 
- * "Fork apply all", might be rewritten using this functionality.
- */
-
-struct fork_info *
-iterate_over_forks (int (*callback) (struct fork_info *, void *),
-                     void *data)
-{
-  struct fork_info *fp;
-
-  for (fp = fork_list; fp; fp = fp->next)
-    if ((*callback) (fp, data))
-      return fp;
-
-  return NULL;
-}
-
-int
-valid_fork_id (int num)
-{
-  struct fork_info *fp;
-
-  for (fp = fork_list; fp; fp = fp->next)
-    if (fp->num == num)
-      return 1;
-
-  return 0;
-}
-
-ptid_t
-fork_id_to_ptid (int num)
-{
-  struct fork_info *fork = find_fork_id (num);
-  if (fork)
-    return fork->ptid;
-  else
-    return pid_to_ptid (-1);
-}
-
-static void
-delete_checkpoint (char *args, int from_tty)
-{
-  ptid_t ptid;
-
-  if (!args || !*args)
-    error ("Requires argument (checkpoint id to delete, see info checkpoint)");
-
-  /* FIXME: check for not-found!  */
-  ptid = fork_id_to_ptid (strtol (args, NULL, 0));
-  if (ptrace (PTRACE_KILL, ptid, 0, 0))
-    error ("Unable to kill pid %s", target_tid_to_str (ptid));
-
-  delete_fork (ptid);
-}
-
-static void
-detach_fork_command (char *args, int from_tty)
-{
-  ptid_t ptid;
-
-  if (!args || !*args)
-    error ("Requires argument (fork id to delete, see info fork)");
-
-  /* FIXME: check for not-found!  */
-  ptid = fork_id_to_ptid (strtol (args, NULL, 0));
-  if (ptid_equal (ptid, inferior_ptid))
-    error ("Please switch to another fork before detaching the current fork");
-
-  if (ptrace (PTRACE_DETACH, ptid, 0, 0))
-    error ("Unable to detach %s", target_tid_to_str (ptid));
-
-  if (from_tty)
-    printf_filtered ("Detached %s\n", target_pid_or_tid_to_str (ptid));
-
-  delete_fork (ptid);
-}
-
-int
-in_fork_list (ptid_t ptid)
-{
-  struct fork_info *fp;
-
-  for (fp = fork_list; fp; fp = fp->next)
-    if (ptid_equal (fp->ptid, ptid))
-      return 1;
-
-  return 0;                    /* Never heard of 'im */
-}
-
-/* Print information about currently known forks 
-
- * Note: this has the drawback that it _really_ switches
- *       forks, which frees the frame cache.  A no-side
- *       effects info-forks command would be nicer.
- */
-
-static void
-info_forks_command (char *arg, int from_tty)
-{
-  struct frame_info *cur_frame;
-  struct symtab_and_line sal;
-  struct symtab *cur_symtab;
-  struct fork_info *fp;
-  int cur_line;
-  ULONGEST pc;
-
-  for (fp = fork_list; fp; fp = fp->next)
-    {
-      if (ptid_equal (fp->ptid, inferior_ptid))
-       {
-         printf_filtered ("* ");
-         pc = read_pc ();
-       }
-      else
-       {
-         printf_filtered ("  ");
-         regcache_raw_read_unsigned (fp->savedregs, PC_REGNUM, &pc);
-       }
-      printf_filtered ("%d %s", fp->num, target_tid_to_str (fp->ptid));
-      if (fp->num == 0)
-       printf_filtered (" (main process)");
-      printf_filtered (" at ");
-      deprecated_print_address_numeric (pc, 1, gdb_stdout);
-
-      sal = find_pc_line (pc, 0);
-      if (sal.symtab)
-       printf_filtered (", file %s", sal.symtab->filename);
-      if (sal.line)
-       printf_filtered (", line %d", sal.line);
-      if (!sal.symtab && !sal.line)
-       {
-         struct minimal_symbol *msym;
-
-         msym = lookup_minimal_symbol_by_pc (pc);
-         if (msym)
-           printf_filtered (", <%s>", SYMBOL_LINKAGE_NAME (msym));
-       }
-
-      putchar_filtered ('\n');
-    }
-}
-
-static void
-checkpoint_command (char *args, int from_tty)
-{
-  struct target_waitstatus last_target_waitstatus;
-  ptid_t last_target_ptid;
-  struct value *fork_fn = NULL, *ret;
-  struct fork_info *fp;
-  pid_t retpid;
-  int save_detach_fork;
-  long i;
-
-  /* Make the inferior fork, record its (and gdb's) state.  */
-
-  if (lookup_minimal_symbol ("fork", NULL, NULL) != NULL)
-    fork_fn = find_function_in_inferior ("fork");
-  if (!fork_fn)
-    if (lookup_minimal_symbol ("_fork", NULL, NULL) != NULL)
-      fork_fn = find_function_in_inferior ("fork");
-  if (!fork_fn)
-    error ("checkpoint: can't find fork function in inferior.");
-
-  ret = value_from_longest (builtin_type_int, 0);
-  save_detach_fork = detach_fork;
-  detach_fork = 0;
-  ret = call_function_by_hand (fork_fn, 0, &ret);
-  detach_fork = save_detach_fork;
-  if (!ret)    /* Probably can't happen.  */
-    error ("checkpoint: call_function_by_hand returned null.");
-
-  retpid = value_as_long (ret);
-  get_last_target_status (&last_target_ptid, &last_target_waitstatus);
-  if (from_tty)
-    {
-      int parent_pid;
-
-      printf_filtered ("checkpoint: fork returned %ld.\n", (long) retpid);
-      parent_pid = ptid_get_lwp (last_target_ptid);
-      if (parent_pid == 0)
-       parent_pid = ptid_get_pid (last_target_ptid);
-      printf_filtered ("   gdb says parent = %ld.\n", (long) parent_pid);
-    }
-
-#if 0 /* child_follow_fork will have created the new fork.
-        We still need to save its register state, to update
-        the savedregs.  */
-  fp = add_fork (retpid);
-  fork_save_infrun_state (fp, 1);
-#else
-  fp = find_fork_ptid (pid_to_ptid (retpid));
-  if (!fp)
-    error ("Failed to find new fork");
-  fork_save_infrun_state (fp, 1);
-#endif
-
-  if (info_verbose && from_tty)
-    {
-      printf_filtered ("retpid registers:\n");
-      errno = 0;
-      for (i = 0; errno == 0; i += 4)
-       printf_filtered ("0x%08lx\n", 
-                        ptrace (PTRACE_PEEKUSER, retpid, i, 0));
-      errno = 0;
-    }
-}
-
-#include "string.h"
-
-static int restart_auto_finish;
-
-static void
-restart_command (char *args, int from_tty)
-{
-  /* Now we attempt to switch processes.  */
-  struct fork_info *oldfp = find_fork_ptid (inferior_ptid);
-  struct fork_info *newfp;
-  ptid_t ptid;
-  int id, i;
-
-  if (!args || !*args)
-    error ("Requires argument (checkpoint or fork id, see info checkpoint)");
-
-  id = strtol (args, NULL, 0);
-  newfp = find_fork_id (id);
-  if (!newfp)
-    error ("No such checkpoint id: %d\n", id);
-
-  if (!oldfp)
-    {
-      oldfp = add_fork (ptid_get_pid (inferior_ptid));
-    }
-
-  fork_save_infrun_state (oldfp, 1);
-  oldfp->been_restarted = 1;
-  inferior_ptid = newfp->ptid;
-  fork_load_infrun_state (newfp);
-  registers_changed ();
-  /* FIXME lose this.  */
-#if 0
-  target_fetch_registers (-1); /* FIXME should not be necessary;
-                                  fill_gregset should do it automatically. */
-#endif
-  reinit_frame_cache ();
-  stop_pc = read_pc ();
-  select_frame (get_current_frame ());
-
-  if (!newfp->been_restarted)
-    for (i = 0; i < restart_auto_finish; i++)
-      {
-       execute_command ("finish", from_tty);
-      }
-
-  newfp->been_restarted = 1;
-  printf_filtered ("Switching to %s\n", 
-                  target_pid_to_str (inferior_ptid));
-
-  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
-}
-
-static void
-checkpoint_init (void)
-{
-  init_fork_list ();
-
-  add_setshow_boolean_cmd ("detach-on-fork", class_obscure, &detach_fork, _("\
-Set whether gdb will detach the child of a fork."), _("\
-Show whether gdb will detach the child of a fork."), _("\
-Tells gdb whether to detach the child of a fork."), 
-                          NULL, NULL, &setlist, &showlist);
-
-  add_setshow_integer_cmd ("restart-auto-finish", class_obscure, 
-                          &restart_auto_finish, _("\
-Set number of finish commands gdb should do on restart of a fork."), _("\
-Show number of finish commands gdb should do on restart of a fork."), _("\
-Tells gdb how many finish commands to do on restart of a fork."),
-                          NULL, NULL, &setlist, &showlist);
-
-
-  add_com ("checkpoint", class_obscure, checkpoint_command, _("\
-Fork a duplicate process (experimental)."));
-  add_com ("restart", class_obscure, restart_command, _("\
-Switch between parent and child fork (experimental)."));
-  add_com_alias ("fork", "restart", class_obscure, 1);
-  add_com ("delete-checkpoint", class_obscure, delete_checkpoint, _("\
-Delete a fork/checkpoint (experimental)."));
-  add_com_alias ("delete-fork", "delete-checkpoint", class_obscure, 1);
-  add_com ("detach-fork", class_obscure, detach_fork_command, _("\
-Detach from a fork/checkpoint (experimental)."));
-  add_info ("checkpoints", info_forks_command,
-           _("IDs of currently known forks/checkpoints."));
-  add_info_alias ("forks", "checkpoints", 0);
-}