/* GNU/Linux native-dependent code common to multiple platforms.
- Copyright 2001, 2002, 2003, 2004 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"
+#include "inf-ptrace.h"
+#include "auxv.h"
#include <sys/param.h> /* for MAXPATHLEN */
#include <sys/procfs.h> /* for elf_gregset etc. */
#include "elf-bfd.h" /* for elfcore_write_* */
#define PTRACE_EVENT_VFORK 2
#define PTRACE_EVENT_CLONE 3
#define PTRACE_EVENT_EXEC 4
-#define PTRACE_EVENT_VFORKDONE 5
+#define PTRACE_EVENT_VFORK_DONE 5
#define PTRACE_EVENT_EXIT 6
#endif /* PTRACE_EVENT_FORK */
#define __WALL 0x40000000 /* Wait for any child. */
#endif
+/* 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;
+
+/* 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,
+ struct cmd_list_element *c, const char *value)
+{
+ fprintf_filtered (file, _("Debugging of GNU/Linux lwp module is %s.\n"),
+ value);
+}
static int linux_parent_pid;
ptrace (PTRACE_TRACEME, 0, 0, 0);
kill (getpid (), SIGSTOP);
fork ();
- exit (0);
+ _exit (0);
}
-/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events. We
+/* Wrapper function for waitpid which handles EINTR. */
+
+static int
+my_waitpid (int pid, int *status, int flags)
+{
+ int ret;
+ do
+ {
+ ret = waitpid (pid, status, flags);
+ }
+ while (ret == -1 && errno == EINTR);
+
+ return ret;
+}
+
+/* Determine if PTRACE_O_TRACEFORK can be used to follow fork events.
+
+ First, we try to enable fork tracing on ORIGINAL_PID. If this fails,
+ we know that the feature is not available. This may change the tracing
+ options for ORIGINAL_PID, but we'll be setting them shortly anyway.
+
+ However, if it succeeds, we don't know for sure that the feature is
+ available; old versions of PTRACE_SETOPTIONS ignored unknown options. We
create a child process, attach to it, use PTRACE_SETOPTIONS to enable
- fork tracing, and let it fork. If the process exits, we assume that
- we can't use TRACEFORK; if we get the fork notification, and we can
- extract the new child's PID, then we assume that we can. */
+ fork tracing, and let it fork. If the process exits, we assume that we
+ can't use TRACEFORK; if we get the fork notification, and we can extract
+ the new child's PID, then we assume that we can. */
static void
-linux_test_for_tracefork (void)
+linux_test_for_tracefork (int original_pid)
{
int child_pid, ret, status;
long second_pid;
+ linux_supports_tracefork_flag = 0;
+ linux_supports_tracevforkdone_flag = 0;
+
+ ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACEFORK);
+ if (ret != 0)
+ return;
+
child_pid = fork ();
if (child_pid == -1)
- perror_with_name ("linux_test_for_tracefork: fork");
+ perror_with_name (("fork"));
if (child_pid == 0)
linux_tracefork_child ();
- ret = waitpid (child_pid, &status, 0);
+ ret = my_waitpid (child_pid, &status, 0);
if (ret == -1)
- perror_with_name ("linux_test_for_tracefork: waitpid");
+ perror_with_name (("waitpid"));
else if (ret != child_pid)
- error ("linux_test_for_tracefork: waitpid: unexpected result %d.", ret);
+ error (_("linux_test_for_tracefork: waitpid: unexpected result %d."), ret);
if (! WIFSTOPPED (status))
- error ("linux_test_for_tracefork: waitpid: unexpected status %d.", status);
-
- linux_supports_tracefork_flag = 0;
+ error (_("linux_test_for_tracefork: waitpid: unexpected status %d."), status);
ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK);
if (ret != 0)
{
- ptrace (PTRACE_KILL, child_pid, 0, 0);
- waitpid (child_pid, &status, 0);
+ ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+ if (ret != 0)
+ {
+ warning (_("linux_test_for_tracefork: failed to kill child"));
+ return;
+ }
+
+ ret = my_waitpid (child_pid, &status, 0);
+ if (ret != child_pid)
+ warning (_("linux_test_for_tracefork: failed to wait for killed child"));
+ else if (!WIFSIGNALED (status))
+ warning (_("linux_test_for_tracefork: unexpected wait status 0x%x from "
+ "killed child"), status);
+
return;
}
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
linux_supports_tracevforkdone_flag = (ret == 0);
- ptrace (PTRACE_CONT, child_pid, 0, 0);
- ret = waitpid (child_pid, &status, 0);
+ ret = ptrace (PTRACE_CONT, child_pid, 0, 0);
+ if (ret != 0)
+ warning (_("linux_test_for_tracefork: failed to resume child"));
+
+ ret = my_waitpid (child_pid, &status, 0);
+
if (ret == child_pid && WIFSTOPPED (status)
&& status >> 16 == PTRACE_EVENT_FORK)
{
int second_status;
linux_supports_tracefork_flag = 1;
- waitpid (second_pid, &second_status, 0);
- ptrace (PTRACE_DETACH, second_pid, 0, 0);
+ my_waitpid (second_pid, &second_status, 0);
+ ret = ptrace (PTRACE_KILL, second_pid, 0, 0);
+ if (ret != 0)
+ warning (_("linux_test_for_tracefork: failed to kill second child"));
}
}
+ else
+ warning (_("linux_test_for_tracefork: unexpected result from waitpid "
+ "(%d, status 0x%x)"), ret, status);
- if (WIFSTOPPED (status))
- {
- ptrace (PTRACE_DETACH, child_pid, 0, 0);
- waitpid (child_pid, &status, 0);
- }
+ ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+ if (ret != 0)
+ warning (_("linux_test_for_tracefork: failed to kill child"));
+ my_waitpid (child_pid, &status, 0);
}
/* Return non-zero iff we have tracefork functionality available.
This function also sets linux_supports_tracefork_flag. */
static int
-linux_supports_tracefork (void)
+linux_supports_tracefork (int pid)
{
if (linux_supports_tracefork_flag == -1)
- linux_test_for_tracefork ();
+ linux_test_for_tracefork (pid);
return linux_supports_tracefork_flag;
}
static int
-linux_supports_tracevforkdone (void)
+linux_supports_tracevforkdone (int pid)
{
if (linux_supports_tracefork_flag == -1)
- linux_test_for_tracefork ();
+ linux_test_for_tracefork (pid);
return linux_supports_tracevforkdone_flag;
}
void
linux_enable_event_reporting (ptid_t ptid)
{
- int pid = ptid_get_pid (ptid);
+ int pid = ptid_get_lwp (ptid);
int options;
- if (! linux_supports_tracefork ())
+ if (pid == 0)
+ pid = ptid_get_pid (ptid);
+
+ if (! linux_supports_tracefork (pid))
return;
options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEEXEC
| PTRACE_O_TRACECLONE;
- if (linux_supports_tracevforkdone ())
+ if (linux_supports_tracevforkdone (pid))
options |= PTRACE_O_TRACEVFORKDONE;
/* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to support
linux_enable_event_reporting (pid_to_ptid (pid));
}
-void
+static void
linux_child_post_startup_inferior (ptid_t ptid)
{
linux_enable_event_reporting (ptid);
}
-#ifndef LINUX_CHILD_POST_STARTUP_INFERIOR
-void
-child_post_startup_inferior (ptid_t ptid)
-{
- linux_child_post_startup_inferior (ptid);
-}
-#endif
-
int
-child_follow_fork (int follow_child)
+child_follow_fork (struct target_ops *ops, int follow_child)
{
ptid_t last_ptid;
struct target_waitstatus last_status;
get_last_target_status (&last_ptid, &last_status);
has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
- parent_pid = ptid_get_pid (last_ptid);
+ parent_pid = ptid_get_lwp (last_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (last_ptid);
child_pid = last_status.value.related_pid;
if (! follow_child)
also, but they'll be reinserted below. */
detach_breakpoints (child_pid);
- fprintf_filtered (gdb_stdout,
- "Detaching after fork from child process %d.\n",
- child_pid);
+ /* Detach new forked process? */
+ if (detach_fork)
+ {
+ 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 (linux_supports_tracevforkdone ())
+ gdb_assert (linux_supports_tracefork_flag >= 0);
+ if (linux_supports_tracevforkdone (0))
{
int status;
ptrace (PTRACE_CONT, parent_pid, 0, 0);
- waitpid (parent_pid, &status, __WALL);
- if ((status >> 16) != PTRACE_EVENT_VFORKDONE)
- warning ("Unexpected waitpid result %06x when waiting for "
- "vfork-done", status);
+ my_waitpid (parent_pid, &status, __WALL);
+ if ((status >> 16) != PTRACE_EVENT_VFORK_DONE)
+ warning (_("Unexpected waitpid result %06x when waiting for "
+ "vfork-done"), status);
}
else
{
/* Before detaching from the parent, remove all breakpoints from it. */
remove_breakpoints ();
- fprintf_filtered (gdb_stdout,
- "Attaching after fork to child process %d.\n",
- child_pid);
+ if (debug_linux_nat)
+ {
+ target_terminal_ours ();
+ 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
the child exits or execs. At exec time we can remove the old
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);
- push_target (&deprecated_child_ops);
+
+ /* 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. */
follow_inferior_reset_breakpoints ();
{
/* The new child has a pending SIGSTOP. We can't affect it until it
hits the SIGSTOP, but we're already attached. */
- do {
- ret = waitpid (new_pid, &status,
- (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
- } while (ret == -1 && errno == EINTR);
+ ret = my_waitpid (new_pid, &status,
+ (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0);
if (ret == -1)
- perror_with_name ("waiting for new child");
+ perror_with_name (_("waiting for new child"));
else if (ret != new_pid)
internal_error (__FILE__, __LINE__,
- "wait returned unexpected PID %d", ret);
+ _("wait returned unexpected PID %d"), ret);
else if (!WIFSTOPPED (status) || WSTOPSIG (status) != SIGSTOP)
internal_error (__FILE__, __LINE__,
- "wait returned unexpected status 0x%x", status);
+ _("wait returned unexpected status 0x%x"), status);
}
if (event == PTRACE_EVENT_FORK)
}
internal_error (__FILE__, __LINE__,
- "unknown ptrace event %d", event);
+ _("unknown ptrace event %d"), event);
}
\f
-int
+void
child_insert_fork_catchpoint (int pid)
{
- if (! linux_supports_tracefork ())
- error ("Your system does not support fork catchpoints.");
-
- return 0;
+ if (! linux_supports_tracefork (pid))
+ error (_("Your system does not support fork catchpoints."));
}
-int
+void
child_insert_vfork_catchpoint (int pid)
{
- if (!linux_supports_tracefork ())
- error ("Your system does not support vfork catchpoints.");
-
- return 0;
+ if (!linux_supports_tracefork (pid))
+ error (_("Your system does not support vfork catchpoints."));
}
-int
+void
child_insert_exec_catchpoint (int pid)
{
- if (!linux_supports_tracefork ())
- error ("Your system does not support exec catchpoints.");
-
- return 0;
+ if (!linux_supports_tracefork (pid))
+ error (_("Your system does not support exec catchpoints."));
}
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
}
if (verbose)
- printf_filtered ("[New %s]\n", target_pid_to_str (ptid));
+ printf_filtered (_("[New %s]\n"), target_pid_to_str (ptid));
found_lp = lp = find_lwp_pid (ptid);
if (lp == NULL)
int status;
if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
- error ("Can't attach %s: %s", target_pid_to_str (ptid),
+ error (_("Can't attach %s: %s"), target_pid_to_str (ptid),
safe_strerror (errno));
if (debug_linux_nat)
"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
target_pid_to_str (ptid));
- pid = waitpid (GET_LWP (ptid), &status, 0);
+ pid = my_waitpid (GET_LWP (ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
{
/* Try again with __WCLONE to check cloned processes. */
- pid = waitpid (GET_LWP (ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
lp->cloned = 1;
}
/* FIXME: We should probably accept a list of process id's, and
attach all of them. */
- deprecated_child_ops.to_attach (args, from_tty);
+ 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)));
/* Make sure the initial process is stopped. The user-level threads
layer might want to poke around in the inferior, and that won't
work if things haven't stabilized yet. */
- pid = waitpid (GET_PID (inferior_ptid), &status, 0);
+ pid = my_waitpid (GET_PID (inferior_ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
{
- warning ("%s is a cloned process", target_pid_to_str (inferior_ptid));
+ warning (_("%s is a cloned process"), target_pid_to_str (inferior_ptid));
/* Try again with __WCLONE to check cloned processes. */
- pid = waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
lp->cloned = 1;
}
errno = 0;
if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
WSTOPSIG (lp->status)) < 0)
- error ("Can't continue %s: %s", target_pid_to_str (lp->ptid),
+ error (_("Can't continue %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
if (debug_linux_nat)
errno = 0;
if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
WSTOPSIG (lp->status)) < 0)
- error ("Can't detach %s: %s", target_pid_to_str (lp->ptid),
+ error (_("Can't detach %s: %s"), target_pid_to_str (lp->ptid),
safe_strerror (errno));
if (debug_linux_nat)
sigemptyset (&blocked_mask);
inferior_ptid = pid_to_ptid (GET_PID (inferior_ptid));
- deprecated_child_ops.to_detach (args, from_tty);
+ linux_ops->to_detach (args, from_tty);
}
/* Resume LP. */
{
struct thread_info *tp;
- child_resume (pid_to_ptid (GET_LWP (lp->ptid)), 0, TARGET_SIGNAL_0);
+ linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ 0, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"RC: PTRACE_CONT %s, 0, 0 (resume sibling)\n",
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;
}
if (resume_all)
iterate_over_lwps (resume_callback, NULL);
- child_resume (ptid, step, signo);
+ linux_ops->to_resume (ptid, step, signo);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLR: %s %s, %s (resume event thread)\n",
gdb_assert (!lp->stopped);
gdb_assert (lp->status == 0);
- pid = waitpid (GET_LWP (lp->ptid), &status, 0);
+ pid = my_waitpid (GET_LWP (lp->ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
{
- pid = waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
if (pid == -1 && errno == ECHILD)
{
/* The thread has previously exited. We need to delete it
/* Core GDB cannot deal with us deleting the current thread. */
if (!ptid_equal (lp->ptid, inferior_ptid))
delete_thread (lp->ptid);
- printf_unfiltered ("[%s exited]\n",
+ printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
if (lp->status)
{
if (debug_linux_nat)
- printf_unfiltered ("FC: LP has pending status %06x\n", lp->status);
+ printf_unfiltered (_("FC: LP has pending status %06x\n"), lp->status);
if (WIFSTOPPED (lp->status) && sigismember (flush_mask, WSTOPSIG (lp->status)))
lp->status = 0;
}
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;
}
-#ifdef CHILD_WAIT
+/* 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)
attached process. */
set_sigio_trap ();
- pid = waitpid (GET_PID (ptid), &status, 0);
+ pid = my_waitpid (GET_PID (ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
/* Try again with __WCLONE to check cloned processes. */
- pid = waitpid (GET_PID (ptid), &status, __WCLONE);
+ pid = my_waitpid (GET_PID (ptid), &status, __WCLONE);
if (debug_linux_nat)
{
if (pid == -1)
{
- warning ("Child process unexpectedly missing: %s",
+ warning (_("Child process unexpectedly missing: %s"),
safe_strerror (errno));
/* Claim it exited with unknown signal. */
return pid_to_ptid (pid);
}
-#endif
-
/* Stop an active thread, verify it still exists, then resume it. */
static int
/* Resume the thread. It should halt immediately returning the
pending SIGSTOP. */
registers_changed ();
- child_resume (pid_to_ptid (GET_LWP (lp->ptid)), lp->step,
- TARGET_SIGNAL_0);
+ linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s %s, 0, 0 (expect SIGSTOP)\n",
{
pid_t lwpid;
- lwpid = waitpid (pid, &status, options);
+ lwpid = my_waitpid (pid, &status, options);
if (lwpid > 0)
{
gdb_assert (pid == -1 || lwpid == pid);
}
add_thread (lp->ptid);
- printf_unfiltered ("[New %s]\n",
+ printf_unfiltered (_("[New %s]\n"),
target_pid_to_str (lp->ptid));
}
}
thread. */
if (!ptid_equal (lp->ptid, inferior_ptid))
delete_thread (lp->ptid);
- printf_unfiltered ("[%s exited]\n",
+ printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
thread. */
if (!ptid_equal (lp->ptid, inferior_ptid))
delete_thread (lp->ptid);
- printf_unfiltered ("[%s exited]\n",
+ printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
if (debug_linux_nat)
lp->signalled = 0;
registers_changed ();
- child_resume (pid_to_ptid (GET_LWP (lp->ptid)), lp->step,
- TARGET_SIGNAL_0);
+ linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ lp->step, TARGET_SIGNAL_0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
newly attached threads may cause an unwanted delay in
getting them running. */
registers_changed ();
- child_resume (pid_to_ptid (GET_LWP (lp->ptid)), lp->step, signo);
+ linux_ops->to_resume (pid_to_ptid (GET_LWP (lp->ptid)),
+ lp->step, signo);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s %s, %s (preempt 'handle')\n",
{
do
{
- pid = waitpid (GET_LWP (lp->ptid), NULL, __WCLONE);
+ pid = my_waitpid (GET_LWP (lp->ptid), NULL, __WCLONE);
if (pid != (pid_t) -1 && debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
do
{
- pid = waitpid (GET_LWP (lp->ptid), NULL, 0);
+ pid = my_waitpid (GET_LWP (lp->ptid), NULL, 0);
if (pid != (pid_t) -1 && debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
int from_tty)
{
- deprecated_child_ops.to_create_inferior (exec_file, allargs, env, from_tty);
+ linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
}
static void
sigprocmask (SIG_SETMASK, &normal_mask, NULL);
sigemptyset (&blocked_mask);
- deprecated_child_ops.to_mourn_inferior ();
+ linux_ops->to_mourn_inferior ();
}
-static int
-linux_nat_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
- struct mem_attrib *attrib, struct target_ops *target)
+static LONGEST
+linux_nat_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
{
struct cleanup *old_chain = save_inferior_ptid ();
- int xfer;
+ LONGEST xfer;
if (is_lwp (inferior_ptid))
inferior_ptid = pid_to_ptid (GET_LWP (inferior_ptid));
- xfer = linux_proc_xfer_memory (memaddr, myaddr, len, write, attrib, target);
- if (xfer == 0)
- xfer = child_xfer_memory (memaddr, myaddr, len, write, attrib, target);
+ xfer = linux_ops->to_xfer_partial (ops, object, annex, readbuf, writebuf,
+ offset, len);
do_cleanups (old_chain);
return xfer;
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)
{
linux_nat_ops.to_detach = linux_nat_detach;
linux_nat_ops.to_resume = linux_nat_resume;
linux_nat_ops.to_wait = linux_nat_wait;
- /* fetch_inferior_registers and store_inferior_registers will
- honor the LWP id, so we can use them directly. */
- linux_nat_ops.to_fetch_registers = fetch_inferior_registers;
- linux_nat_ops.to_store_registers = store_inferior_registers;
- linux_nat_ops.to_xfer_memory = linux_nat_xfer_memory;
+ 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 = child_post_startup_inferior;
+ 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;
int ret = fscanf (mapfile, "%llx-%llx %s %llx %s %llx",
addr, endaddr, permissions, offset, device, inode);
- if (ret > 0 && ret != EOF && *inode != 0)
+ filename[0] = '\0';
+ if (ret > 0 && ret != EOF)
{
/* Eat everything up to EOL for the filename. This will prevent
weird filenames (such as one with embedded whitespace) from
only. */
ret += fscanf (mapfile, "%[^\n]\n", filename);
}
- else
- {
- filename[0] = '\0'; /* no filename */
- fscanf (mapfile, "\n");
- }
+
return (ret != 0 && ret != EOF);
}
/* Compose the filename for the /proc memory map, and open it. */
sprintf (mapsfilename, "/proc/%lld/maps", pid);
if ((mapsfile = fopen (mapsfilename, "r")) == NULL)
- error ("Could not open %s\n", mapsfilename);
+ error (_("Could not open %s."), mapsfilename);
if (info_verbose)
fprintf_filtered (gdb_stdout,
char psargs[80] = { '\0' };
char *note_data = NULL;
ptid_t current_ptid = inferior_ptid;
- char *auxv;
+ gdb_byte *auxv;
int auxv_len;
if (get_exec_file (0))
argv++;
}
if (pid == 0)
- error ("No current process: you must name one.");
+ error (_("No current process: you must name one."));
sprintf (fname1, "/proc/%lld", pid);
if (stat (fname1, &dummy) != 0)
- error ("No /proc directory: '%s'", fname1);
+ error (_("No /proc directory: '%s'"), fname1);
- printf_filtered ("process %lld\n", pid);
+ printf_filtered (_("process %lld\n"), pid);
if (cmdline_f || all)
{
sprintf (fname1, "/proc/%lld/cmdline", pid);
fclose (procfile);
}
else
- warning ("unable to open /proc file '%s'", fname1);
+ warning (_("unable to open /proc file '%s'"), fname1);
}
if (cwd_f || all)
{
if (readlink (fname1, fname2, sizeof (fname2)) > 0)
printf_filtered ("cwd = '%s'\n", fname2);
else
- warning ("unable to read link '%s'", fname1);
+ warning (_("unable to read link '%s'"), fname1);
}
if (exe_f || all)
{
if (readlink (fname1, fname2, sizeof (fname2)) > 0)
printf_filtered ("exe = '%s'\n", fname2);
else
- warning ("unable to read link '%s'", fname1);
+ warning (_("unable to read link '%s'"), fname1);
}
if (mappings_f || all)
{
long long addr, endaddr, size, offset, inode;
char permissions[8], device[8], filename[MAXPATHLEN];
- printf_filtered ("Mapped address spaces:\n\n");
+ printf_filtered (_("Mapped address spaces:\n\n"));
if (TARGET_ADDR_BIT == 32)
{
printf_filtered ("\t%10s %10s %10s %10s %7s\n",
fclose (procfile);
}
else
- warning ("unable to open /proc file '%s'", fname1);
+ warning (_("unable to open /proc file '%s'"), fname1);
}
if (status_f || all)
{
fclose (procfile);
}
else
- warning ("unable to open /proc file '%s'", fname1);
+ warning (_("unable to open /proc file '%s'"), fname1);
}
if (stat_f || all)
{
char ctmp;
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("Process: %d\n", itmp);
+ printf_filtered (_("Process: %d\n"), itmp);
if (fscanf (procfile, "%s ", &buffer[0]) > 0)
- printf_filtered ("Exec file: %s\n", buffer);
+ printf_filtered (_("Exec file: %s\n"), buffer);
if (fscanf (procfile, "%c ", &ctmp) > 0)
- printf_filtered ("State: %c\n", ctmp);
+ printf_filtered (_("State: %c\n"), ctmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("Parent process: %d\n", itmp);
+ printf_filtered (_("Parent process: %d\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("Process group: %d\n", itmp);
+ printf_filtered (_("Process group: %d\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("Session id: %d\n", itmp);
+ printf_filtered (_("Session id: %d\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("TTY: %d\n", itmp);
+ printf_filtered (_("TTY: %d\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("TTY owner process group: %d\n", itmp);
+ printf_filtered (_("TTY owner process group: %d\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Flags: 0x%x\n", itmp);
+ printf_filtered (_("Flags: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Minor faults (no memory page): %u\n",
+ printf_filtered (_("Minor faults (no memory page): %u\n"),
(unsigned int) itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Minor faults, children: %u\n",
+ printf_filtered (_("Minor faults, children: %u\n"),
(unsigned int) itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Major faults (memory page faults): %u\n",
+ printf_filtered (_("Major faults (memory page faults): %u\n"),
(unsigned int) itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Major faults, children: %u\n",
+ printf_filtered (_("Major faults, children: %u\n"),
(unsigned int) itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered ("utime: %d\n", itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered ("stime, children: %d\n", itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("jiffies remaining in current time slice: %d\n",
+ printf_filtered (_("jiffies remaining in current time slice: %d\n"),
itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered ("'nice' value: %d\n", itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("jiffies until next timeout: %u\n",
+ printf_filtered (_("jiffies until next timeout: %u\n"),
(unsigned int) itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
printf_filtered ("jiffies until next SIGALRM: %u\n",
(unsigned int) itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("start time (jiffies since system boot): %d\n",
+ printf_filtered (_("start time (jiffies since system boot): %d\n"),
itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Virtual memory size: %u\n",
+ printf_filtered (_("Virtual memory size: %u\n"),
(unsigned int) itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Resident set size: %u\n", (unsigned int) itmp);
+ printf_filtered (_("Resident set size: %u\n"), (unsigned int) itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
printf_filtered ("rlim: %u\n", (unsigned int) itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Start of text: 0x%x\n", itmp);
+ printf_filtered (_("Start of text: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("End of text: 0x%x\n", itmp);
+ printf_filtered (_("End of text: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
- printf_filtered ("Start of stack: 0x%x\n", itmp);
+ printf_filtered (_("Start of stack: 0x%x\n"), itmp);
#if 0 /* Don't know how architecture-dependent the rest is...
Anyway the signal bitmap info is available from "status". */
if (fscanf (procfile, "%u ", &itmp) > 0) /* FIXME arch? */
- printf_filtered ("Kernel stack pointer: 0x%x\n", itmp);
+ printf_filtered (_("Kernel stack pointer: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0) /* FIXME arch? */
- printf_filtered ("Kernel instr pointer: 0x%x\n", itmp);
+ printf_filtered (_("Kernel instr pointer: 0x%x\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("Pending signals bitmap: 0x%x\n", itmp);
+ printf_filtered (_("Pending signals bitmap: 0x%x\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("Blocked signals bitmap: 0x%x\n", itmp);
+ printf_filtered (_("Blocked signals bitmap: 0x%x\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("Ignored signals bitmap: 0x%x\n", itmp);
+ printf_filtered (_("Ignored signals bitmap: 0x%x\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
- printf_filtered ("Catched signals bitmap: 0x%x\n", itmp);
+ printf_filtered (_("Catched signals bitmap: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0) /* FIXME arch? */
- printf_filtered ("wchan (system call): 0x%x\n", itmp);
+ printf_filtered (_("wchan (system call): 0x%x\n"), itmp);
#endif
fclose (procfile);
}
else
- warning ("unable to open /proc file '%s'", fname1);
+ warning (_("unable to open /proc file '%s'"), fname1);
}
}
-int
-linux_proc_xfer_memory (CORE_ADDR addr, char *myaddr, int len, int write,
- struct mem_attrib *attrib, struct target_ops *target)
+/* Implement the to_xfer_partial interface for memory reads using the /proc
+ filesystem. Because we can use a single read() call for /proc, this
+ can be much more efficient than banging away at PTRACE_PEEKTEXT,
+ but it doesn't support writes. */
+
+static LONGEST
+linux_proc_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
{
- int fd, ret;
+ LONGEST ret;
+ int fd;
char filename[64];
- if (write)
+ if (object != TARGET_OBJECT_MEMORY || !readbuf)
return 0;
/* Don't bother for one word. */
32-bit platforms (for instance, SPARC debugging a SPARC64
application). */
#ifdef HAVE_PREAD64
- if (pread64 (fd, myaddr, len, addr) != len)
+ if (pread64 (fd, readbuf, len, offset) != len)
#else
- if (lseek (fd, addr, SEEK_SET) == -1 || read (fd, myaddr, len) != len)
+ if (lseek (fd, offset, SEEK_SET) == -1 || read (fd, readbuf, len) != len)
#endif
ret = 0;
else
int signum;
if (line[len] != '\n')
- error ("Could not parse signal set: %s", line);
+ error (_("Could not parse signal set: %s"), line);
p = line;
signum = len * 4;
else if (*p >= 'a' && *p <= 'f')
digit = *p - 'a' + 10;
else
- error ("Could not parse signal set: %s", line);
+ error (_("Could not parse signal set: %s"), line);
signum -= 4;
sprintf (fname, "/proc/%d/status", pid);
procfile = fopen (fname, "r");
if (procfile == NULL)
- error ("Could not open %s", fname);
+ error (_("Could not open %s"), fname);
while (fgets (buffer, MAXPATHLEN, procfile) != NULL)
{
fclose (procfile);
}
+static LONGEST
+linux_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+ LONGEST xfer;
+
+ if (object == TARGET_OBJECT_AUXV)
+ return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf,
+ offset, len);
+
+ xfer = linux_proc_xfer_partial (ops, object, annex, readbuf, writebuf,
+ offset, len);
+ if (xfer != 0)
+ return xfer;
+
+ return super_xfer_partial (ops, object, annex, readbuf, writebuf,
+ offset, len);
+}
+
+#ifndef FETCH_INFERIOR_REGISTERS
+
+/* Return the address in the core dump or inferior of register
+ REGNO. */
+
+static CORE_ADDR
+linux_register_u_offset (int regno)
+{
+ /* FIXME drow/2005-09-04: The hardcoded use of register_addr should go
+ away. This requires disentangling the various definitions of it
+ (particularly alpha-nat.c's). */
+ return register_addr (regno, 0);
+}
+
+#endif
+
+/* Create a prototype generic Linux target. The client can override
+ it with local methods. */
+
+struct target_ops *
+linux_target (void)
+{
+ struct target_ops *t;
+
+#ifdef FETCH_INFERIOR_REGISTERS
+ t = inf_ptrace_target ();
+#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;
+ t->to_pid_to_exec_file = child_pid_to_exec_file;
+ t->to_post_startup_inferior = linux_child_post_startup_inferior;
+ t->to_post_attach = child_post_attach;
+ t->to_follow_fork = child_follow_fork;
+ t->to_find_memory_regions = linux_nat_find_memory_regions;
+ t->to_make_corefile_notes = linux_nat_make_corefile_notes;
+
+ 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
_initialize_linux_nat (void)
{
struct sigaction action;
extern void thread_db_init (struct target_ops *);
- child_ops.to_find_memory_regions = linux_nat_find_memory_regions;
- child_ops.to_make_corefile_notes = linux_nat_make_corefile_notes;
-
- add_info ("proc", linux_nat_info_proc_cmd,
- "Show /proc process information about any running process.\n\
+ add_info ("proc", linux_nat_info_proc_cmd, _("\
+Show /proc process information about any running process.\n\
Specify any process id, or use the program being debugged by default.\n\
Specify any of the following keywords for detailed info:\n\
mappings -- list of mapped memory regions.\n\
stat -- list a bunch of random process info.\n\
status -- list a different bunch of random process info.\n\
- all -- list all available /proc info.");
+ all -- list all available /proc info."));
init_linux_nat_ops ();
add_target (&linux_nat_ops);
action.sa_handler = sigchld_handler;
sigemptyset (&action.sa_mask);
- action.sa_flags = 0;
+ action.sa_flags = SA_RESTART;
sigaction (SIGCHLD, &action, NULL);
/* Make sure we don't block SIGCHLD during a sigsuspend. */
sigemptyset (&blocked_mask);
- deprecated_add_show_from_set
- (add_set_cmd ("lin-lwp", no_class, var_zinteger,
- (char *) &debug_linux_nat,
- "Set debugging of GNU/Linux lwp module.\n\
-Enables printf debugging output.\n", &setdebuglist), &showdebuglist);
+ add_setshow_zinteger_cmd ("lin-lwp", no_class, &debug_linux_nat, _("\
+Set debugging of GNU/Linux lwp module."), _("\
+Show debugging of GNU/Linux lwp module."), _("\
+Enables printf debugging output."),
+ NULL,
+ show_debug_linux_nat,
+ &setdebuglist, &showdebuglist);
}
\f
if (ms == NULL)
return 0;
- if (target_read_memory (SYMBOL_VALUE_ADDRESS (ms), (char *) &signo,
+ if (target_read_memory (SYMBOL_VALUE_ADDRESS (ms), (gdb_byte *) &signo,
sizeof (signo)) != 0)
return 0;
action.sa_handler = sigchld_handler;
sigemptyset (&action.sa_mask);
- action.sa_flags = 0;
+ action.sa_flags = SA_RESTART;
sigaction (cancel, &action, NULL);
/* We block the "cancel" signal throughout this code ... */
/* ... except during a sigsuspend. */
sigdelset (&suspend_mask, cancel);
}
+