aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog20
-rw-r--r--gdb/linux-nat.c157
2 files changed, 139 insertions, 38 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index f7bc6a9..3edcd14 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,23 @@
+2005-11-23 Michael Snyder <msnyder@redhat.com>
+
+ * linux-nat.c: Adapt fork list to work with follow_fork.
+
+ (child_follow_fork): Add non-detached fork to fork list.
+ Handle follow-child as well as follow-parent.
+ (fork_load_infrun_state, fork_save_infrun_state):
+ Don't save any actual infrun state except for last_ptid.
+ Just save registers, and add the option of NOT restoring them.
+ Also don't leak regcache objects.
+ (add_fork): Accept a pid instead of a ptid.
+ (find_fork_pid): New function.
+ (pid_to_fork_id): Delete function.
+ (detach_fork_command): New command.
+ (checkpoint_command): Remember previous setting of detach_fork.
+ Don't call add_fork, it's now done by child_follow_fork.
+ FIXME get rid of call to target_fetch_registers.
+ (checkpoint_init): Add setshow detach-on-fork.
+ Add detach-fork.
+
2005-11-21 Michael Snyder <msnyder@redhat.com>
* linux-nat.c: Add crude checkpoint/restart using forks.
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 1f5fb36..d791736 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -339,7 +339,11 @@ linux_child_post_startup_inferior (ptid_t ptid)
linux_enable_event_reporting (ptid);
}
-int forky_forky;
+struct fork_info;
+static int detach_fork = 1;
+static struct fork_info *add_fork (pid_t);
+static struct fork_info *find_fork_pid (pid_t);
+static void fork_save_infrun_state (struct fork_info *, int);
int
child_follow_fork (struct target_ops *ops, int follow_child)
@@ -376,8 +380,19 @@ child_follow_fork (struct target_ops *ops, int follow_child)
}
/* Don't detach if doing forky command. */
- if (!forky_forky)
- ptrace (PTRACE_DETACH, child_pid, 0, 0);
+ if (detach_fork)
+ {
+ ptrace (PTRACE_DETACH, child_pid, 0, 0);
+ }
+ else
+ {
+ struct fork_info *fp;
+ /* 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);
+ }
if (has_vforked)
{
@@ -472,6 +487,15 @@ child_follow_fork (struct target_ops *ops, int follow_child)
if (has_vforked)
linux_parent_pid = 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);
+ }
else
target_detach (NULL, 0);
@@ -479,6 +503,11 @@ child_follow_fork (struct target_ops *ops, int follow_child)
/* Reinstall ourselves, since we might have been removed in
target_detach (which does other necessary cleanup). */
+ /* FIXME: As written, we may do a push_target WITHOUT doing
+ a target_detach (if has_vforked). I'm going to momentarily
+ assume that that means it's OK to do the same if we've
+ forked but not detached. */
+
push_target (ops);
/* Reset breakpoints in the child as appropriate. */
@@ -3333,6 +3362,7 @@ struct fork_info
int num; /* Convenient handle (GDB fork id) */
struct inferior_status *status;
struct regcache *savedregs;
+ int clobber_regs; /* True if we should restore saved regs. */
ptid_t last_target_ptid;
struct target_waitstatus last_target_waitstatus;
@@ -3343,38 +3373,47 @@ struct fork_info
static void
fork_load_infrun_state (struct fork_info *fp)
{
+ extern void set_last_target_status (ptid_t, struct target_waitstatus);
+
if (fp->status)
{
restore_inferior_status (fp->status);
fp->status = NULL;
}
- if (fp->savedregs)
+ if (fp->savedregs && fp->clobber_regs)
regcache_cpy (current_regcache, fp->savedregs);
+
set_last_target_status (fp->ptid, fp->last_target_waitstatus);
}
/* Save infrun state for the fork PTID. */
static void
-fork_save_infrun_state (struct fork_info *fp)
+fork_save_infrun_state (struct fork_info *fp, int clobber_regs)
{
+#if 0
+ /* I claim that I no longer need any infrun status. */
fp->status = save_inferior_status (0);
+#endif
+ if (fp->savedregs)
+ regcache_xfree (fp->savedregs);
+
fp->savedregs = regcache_dup (current_regcache);
get_last_target_status (&fp->last_target_ptid,
&fp->last_target_waitstatus);
+ fp->clobber_regs = clobber_regs;
}
static struct fork_info *fork_list = NULL;
static int highest_fork_num;
-struct fork_info *
-add_fork (ptid_t ptid)
+static struct fork_info *
+add_fork (pid_t pid)
{
struct fork_info *fp;
- fp = (struct fork_info *) xmalloc (sizeof (*fp));
- memset (fp, 0, sizeof (*fp));
- fp->ptid = ptid;
+ fp = XZALLOC (struct fork_info);
+ fp->ptid = pid_to_ptid (pid);
fp->num = ++highest_fork_num;
fp->next = fork_list;
fork_list = fp;
@@ -3448,7 +3487,7 @@ init_fork_list (void)
}
/* Find a fork_info by matching PTID. */
-struct fork_info *
+static struct fork_info *
find_fork_ptid (ptid_t ptid)
{
struct fork_info *fp;
@@ -3460,6 +3499,21 @@ find_fork_ptid (ptid_t ptid)
return NULL;
}
+/* Find a fork_info by matching pid. */
+static 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.
*
@@ -3499,18 +3553,6 @@ valid_fork_id (int num)
return 0;
}
-int
-pid_to_fork_id (ptid_t ptid)
-{
- struct fork_info *fp;
-
- for (fp = fork_list; fp; fp = fp->next)
- if (ptid_equal (fp->ptid, ptid))
- return fp->num;
-
- return 0;
-}
-
ptid_t
fork_id_to_ptid (int num)
{
@@ -3529,6 +3571,7 @@ delete_checkpoint (char *args, int from_tty)
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));
@@ -3536,6 +3579,28 @@ delete_checkpoint (char *args, int from_tty)
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 pid %s", target_tid_to_str (ptid));
+
+ if (from_tty)
+ printf_filtered ("Detached process %s\n",
+ target_pid_or_tid_to_str (inferior_ptid));
+ delete_fork (ptid);
+}
+
int
in_fork_list (ptid_t ptid)
{
@@ -3594,6 +3659,7 @@ checkpoint_command (char *args, int from_tty)
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. */
@@ -3607,9 +3673,10 @@ checkpoint_command (char *args, int from_tty)
error ("checkpoint: can't find fork function in inferior.");
ret = value_from_longest (builtin_type_int, 0);
- forky_forky = 1;
+ save_detach_fork = detach_fork;
+ detach_fork = 0;
ret = call_function_by_hand (fork_fn, 0, &ret);
- forky_forky = 0;
+ detach_fork = save_detach_fork;
if (!ret) /* Probably can't happen. */
error ("checkpoint: call_function_by_hand returned null.");
@@ -3626,12 +3693,17 @@ checkpoint_command (char *args, int from_tty)
printf_filtered (" gdb says parent = %ld.\n", (long) parent_pid);
}
- fp = add_fork (pid_to_ptid (retpid));
- fork_save_infrun_state (fp);
-
- /* FIXME: The new fork is still in the call_function_by_hand
- state, in the call to fork. It should be sufficient to just
- copy its registers from the current state. */
+#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)
{
@@ -3665,14 +3737,17 @@ restart_command (char *args, int from_tty)
if (!oldfp)
{
- oldfp = add_fork (inferior_ptid);
+ oldfp = add_fork (ptid_get_pid (inferior_ptid));
}
- fork_save_infrun_state (oldfp);
+ fork_save_infrun_state (oldfp, 1);
inferior_ptid = newfp->ptid;
fork_load_infrun_state (newfp);
- flush_cached_frames ();
registers_changed ();
+ /* FIXME lose this. */
+ target_fetch_registers (-1); /* FIXME should not be necessary;
+ fill_gregset should do it automatically. */
+ reinit_frame_cache ();
stop_pc = read_pc ();
select_frame (get_current_frame ());
printf_filtered ("Switching to %s\n",
@@ -3680,19 +3755,25 @@ restart_command (char *args, int from_tty)
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_com ("checkpoint", class_obscure, checkpoint_command, _("\
Fork a duplicate process (experimental)."));
add_com ("restart", class_obscure, restart_command, _("\
-Flip from parent to child (experimental)."));
+Flip from parent to child fork (experimental)."));
add_com ("delete-checkpoint", class_obscure, delete_checkpoint, _("\
Delete a fork/checkpoint (experimental)."));
+ 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);