aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Snyder <msnyder@vmware.com>2005-11-28 22:28:33 +0000
committerMichael Snyder <msnyder@vmware.com>2005-11-28 22:28:33 +0000
commit65e867da06bae863a10781dd58add44fe4f54791 (patch)
tree963d8e937d808827cf74083e24e4029f9a57e203
parent1d2d1dd46fb45402443192a14cd2e485596f242f (diff)
downloadgdb-65e867da06bae863a10781dd58add44fe4f54791.zip
gdb-65e867da06bae863a10781dd58add44fe4f54791.tar.gz
gdb-65e867da06bae863a10781dd58add44fe4f54791.tar.bz2
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.
-rw-r--r--gdb/ChangeLog9
-rw-r--r--gdb/linux-fork.c545
-rw-r--r--gdb/linux-fork.h33
-rw-r--r--gdb/linux-nat.c510
4 files changed, 599 insertions, 498 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 0c49ef3..66cf234 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -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
index 0000000..a262238
--- /dev/null
+++ b/gdb/linux-fork.c
@@ -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
index 0000000..c4c93ae
--- /dev/null
+++ b/gdb/linux-fork.h
@@ -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;
+
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index ebba1b1..74a48f1 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -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 ();
}
@@ -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);
-}