diff options
author | Michael Snyder <msnyder@vmware.com> | 2006-01-04 19:34:58 +0000 |
---|---|---|
committer | Michael Snyder <msnyder@vmware.com> | 2006-01-04 19:34:58 +0000 |
commit | ac264b3b9cf16064cea9011faf07ec61babb0ef8 (patch) | |
tree | 02269be136dcba8affaec7ce4be33ba4c35ff827 /gdb/linux-nat.c | |
parent | 5c95884b4cb77d6a902ade2f566eb71f9a322dd0 (diff) | |
download | gdb-ac264b3b9cf16064cea9011faf07ec61babb0ef8.zip gdb-ac264b3b9cf16064cea9011faf07ec61babb0ef8.tar.gz gdb-ac264b3b9cf16064cea9011faf07ec61babb0ef8.tar.bz2 |
2006-01-04 Michael Snyder <msnyder@redhat.com>
Checkpoint/Restart for Linux.
* linux-nat.c: Add support for debugging multiple forks.
Add #include for linux-fork.h (interface spec).
(super_mourn_inferior): New function pointer.
(child_mourn_inferior): New function / target method.
(linux_target): Claim to_mourn_inferior method pointer.
(child_follow_fork): Call interface to linux-fork, conditionally
add new fork processes to list of debugged processes.
(kill_inferior): Use interface to linux-fork to kill
multiple processes.
* linux-fork.h: New file.
* linux-fork.c: New file. Support for debugging multiple forks
of the same program. Support for checkpoint and restart commands.
* infrun.c (nullify_last_target_wait_ptid): New function.
* Makefile.in: Add linux-fork.
* config/*/linux.mh: Add linux-fork.
* NEWS: Mention new functionality.
Diffstat (limited to 'gdb/linux-nat.c')
-rw-r--r-- | gdb/linux-nat.c | 139 |
1 files changed, 104 insertions, 35 deletions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 786e549..7b4bed8 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -31,6 +31,7 @@ #endif #include <sys/ptrace.h> #include "linux-nat.h" +#include "linux-fork.h" #include "gdbthread.h" #include "gdbcmd.h" #include "regcache.h" @@ -87,12 +88,18 @@ the use of the multi-threaded target. */ static struct target_ops *linux_ops; -/* The saved to_xfer_partial method, inherited from inf-ptrace.c. Called - by our to_xfer_partial. */ -static LONGEST (*super_xfer_partial) (struct target_ops *, enum target_object, - const char *, gdb_byte *, const gdb_byte *, +/* The saved to_xfer_partial method, inherited from inf-ptrace.c. + Called by our to_xfer_partial. */ +static LONGEST (*super_xfer_partial) (struct target_ops *, + enum target_object, + const char *, gdb_byte *, + const gdb_byte *, ULONGEST, LONGEST); +/* The saved to_mourn_inferior method, inherited from inf-ptrace.c. + Called by our to_mourn_inferior. */ +static void (*super_mourn_inferior) (void); + static int debug_linux_nat; static void show_debug_linux_nat (struct ui_file *file, int from_tty, @@ -363,15 +370,28 @@ child_follow_fork (struct target_ops *ops, int follow_child) also, but they'll be reinserted below. */ detach_breakpoints (child_pid); - if (debug_linux_nat) + /* Detach new forked process? */ + if (detach_fork) { - target_terminal_ours (); - fprintf_unfiltered (gdb_stdlog, - "Detaching after fork from child process %d.\n", - child_pid); - } + if (debug_linux_nat) + { + target_terminal_ours (); + fprintf_filtered (gdb_stdlog, + "Detaching after fork from child process %d.\n", + child_pid); + } - ptrace (PTRACE_DETACH, child_pid, 0, 0); + 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) { @@ -441,9 +461,9 @@ child_follow_fork (struct target_ops *ops, int follow_child) if (debug_linux_nat) { target_terminal_ours (); - fprintf_unfiltered (gdb_stdlog, - "Attaching after fork to child process %d.\n", - child_pid); + fprintf_filtered (gdb_stdlog, + "Attaching after fork to child process %d.\n", + child_pid); } /* If we're vforking, we may want to hold on to the parent until @@ -466,13 +486,25 @@ 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); + { + target_detach (NULL, 0); + } inferior_ptid = pid_to_ptid (child_pid); /* Reinstall ourselves, since we might have been removed in target_detach (which does other necessary cleanup). */ + push_target (ops); /* Reset breakpoints in the child as appropriate. */ @@ -579,33 +611,42 @@ kill_inferior (void) if (pid == 0) return; - /* If we're stopped while forking and we haven't followed yet, kill the - other task. We need to do this first because the parent will be - sleeping if this is a vfork. */ - - get_last_target_status (&last_ptid, &last); - - if (last.kind == TARGET_WAITKIND_FORKED - || last.kind == TARGET_WAITKIND_VFORKED) + /* First cut -- let's crudely do everything inline. */ + if (forks_exist_p ()) { - ptrace (PT_KILL, last.value.related_pid, 0, 0); - wait (&status); + linux_fork_killall (); + pop_target (); + generic_mourn_inferior (); } + else + { + /* If we're stopped while forking and we haven't followed yet, + kill the other task. We need to do this first because the + parent will be sleeping if this is a vfork. */ - /* Kill the current process. */ - ptrace (PT_KILL, pid, 0, 0); - ret = wait (&status); + get_last_target_status (&last_ptid, &last); - /* We might get a SIGCHLD instead of an exit status. This is - aggravated by the first kill above - a child has just died. */ + if (last.kind == TARGET_WAITKIND_FORKED + || last.kind == TARGET_WAITKIND_VFORKED) + { + ptrace (PT_KILL, last.value.related_pid, 0, 0); + wait (&status); + } - while (ret == pid && WIFSTOPPED (status)) - { + /* Kill the current process. */ ptrace (PT_KILL, pid, 0, 0); ret = wait (&status); - } - target_mourn_inferior (); + /* We might get a SIGCHLD instead of an exit status. This is + aggravated by the first kill above - a child has just died. */ + + while (ret == pid && WIFSTOPPED (status)) + { + ptrace (PT_KILL, pid, 0, 0); + ret = wait (&status); + } + target_mourn_inferior (); + } } /* On GNU/Linux there are no real LWP's. The closest thing to LWP's @@ -1677,7 +1718,7 @@ select_event_lwp (struct lwp_info **orig_lp, int *status) int random_selector; struct lwp_info *event_lp; - /* Record the wait status for the origional LWP. */ + /* Record the wait status for the original LWP. */ (*orig_lp)->status = *status; /* Give preference to any LWP that is being single-stepped. */ @@ -1729,6 +1770,30 @@ resumed_callback (struct lwp_info *lp, void *data) return lp->resumed; } +/* Local mourn_inferior -- we need to override mourn_inferior + so that we can do something clever if one of several forks + has exited. */ + +static void +child_mourn_inferior (void) +{ + int status; + + if (! forks_exist_p ()) + { + /* Normal case, no other forks available. */ + super_mourn_inferior (); + return; + } + 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 processes, since a normal wait (as done by the default version) ignores those processes. */ @@ -3201,6 +3266,9 @@ linux_target (void) super_xfer_partial = t->to_xfer_partial; t->to_xfer_partial = linux_xfer_partial; + super_mourn_inferior = t->to_mourn_inferior; + t->to_mourn_inferior = child_mourn_inferior; + linux_ops = t; return t; } @@ -3312,3 +3380,4 @@ lin_thread_get_thread_signals (sigset_t *set) /* ... except during a sigsuspend. */ sigdelset (&suspend_mask, cancel); } + |