/* GNU/Linux native-dependent code common to multiple platforms.
- Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GDB.
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. */
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "defs.h"
#include "inferior.h"
#endif
#include <sys/ptrace.h>
#include "linux-nat.h"
+#include "linux-fork.h"
#include "gdbthread.h"
#include "gdbcmd.h"
#include "regcache.h"
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,
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)
{
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
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. */
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
struct lwp_info *lp;
int resume_all;
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLR: Preparing to %s %s, %s, inferior_ptid %s\n",
+ step ? "step" : "resume",
+ target_pid_to_str (ptid),
+ signo ? strsignal (signo) : "0",
+ target_pid_to_str (inferior_ptid));
+
/* A specific PTID means `step only this process id'. */
resume_all = (PIDGET (ptid) == -1);
lp->resumed = 1;
/* If we have a pending wait status for this thread, there is no
- point in resuming the process. */
+ point in resuming the process. But first make sure that
+ linux_nat_wait won't preemptively handle the event - we
+ should never take this short-circuit if we are going to
+ leave LP running, since we have skipped resuming all the
+ other threads. This bit of code needs to be synchronized
+ with linux_nat_wait. */
+
+ if (lp->status && WIFSTOPPED (lp->status))
+ {
+ int saved_signo = target_signal_from_host (WSTOPSIG (lp->status));
+
+ if (signal_stop_state (saved_signo) == 0
+ && signal_print_state (saved_signo) == 0
+ && signal_pass_state (saved_signo) == 1)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLR: Not short circuiting for ignored "
+ "status 0x%x\n", lp->status);
+
+ /* FIXME: What should we do if we are supposed to continue
+ this thread with a signal? */
+ gdb_assert (signo == TARGET_SIGNAL_0);
+ signo = saved_signo;
+ lp->status = 0;
+ }
+ }
+
if (lp->status)
{
/* FIXME: What should we do if we are supposed to continue
this thread with a signal? */
gdb_assert (signo == TARGET_SIGNAL_0);
+
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLR: Short circuiting for status 0x%x\n",
+ lp->status);
+
return;
}
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. */
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. */
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;
}
/* ... except during a sigsuspend. */
sigdelset (&suspend_mask, cancel);
}
+