/* GNU/Linux native-dependent code common to multiple platforms.
- Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
+ Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
This file is part of GDB.
#include "gdbthread.h"
#include "gdbcmd.h"
#include "regcache.h"
+#include "regset.h"
#include "inf-ptrace.h"
#include "auxv.h"
#include <sys/param.h> /* for MAXPATHLEN */
/* The single-threaded native GNU/Linux target_ops. We save a pointer for
the use of the multi-threaded target. */
static struct target_ops *linux_ops;
+static struct target_ops linux_ops_saved;
/* The saved to_xfer_partial method, inherited from inf-ptrace.c.
Called by our to_xfer_partial. */
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,
struct simple_pid_list
{
int pid;
+ int status;
struct simple_pid_list *next;
};
struct simple_pid_list *stopped_pids;
/* Trivial list manipulation functions to keep track of a list of
new stopped processes. */
static void
-add_to_pid_list (struct simple_pid_list **listp, int pid)
+add_to_pid_list (struct simple_pid_list **listp, int pid, int status)
{
struct simple_pid_list *new_pid = xmalloc (sizeof (struct simple_pid_list));
new_pid->pid = pid;
+ new_pid->status = status;
new_pid->next = *listp;
*listp = new_pid;
}
static int
-pull_pid_from_list (struct simple_pid_list **listp, int pid)
+pull_pid_from_list (struct simple_pid_list **listp, int pid, int *status)
{
struct simple_pid_list **p;
if ((*p)->pid == pid)
{
struct simple_pid_list *next = (*p)->next;
+ *status = (*p)->status;
xfree (*p);
*p = next;
return 1;
return 0;
}
-void
-linux_record_stopped_pid (int pid)
+static void
+linux_record_stopped_pid (int pid, int status)
{
- add_to_pid_list (&stopped_pids, pid);
+ add_to_pid_list (&stopped_pids, pid, status);
}
\f
ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
if (ret != 0)
warning (_("linux_test_for_tracefork: failed to kill second child"));
+ my_waitpid (second_pid, &status, 0);
}
}
else
child_post_attach (int pid)
{
linux_enable_event_reporting (pid_to_ptid (pid));
+ check_for_thread_db ();
}
static void
linux_child_post_startup_inferior (ptid_t ptid)
{
linux_enable_event_reporting (ptid);
+ check_for_thread_db ();
}
int
return 0;
}
-ptid_t
-linux_handle_extended_wait (int pid, int status,
- struct target_waitstatus *ourstatus)
-{
- int event = status >> 16;
-
- if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
- || event == PTRACE_EVENT_CLONE)
- {
- unsigned long new_pid;
- int ret;
-
- ptrace (PTRACE_GETEVENTMSG, pid, 0, &new_pid);
-
- /* If we haven't already seen the new PID stop, wait for it now. */
- if (! pull_pid_from_list (&stopped_pids, new_pid))
- {
- /* The new child has a pending SIGSTOP. We can't affect it until it
- hits the SIGSTOP, but we're already attached. */
- ret = my_waitpid (new_pid, &status,
- (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
- if (ret == -1)
- perror_with_name (_("waiting for new child"));
- else if (ret != new_pid)
- internal_error (__FILE__, __LINE__,
- _("wait returned unexpected PID %d"), ret);
- else if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGSTOP)
- internal_error (__FILE__, __LINE__,
- _("wait returned unexpected status 0x%x"), status);
- }
-
- if (event == PTRACE_EVENT_FORK)
- ourstatus->kind = TARGET_WAITKIND_FORKED;
- else if (event == PTRACE_EVENT_VFORK)
- ourstatus->kind = TARGET_WAITKIND_VFORKED;
- else
- ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
-
- ourstatus->value.related_pid = new_pid;
- return inferior_ptid;
- }
-
- if (event == PTRACE_EVENT_EXEC)
- {
- ourstatus->kind = TARGET_WAITKIND_EXECD;
- ourstatus->value.execd_pathname
- = xstrdup (child_pid_to_exec_file (pid));
-
- if (linux_parent_pid)
- {
- detach_breakpoints (linux_parent_pid);
- ptrace (PTRACE_DETACH, linux_parent_pid, 0, 0);
-
- linux_parent_pid = 0;
- }
-
- return inferior_ptid;
- }
-
- internal_error (__FILE__, __LINE__,
- _("unknown ptrace event %d"), event);
-}
-
\f
void
child_insert_fork_catchpoint (int pid)
error (_("Your system does not support exec catchpoints."));
}
-void
-kill_inferior (void)
-{
- int status;
- int pid = PIDGET (inferior_ptid);
- struct target_waitstatus last;
- ptid_t last_ptid;
- int ret;
-
- if (pid == 0)
- return;
-
- /* First cut -- let's crudely do everything inline. */
- if (forks_exist_p ())
- {
- linux_fork_killall ();
- }
- 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. */
-
- get_last_target_status (&last_ptid, &last);
-
- if (last.kind == TARGET_WAITKIND_FORKED
- || last.kind == TARGET_WAITKIND_VFORKED)
- {
- ptrace (PT_KILL, last.value.related_pid, 0, 0);
- wait (&status);
- }
-
- /* Kill the current process. */
- ptrace (PT_KILL, pid, 0, 0);
- ret = wait (&status);
-
- /* 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
are processes sharing the same VM space. A multi-threaded process
is basically a group of such processes. However, such a grouping
/* Number of LWPs in the list. */
static int num_lwps;
-
-/* Non-zero if we're running in "threaded" mode. */
-static int threaded;
\f
#define GET_LWP(ptid) ptid_get_lwp (ptid)
ptid_t trap_ptid;
\f
-/* This module's target-specific operations. */
-static struct target_ops linux_nat_ops;
-
/* Since we cannot wait (in linux_nat_wait) for the initial process and
any cloned processes with a single call to waitpid, we have to use
the WNOHANG flag and call waitpid in a loop. To optimize
lwp_list = NULL;
num_lwps = 0;
- threaded = 0;
}
-/* Add the LWP specified by PID to the list. If this causes the
- number of LWPs to become larger than one, go into "threaded" mode.
- Return a pointer to the structure describing the new LWP. */
+/* Add the LWP specified by PID to the list. Return a pointer to the
+ structure describing the new LWP. */
static struct lwp_info *
add_lwp (ptid_t ptid)
lp->next = lwp_list;
lwp_list = lp;
- if (++num_lwps > 1)
- threaded = 1;
+ ++num_lwps;
return lp;
}
if (!lp)
return;
- /* We don't go back to "non-threaded" mode if the number of threads
- becomes less than two. */
num_lwps--;
if (lpprev)
return NULL;
}
+/* Update our internal state when changing from one fork (checkpoint,
+ et cetera) to another indicated by NEW_PTID. We can only switch
+ single-threaded applications, so we only create one new LWP, and
+ the previous list is discarded. */
+
+void
+linux_nat_switch_fork (ptid_t new_ptid)
+{
+ struct lwp_info *lp;
+
+ init_lwp_list ();
+ lp = add_lwp (new_ptid);
+ lp->stopped = 1;
+}
+
/* Record a PTID for later deletion. */
struct saved_ptids
struct thread_info *thr;
thr = iterate_over_threads (find_thread_from_lwp, &lp->ptid);
- if (thr && !ptid_equal (thr->ptid, inferior_ptid))
- delete_thread (thr->ptid);
- else
- record_dead_thread (thr->ptid);
+ if (thr)
+ {
+ if (!ptid_equal (thr->ptid, inferior_ptid))
+ delete_thread (thr->ptid);
+ else
+ record_dead_thread (thr->ptid);
+ }
}
delete_lwp (lp->ptid);
/* Attach to the LWP specified by PID. If VERBOSE is non-zero, print
a message telling the user that a new LWP has been added to the
- process. */
+ process. Return 0 if successful or -1 if the new LWP could not
+ be attached. */
-void
+int
lin_lwp_attach_lwp (ptid_t ptid, int verbose)
{
- struct lwp_info *lp, *found_lp;
+ struct lwp_info *lp;
gdb_assert (is_lwp (ptid));
sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
}
- if (verbose)
- printf_filtered (_("[New %s]\n"), target_pid_to_str (ptid));
-
- found_lp = lp = find_lwp_pid (ptid);
- if (lp == NULL)
- lp = add_lwp (ptid);
+ lp = find_lwp_pid (ptid);
/* We assume that we're already attached to any LWP that has an id
equal to the overall process id, and to any LWP that is already
and we've had PID wraparound since we last tried to stop all threads,
this assumption might be wrong; fortunately, this is very unlikely
to happen. */
- if (GET_LWP (ptid) != GET_PID (ptid) && found_lp == NULL)
+ if (GET_LWP (ptid) != GET_PID (ptid) && lp == NULL)
{
pid_t pid;
int status;
if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
- error (_("Can't attach %s: %s"), target_pid_to_str (ptid),
- safe_strerror (errno));
+ {
+ /* If we fail to attach to the thread, issue a warning,
+ but continue. One way this can happen is if thread
+ creation is interrupted; as of Linux 2.6.19, a kernel
+ bug may place threads in the thread list and then fail
+ to create them. */
+ warning (_("Can't attach %s: %s"), target_pid_to_str (ptid),
+ safe_strerror (errno));
+ return -1;
+ }
+
+ if (lp == NULL)
+ lp = add_lwp (ptid);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
gdb_assert (pid == GET_LWP (ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status));
- child_post_attach (pid);
+ target_post_attach (pid);
lp->stopped = 1;
{
/* We assume that the LWP representing the original process is
already stopped. Mark it as stopped in the data structure
- that the linux ptrace layer uses to keep track of threads.
- Note that this won't have already been done since the main
- thread will have, we assume, been stopped by an attach from a
- different layer. */
+ that the GNU/linux ptrace layer uses to keep track of
+ threads. Note that this won't have already been done since
+ the main thread will have, we assume, been stopped by an
+ attach from a different layer. */
+ if (lp == NULL)
+ lp = add_lwp (ptid);
lp->stopped = 1;
}
+
+ if (verbose)
+ printf_filtered (_("[New %s]\n"), target_pid_to_str (ptid));
+
+ return 0;
}
static void
linux_ops->to_attach (args, from_tty);
/* Add the initial process as the first LWP to the list. */
- lp = add_lwp (BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid)));
+ inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid));
+ lp = add_lwp (inferior_ptid);
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
return kill (lwpid, signo);
}
-/* Handle a GNU/Linux extended wait response. Most of the work we
- just pass off to linux_handle_extended_wait, but if it reports a
- clone event we need to add the new LWP to our list (and not report
- the trap to higher layers). This function returns non-zero if
- the event should be ignored and we should wait again. */
+/* Handle a GNU/Linux extended wait response. If we see a clone
+ event, we need to add the new LWP to our list (and not report the
+ trap to higher layers). This function returns non-zero if the
+ event should be ignored and we should wait again. If STOPPING is
+ true, the new LWP remains stopped, otherwise it is continued. */
static int
-linux_nat_handle_extended (struct lwp_info *lp, int status)
+linux_handle_extended_wait (struct lwp_info *lp, int status,
+ int stopping)
{
- linux_handle_extended_wait (GET_LWP (lp->ptid), status,
- &lp->waitstatus);
+ int pid = GET_LWP (lp->ptid);
+ struct target_waitstatus *ourstatus = &lp->waitstatus;
+ struct lwp_info *new_lp = NULL;
+ int event = status >> 16;
- /* TARGET_WAITKIND_SPURIOUS is used to indicate clone events. */
- if (lp->waitstatus.kind == TARGET_WAITKIND_SPURIOUS)
+ if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
+ || event == PTRACE_EVENT_CLONE)
{
- struct lwp_info *new_lp;
- new_lp = add_lwp (BUILD_LWP (lp->waitstatus.value.related_pid,
- GET_PID (inferior_ptid)));
- new_lp->cloned = 1;
- new_lp->stopped = 1;
+ unsigned long new_pid;
+ int ret;
- lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+ ptrace (PTRACE_GETEVENTMSG, pid, 0, &new_pid);
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "LLHE: Got clone event from LWP %ld, resuming\n",
- GET_LWP (lp->ptid));
- ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
+ /* If we haven't already seen the new PID stop, wait for it now. */
+ if (! pull_pid_from_list (&stopped_pids, new_pid, &status))
+ {
+ /* The new child has a pending SIGSTOP. We can't affect it until it
+ hits the SIGSTOP, but we're already attached. */
+ ret = my_waitpid (new_pid, &status,
+ (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
+ if (ret == -1)
+ perror_with_name (_("waiting for new child"));
+ else if (ret != new_pid)
+ internal_error (__FILE__, __LINE__,
+ _("wait returned unexpected PID %d"), ret);
+ else if (!WIFSTOPPED (status))
+ internal_error (__FILE__, __LINE__,
+ _("wait returned unexpected status 0x%x"), status);
+ }
- return 1;
+ ourstatus->value.related_pid = new_pid;
+
+ if (event == PTRACE_EVENT_FORK)
+ ourstatus->kind = TARGET_WAITKIND_FORKED;
+ else if (event == PTRACE_EVENT_VFORK)
+ ourstatus->kind = TARGET_WAITKIND_VFORKED;
+ else
+ {
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+ new_lp = add_lwp (BUILD_LWP (new_pid, GET_PID (inferior_ptid)));
+ new_lp->cloned = 1;
+
+ if (WSTOPSIG (status) != SIGSTOP)
+ {
+ /* This can happen if someone starts sending signals to
+ the new thread before it gets a chance to run, which
+ have a lower number than SIGSTOP (e.g. SIGUSR1).
+ This is an unlikely case, and harder to handle for
+ fork / vfork than for clone, so we do not try - but
+ we handle it for clone events here. We'll send
+ the other signal on to the thread below. */
+
+ new_lp->signalled = 1;
+ }
+ else
+ status = 0;
+
+ if (stopping)
+ new_lp->stopped = 1;
+ else
+ {
+ new_lp->resumed = 1;
+ ptrace (PTRACE_CONT, lp->waitstatus.value.related_pid, 0,
+ status ? WSTOPSIG (status) : 0);
+ }
+
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LHEW: Got clone event from LWP %ld, resuming\n",
+ GET_LWP (lp->ptid));
+ ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
+
+ return 1;
+ }
+
+ return 0;
}
- return 0;
+ if (event == PTRACE_EVENT_EXEC)
+ {
+ ourstatus->kind = TARGET_WAITKIND_EXECD;
+ ourstatus->value.execd_pathname
+ = xstrdup (child_pid_to_exec_file (pid));
+
+ if (linux_parent_pid)
+ {
+ detach_breakpoints (linux_parent_pid);
+ ptrace (PTRACE_DETACH, linux_parent_pid, 0, 0);
+
+ linux_parent_pid = 0;
+ }
+
+ return 0;
+ }
+
+ internal_error (__FILE__, __LINE__,
+ _("unknown ptrace event %d"), event);
}
/* Wait for LP to stop. Returns the wait status, or 0 if the LWP has
fprintf_unfiltered (gdb_stdlog,
"WL: Handling extended status 0x%06x\n",
status);
- if (linux_nat_handle_extended (lp, status))
+ if (linux_handle_extended_wait (lp, status, 1))
return wait_lwp (lp);
}
lp->status = 0;
}
- while (linux_nat_has_pending (GET_LWP (lp->ptid), &pending, flush_mask))
+ /* While there is a pending signal we would like to flush, continue
+ the inferior and collect another signal. But if there's already
+ a saved status that we don't want to flush, we can't resume the
+ inferior - if it stopped for some other reason we wouldn't have
+ anywhere to save the new status. In that case, we must leave the
+ signal unflushed (and possibly generate an extra SIGINT stop).
+ That's much less bad than losing a signal. */
+ while (lp->status == 0
+ && linux_nat_has_pending (GET_LWP (lp->ptid), &pending, flush_mask))
{
int ret;
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. */
-
-/* Wait for child PTID to do something. Return id of the child,
- minus_one_ptid in case of error; store status into *OURSTATUS. */
-
-ptid_t
-child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
-{
- int save_errno;
- int status;
- pid_t pid;
-
- ourstatus->kind = TARGET_WAITKIND_IGNORE;
-
- do
- {
- set_sigint_trap (); /* Causes SIGINT to be passed on to the
- attached process. */
- set_sigio_trap ();
-
- pid = my_waitpid (GET_PID (ptid), &status, 0);
- if (pid == -1 && errno == ECHILD)
- /* Try again with __WCLONE to check cloned processes. */
- pid = my_waitpid (GET_PID (ptid), &status, __WCLONE);
-
- if (debug_linux_nat)
- {
- fprintf_unfiltered (gdb_stdlog,
- "CW: waitpid %ld received %s\n",
- (long) pid, status_to_str (status));
- }
-
- save_errno = errno;
-
- /* Make sure we don't report an event for the exit of the
- original program, if we've detached from it. */
- if (pid != -1 && !WIFSTOPPED (status) && pid != GET_PID (inferior_ptid))
- {
- pid = -1;
- save_errno = EINTR;
- }
-
- /* Check for stop events reported by a process we didn't already
- know about - in this case, anything other than inferior_ptid.
-
- If we're expecting to receive stopped processes after fork,
- vfork, and clone events, then we'll just add the new one to
- our list and go back to waiting for the event to be reported
- - the stopped process might be returned from waitpid before
- or after the event is. If we want to handle debugging of
- CLONE_PTRACE processes we need to do more here, i.e. switch
- to multi-threaded mode. */
- if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP
- && pid != GET_PID (inferior_ptid))
- {
- linux_record_stopped_pid (pid);
- pid = -1;
- save_errno = EINTR;
- }
-
- /* Handle GNU/Linux's extended waitstatus for trace events. */
- if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
- && status >> 16 != 0)
- {
- linux_handle_extended_wait (pid, status, ourstatus);
-
- /* If we see a clone event, detach the child, and don't
- report the event. It would be nice to offer some way to
- switch into a non-thread-db based threaded mode at this
- point. */
- if (ourstatus->kind == TARGET_WAITKIND_SPURIOUS)
- {
- ptrace (PTRACE_DETACH, ourstatus->value.related_pid, 0, 0);
- ourstatus->kind = TARGET_WAITKIND_IGNORE;
- ptrace (PTRACE_CONT, pid, 0, 0);
- pid = -1;
- save_errno = EINTR;
- }
- }
-
- clear_sigio_trap ();
- clear_sigint_trap ();
- }
- while (pid == -1 && save_errno == EINTR);
-
- if (pid == -1)
- {
- warning (_("Child process unexpectedly missing: %s"),
- safe_strerror (errno));
-
- /* Claim it exited with unknown signal. */
- ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
- ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
- return minus_one_ptid;
- }
-
- if (ourstatus->kind == TARGET_WAITKIND_IGNORE)
- store_waitstatus (ourstatus, status);
-
- return pid_to_ptid (pid);
-}
-
/* Stop an active thread, verify it still exists, then resume it. */
static int
pid_t pid = PIDGET (ptid);
sigset_t flush_mask;
+ /* The first time we get here after starting a new inferior, we may
+ not have added it to the LWP list yet - this is the earliest
+ moment at which we know its PID. */
+ if (num_lwps == 0)
+ {
+ gdb_assert (!is_lwp (inferior_ptid));
+
+ inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid),
+ GET_PID (inferior_ptid));
+ lp = add_lwp (inferior_ptid);
+ lp->resumed = 1;
+ }
+
sigemptyset (&flush_mask);
/* Make sure SIGCHLD is blocked. */
retry:
- /* Make sure there is at least one LWP that has been resumed, at
- least if there are any LWPs at all. */
- gdb_assert (num_lwps == 0 || iterate_over_lwps (resumed_callback, NULL));
+ /* Make sure there is at least one LWP that has been resumed. */
+ gdb_assert (iterate_over_lwps (resumed_callback, NULL));
/* First check if there is a LWP with a wait status pending. */
if (pid == -1)
from waitpid before or after the event is. */
if (WIFSTOPPED (status) && !lp)
{
- linux_record_stopped_pid (lwpid);
+ linux_record_stopped_pid (lwpid, status);
status = 0;
continue;
}
if (options & __WCLONE)
lp->cloned = 1;
- if (threaded)
- {
- gdb_assert (WIFSTOPPED (status)
- && WSTOPSIG (status) == SIGSTOP);
- lp->signalled = 1;
+ gdb_assert (WIFSTOPPED (status)
+ && WSTOPSIG (status) == SIGSTOP);
+ lp->signalled = 1;
- if (!in_thread_list (inferior_ptid))
- {
- inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid),
- GET_PID (inferior_ptid));
- add_thread (inferior_ptid);
- }
-
- add_thread (lp->ptid);
- printf_unfiltered (_("[New %s]\n"),
- target_pid_to_str (lp->ptid));
+ if (!in_thread_list (inferior_ptid))
+ {
+ inferior_ptid = BUILD_LWP (GET_PID (inferior_ptid),
+ GET_PID (inferior_ptid));
+ add_thread (inferior_ptid);
}
+
+ add_thread (lp->ptid);
+ printf_unfiltered (_("[New %s]\n"),
+ target_pid_to_str (lp->ptid));
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
fprintf_unfiltered (gdb_stdlog,
"LLW: Handling extended status 0x%06x\n",
status);
- if (linux_nat_handle_extended (lp, status))
+ if (linux_handle_extended_wait (lp, status, 0))
{
status = 0;
continue;
{
int signo = target_signal_from_host (WSTOPSIG (status));
- if (signal_stop_state (signo) == 0
+ /* If we get a signal while single-stepping, we may need special
+ care, e.g. to skip the signal handler. Defer to common code. */
+ if (!lp->step
+ && signal_stop_state (signo) == 0
&& signal_print_state (signo) == 0
&& signal_pass_state (signo) == 1)
{
the comment in cancel_breakpoints_callback to find out why. */
iterate_over_lwps (cancel_breakpoints_callback, lp);
- /* If we're not running in "threaded" mode, we'll report the bare
- process id. */
-
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
{
- trap_ptid = (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
+ trap_ptid = lp->ptid;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: trap_ptid is %s.\n",
else
store_waitstatus (ourstatus, status);
- return (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
+ return lp->ptid;
}
static int
static void
linux_nat_kill (void)
{
- /* Kill all LWP's ... */
- iterate_over_lwps (kill_callback, NULL);
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+ int status;
- /* ... and wait until we've flushed all events. */
- iterate_over_lwps (kill_wait_callback, NULL);
+ /* 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. */
- target_mourn_inferior ();
-}
+ get_last_target_status (&last_ptid, &last);
-static void
-linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
- int from_tty)
-{
- linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
+ if (last.kind == TARGET_WAITKIND_FORKED
+ || last.kind == TARGET_WAITKIND_VFORKED)
+ {
+ ptrace (PT_KILL, last.value.related_pid, 0, 0);
+ wait (&status);
+ }
+
+ if (forks_exist_p ())
+ linux_fork_killall ();
+ else
+ {
+ /* Kill all LWP's ... */
+ iterate_over_lwps (kill_callback, NULL);
+
+ /* ... and wait until we've flushed all events. */
+ iterate_over_lwps (kill_wait_callback, NULL);
+ }
+
+ target_mourn_inferior ();
}
static void
sigprocmask (SIG_SETMASK, &normal_mask, NULL);
sigemptyset (&blocked_mask);
- linux_ops->to_mourn_inferior ();
+ if (! forks_exist_p ())
+ /* Normal case, no other forks available. */
+ linux_ops->to_mourn_inferior ();
+ 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 ();
}
static LONGEST
"LLTA: PTRACE_PEEKUSER %s, 0, 0 (%s)\n",
target_pid_to_str (ptid),
errno ? safe_strerror (errno) : "OK");
- if (errno)
+
+ /* Not every Linux kernel implements PTRACE_PEEKUSER. But we can
+ handle that case gracefully since ptrace will first do a lookup
+ for the process based upon the passed-in pid. If that fails we
+ will get either -ESRCH or -EPERM, otherwise the child exists and
+ is alive. */
+ if (errno == ESRCH || errno == EPERM)
return 0;
return 1;
{
static char buf[64];
- if (is_lwp (ptid))
+ if (lwp_list && lwp_list->next && is_lwp (ptid))
{
snprintf (buf, sizeof (buf), "LWP %ld", GET_LWP (ptid));
return buf;
return normal_pid_to_str (ptid);
}
-static void
-linux_nat_fetch_registers (int regnum)
-{
- /* to_fetch_registers will honor the LWP ID, so we can use it directly. */
- linux_ops->to_fetch_registers (regnum);
-}
-
-static void
-linux_nat_store_registers (int regnum)
-{
- /* to_store_registers will honor the LWP ID, so we can use it directly. */
- linux_ops->to_store_registers (regnum);
-}
-
-static void
-linux_nat_child_post_startup_inferior (ptid_t ptid)
-{
- linux_ops->to_post_startup_inferior (ptid);
-}
-
-static void
-init_linux_nat_ops (void)
-{
-#if 0
- linux_nat_ops.to_open = linux_nat_open;
-#endif
- linux_nat_ops.to_shortname = "lwp-layer";
- linux_nat_ops.to_longname = "lwp-layer";
- linux_nat_ops.to_doc = "Low level threads support (LWP layer)";
- linux_nat_ops.to_attach = linux_nat_attach;
- linux_nat_ops.to_detach = linux_nat_detach;
- linux_nat_ops.to_resume = linux_nat_resume;
- linux_nat_ops.to_wait = linux_nat_wait;
- linux_nat_ops.to_fetch_registers = linux_nat_fetch_registers;
- linux_nat_ops.to_store_registers = linux_nat_store_registers;
- linux_nat_ops.to_xfer_partial = linux_nat_xfer_partial;
- linux_nat_ops.to_kill = linux_nat_kill;
- linux_nat_ops.to_create_inferior = linux_nat_create_inferior;
- linux_nat_ops.to_mourn_inferior = linux_nat_mourn_inferior;
- linux_nat_ops.to_thread_alive = linux_nat_thread_alive;
- linux_nat_ops.to_pid_to_str = linux_nat_pid_to_str;
- linux_nat_ops.to_post_startup_inferior
- = linux_nat_child_post_startup_inferior;
- linux_nat_ops.to_post_attach = child_post_attach;
- linux_nat_ops.to_insert_fork_catchpoint = child_insert_fork_catchpoint;
- linux_nat_ops.to_insert_vfork_catchpoint = child_insert_vfork_catchpoint;
- linux_nat_ops.to_insert_exec_catchpoint = child_insert_exec_catchpoint;
-
- linux_nat_ops.to_stratum = thread_stratum;
- linux_nat_ops.to_has_thread_control = tc_schedlock;
- linux_nat_ops.to_magic = OPS_MAGIC;
-}
-
static void
sigchld_handler (int signo)
{
size, paddr_nz (addr),
read ? 'r' : ' ',
write ? 'w' : ' ', exec ? 'x' : ' ');
- if (filename && filename[0])
+ if (filename[0])
fprintf_filtered (gdb_stdout, " for %s", filename);
fprintf_filtered (gdb_stdout, "\n");
}
gdb_fpxregset_t fpxregs;
#endif
unsigned long lwp = ptid_get_lwp (ptid);
+ struct gdbarch *gdbarch = current_gdbarch;
+ const struct regset *regset;
+ int core_regset_p;
+
+ core_regset_p = gdbarch_regset_from_core_section_p (gdbarch);
+ if (core_regset_p
+ && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg",
+ sizeof (gregs))) != NULL
+ && regset->collect_regset != NULL)
+ regset->collect_regset (regset, current_regcache, -1,
+ &gregs, sizeof (gregs));
+ else
+ fill_gregset (&gregs, -1);
- fill_gregset (&gregs, -1);
note_data = (char *) elfcore_write_prstatus (obfd,
note_data,
note_size,
lwp,
stop_signal, &gregs);
- fill_fpregset (&fpregs, -1);
+ if (core_regset_p
+ && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg2",
+ sizeof (fpregs))) != NULL
+ && regset->collect_regset != NULL)
+ regset->collect_regset (regset, current_regcache, -1,
+ &fpregs, sizeof (fpregs));
+ else
+ fill_fpregset (&fpregs, -1);
+
note_data = (char *) elfcore_write_prfpreg (obfd,
note_data,
note_size,
&fpregs, sizeof (fpregs));
+
#ifdef FILL_FPXREGSET
- fill_fpxregset (&fpxregs, -1);
+ if (core_regset_p
+ && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg-xfp",
+ sizeof (fpxregs))) != NULL
+ && regset->collect_regset != NULL)
+ regset->collect_regset (regset, current_regcache, -1,
+ &fpxregs, sizeof (fpxregs));
+ else
+ fill_fpxregset (&fpxregs, -1);
+
note_data = (char *) elfcore_write_prxfpreg (obfd,
note_data,
note_size,
note_data = thread_args.note_data;
}
- auxv_len = target_auxv_read (¤t_target, &auxv);
+ auxv_len = target_read_alloc (¤t_target, TARGET_OBJECT_AUXV,
+ NULL, &auxv);
if (auxv_len > 0)
{
note_data = elfcore_write_note (obfd, note_data, note_size,
if (cmdline_f || all)
{
sprintf (fname1, "/proc/%lld/cmdline", pid);
- if ((procfile = fopen (fname1, "r")) > 0)
+ if ((procfile = fopen (fname1, "r")) != NULL)
{
fgets (buffer, sizeof (buffer), procfile);
printf_filtered ("cmdline = '%s'\n", buffer);
if (mappings_f || all)
{
sprintf (fname1, "/proc/%lld/maps", pid);
- if ((procfile = fopen (fname1, "r")) > 0)
+ if ((procfile = fopen (fname1, "r")) != NULL)
{
long long addr, endaddr, size, offset, inode;
char permissions[8], device[8], filename[MAXPATHLEN];
if (status_f || all)
{
sprintf (fname1, "/proc/%lld/status", pid);
- if ((procfile = fopen (fname1, "r")) > 0)
+ if ((procfile = fopen (fname1, "r")) != NULL)
{
while (fgets (buffer, sizeof (buffer), procfile) != NULL)
puts_filtered (buffer);
if (stat_f || all)
{
sprintf (fname1, "/proc/%lld/stat", pid);
- if ((procfile = fopen (fname1, "r")) > 0)
+ if ((procfile = fopen (fname1, "r")) != NULL)
{
int itmp;
char ctmp;
#else
t = inf_ptrace_trad_target (linux_register_u_offset);
#endif
- t->to_wait = child_wait;
- t->to_kill = kill_inferior;
t->to_insert_fork_catchpoint = child_insert_fork_catchpoint;
t->to_insert_vfork_catchpoint = child_insert_vfork_catchpoint;
t->to_insert_exec_catchpoint = child_insert_exec_catchpoint;
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;
}
+void
+linux_nat_add_target (struct target_ops *t)
+{
+ /* Save the provided single-threaded target. We save this in a separate
+ variable because another target we've inherited from (e.g. inf-ptrace)
+ may have saved a pointer to T; we want to use it for the final
+ process stratum target. */
+ linux_ops_saved = *t;
+ linux_ops = &linux_ops_saved;
+
+ /* Override some methods for multithreading. */
+ t->to_attach = linux_nat_attach;
+ t->to_detach = linux_nat_detach;
+ t->to_resume = linux_nat_resume;
+ t->to_wait = linux_nat_wait;
+ t->to_xfer_partial = linux_nat_xfer_partial;
+ t->to_kill = linux_nat_kill;
+ t->to_mourn_inferior = linux_nat_mourn_inferior;
+ t->to_thread_alive = linux_nat_thread_alive;
+ t->to_pid_to_str = linux_nat_pid_to_str;
+ t->to_has_thread_control = tc_schedlock;
+
+ /* We don't change the stratum; this target will sit at
+ process_stratum and thread_db will set at thread_stratum. This
+ is a little strange, since this is a multi-threaded-capable
+ target, but we want to be on the stack below thread_db, and we
+ also want to be used for single-threaded processes. */
+
+ add_target (t);
+
+ /* TODO: Eliminate this and have libthread_db use
+ find_target_beneath. */
+ thread_db_init (t);
+}
+
void
_initialize_linux_nat (void)
{
struct sigaction action;
- extern void thread_db_init (struct target_ops *);
add_info ("proc", linux_nat_info_proc_cmd, _("\
Show /proc process information about any running process.\n\
status -- list a different bunch of random process info.\n\
all -- list all available /proc info."));
- init_linux_nat_ops ();
- add_target (&linux_nat_ops);
- thread_db_init (&linux_nat_ops);
-
/* Save the original signal mask. */
sigprocmask (SIG_SETMASK, NULL, &normal_mask);