#include "defs.h"
#include "inferior.h"
#include "target.h"
#include "gdb_string.h"
#include "gdb_wait.h"
#include "gdb_assert.h"
#ifdef HAVE_TKILL_SYSCALL
#include <unistd.h>
#include <sys/syscall.h>
#endif
#include <sys/ptrace.h>
#include "linux-nat.h"
#include "gdbthread.h"
#include "gdbcmd.h"
#include "regcache.h"
#include <sys/param.h>
#include <sys/procfs.h>
#include "elf-bfd.h"
#include "gregset.h"
#include "gdbcore.h"
#include <ctype.h>
#include "gdbthread.h"
#include "gdb_stat.h"
#include <fcntl.h>
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
#endif
#ifndef PTRACE_EVENT_FORK
#define PTRACE_SETOPTIONS 0x4200
#define PTRACE_GETEVENTMSG 0x4201
#define PTRACE_O_TRACESYSGOOD 0x00000001
#define PTRACE_O_TRACEFORK 0x00000002
#define PTRACE_O_TRACEVFORK 0x00000004
#define PTRACE_O_TRACECLONE 0x00000008
#define PTRACE_O_TRACEEXEC 0x00000010
#define PTRACE_O_TRACEVFORKDONE 0x00000020
#define PTRACE_O_TRACEEXIT 0x00000040
#define PTRACE_EVENT_FORK 1
#define PTRACE_EVENT_VFORK 2
#define PTRACE_EVENT_CLONE 3
#define PTRACE_EVENT_EXEC 4
#define PTRACE_EVENT_VFORK_DONE 5
#define PTRACE_EVENT_EXIT 6
#endif
#ifndef __WALL
#define __WALL 0x40000000
#endif
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;
struct simple_pid_list
{
int pid;
struct simple_pid_list *next;
};
struct simple_pid_list *stopped_pids;
static int linux_supports_tracefork_flag = -1;
static int linux_supports_tracevforkdone_flag = -1;
static void
add_to_pid_list (struct simple_pid_list **listp, int pid)
{
struct simple_pid_list *new_pid = xmalloc (sizeof (struct simple_pid_list));
new_pid->pid = pid;
new_pid->next = *listp;
*listp = new_pid;
}
static int
pull_pid_from_list (struct simple_pid_list **listp, int pid)
{
struct simple_pid_list **p;
for (p = listp; *p != NULL; p = &(*p)->next)
if ((*p)->pid == pid)
{
struct simple_pid_list *next = (*p)->next;
xfree (*p);
*p = next;
return 1;
}
return 0;
}
void
linux_record_stopped_pid (int pid)
{
add_to_pid_list (&stopped_pids, pid);
}
static void
linux_tracefork_child (void)
{
int ret;
ptrace (PTRACE_TRACEME, 0, 0, 0);
kill (getpid (), SIGSTOP);
fork ();
_exit (0);
}
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;
}
static 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 (("fork"));
if (child_pid == 0)
linux_tracefork_child ();
ret = my_waitpid (child_pid, &status, 0);
if (ret == -1)
perror_with_name (("waitpid"));
else if (ret != child_pid)
error (_("linux_test_for_tracefork: waitpid: unexpected result %d."), ret);
if (! WIFSTOPPED (status))
error (_("linux_test_for_tracefork: waitpid: unexpected status %d."), status);
ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACEFORK);
if (ret != 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;
}
ret = ptrace (PTRACE_SETOPTIONS, child_pid, 0,
PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORKDONE);
linux_supports_tracevforkdone_flag = (ret == 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)
{
second_pid = 0;
ret = ptrace (PTRACE_GETEVENTMSG, child_pid, 0, &second_pid);
if (ret == 0 && second_pid != 0)
{
int second_status;
linux_supports_tracefork_flag = 1;
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);
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);
}
static int
linux_supports_tracefork (int pid)
{
if (linux_supports_tracefork_flag == -1)
linux_test_for_tracefork (pid);
return linux_supports_tracefork_flag;
}
static int
linux_supports_tracevforkdone (int pid)
{
if (linux_supports_tracefork_flag == -1)
linux_test_for_tracefork (pid);
return linux_supports_tracevforkdone_flag;
}
void
linux_enable_event_reporting (ptid_t ptid)
{
int pid = ptid_get_lwp (ptid);
int options;
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 (pid))
options |= PTRACE_O_TRACEVFORKDONE;
ptrace (PTRACE_SETOPTIONS, pid, 0, options);
}
void
child_post_attach (int pid)
{
linux_enable_event_reporting (pid_to_ptid (pid));
}
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)
{
ptid_t last_ptid;
struct target_waitstatus last_status;
int has_vforked;
int parent_pid, child_pid;
get_last_target_status (&last_ptid, &last_status);
has_vforked = (last_status.kind == TARGET_WAITKIND_VFORKED);
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)
{
detach_breakpoints (child_pid);
if (debug_linux_nat)
{
target_terminal_ours ();
fprintf_unfiltered (gdb_stdlog,
"Detaching after fork from child process %d.\n",
child_pid);
}
ptrace (PTRACE_DETACH, child_pid, 0, 0);
if (has_vforked)
{
gdb_assert (linux_supports_tracefork_flag >= 0);
if (linux_supports_tracevforkdone (0))
{
int status;
ptrace (PTRACE_CONT, parent_pid, 0, 0);
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
{
usleep (10000);
}
reattach_breakpoints (parent_pid);
}
}
else
{
char child_pid_spelling[40];
if (! has_vforked)
detach_breakpoints (child_pid);
remove_breakpoints ();
if (debug_linux_nat)
{
target_terminal_ours ();
fprintf_unfiltered (gdb_stdlog,
"Attaching after fork to child process %d.\n",
child_pid);
}
if (has_vforked)
linux_parent_pid = parent_pid;
else
target_detach (NULL, 0);
inferior_ptid = pid_to_ptid (child_pid);
push_target (&deprecated_child_ops);
follow_inferior_reset_breakpoints ();
}
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 (! pull_pid_from_list (&stopped_pids, new_pid))
{
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);
}
void
child_insert_fork_catchpoint (int pid)
{
if (! linux_supports_tracefork (pid))
error (_("Your system does not support fork catchpoints."));
}
void
child_insert_vfork_catchpoint (int pid)
{
if (!linux_supports_tracefork (pid))
error (_("Your system does not support vfork catchpoints."));
}
void
child_insert_exec_catchpoint (int pid)
{
if (!linux_supports_tracefork (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;
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);
}
ptrace (PT_KILL, pid, 0, 0);
ret = wait (&status);
while (ret == pid && WIFSTOPPED (status))
{
ptrace (PT_KILL, pid, 0, 0);
ret = wait (&status);
}
target_mourn_inferior ();
}
static struct lwp_info *lwp_list;
static int num_lwps;
static int threaded;
#define GET_LWP(ptid) ptid_get_lwp (ptid)
#define GET_PID(ptid) ptid_get_pid (ptid)
#define is_lwp(ptid) (GET_LWP (ptid) != 0)
#define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0)
ptid_t trap_ptid;
static struct target_ops linux_nat_ops;
static sigset_t normal_mask;
static sigset_t suspend_mask;
static sigset_t blocked_mask;
static int stop_wait_callback (struct lwp_info *lp, void *data);
static int linux_nat_thread_alive (ptid_t ptid);
static char *
status_to_str (int status)
{
static char buf[64];
if (WIFSTOPPED (status))
snprintf (buf, sizeof (buf), "%s (stopped)",
strsignal (WSTOPSIG (status)));
else if (WIFSIGNALED (status))
snprintf (buf, sizeof (buf), "%s (terminated)",
strsignal (WSTOPSIG (status)));
else
snprintf (buf, sizeof (buf), "%d (exited)", WEXITSTATUS (status));
return buf;
}
static void
init_lwp_list (void)
{
struct lwp_info *lp, *lpnext;
for (lp = lwp_list; lp; lp = lpnext)
{
lpnext = lp->next;
xfree (lp);
}
lwp_list = NULL;
num_lwps = 0;
threaded = 0;
}
static struct lwp_info *
add_lwp (ptid_t ptid)
{
struct lwp_info *lp;
gdb_assert (is_lwp (ptid));
lp = (struct lwp_info *) xmalloc (sizeof (struct lwp_info));
memset (lp, 0, sizeof (struct lwp_info));
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
lp->ptid = ptid;
lp->next = lwp_list;
lwp_list = lp;
if (++num_lwps > 1)
threaded = 1;
return lp;
}
static void
delete_lwp (ptid_t ptid)
{
struct lwp_info *lp, *lpprev;
lpprev = NULL;
for (lp = lwp_list; lp; lpprev = lp, lp = lp->next)
if (ptid_equal (lp->ptid, ptid))
break;
if (!lp)
return;
num_lwps--;
if (lpprev)
lpprev->next = lp->next;
else
lwp_list = lp->next;
xfree (lp);
}
static struct lwp_info *
find_lwp_pid (ptid_t ptid)
{
struct lwp_info *lp;
int lwp;
if (is_lwp (ptid))
lwp = GET_LWP (ptid);
else
lwp = GET_PID (ptid);
for (lp = lwp_list; lp; lp = lp->next)
if (lwp == GET_LWP (lp->ptid))
return lp;
return NULL;
}
struct lwp_info *
iterate_over_lwps (int (*callback) (struct lwp_info *, void *), void *data)
{
struct lwp_info *lp, *lpnext;
for (lp = lwp_list; lp; lp = lpnext)
{
lpnext = lp->next;
if ((*callback) (lp, data))
return lp;
}
return NULL;
}
void
lin_lwp_attach_lwp (ptid_t ptid, int verbose)
{
struct lwp_info *lp, *found_lp;
gdb_assert (is_lwp (ptid));
if (!sigismember (&blocked_mask, SIGCHLD))
{
sigaddset (&blocked_mask, SIGCHLD);
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);
if (GET_LWP (ptid) != GET_PID (ptid) && found_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 (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLAL: PTRACE_ATTACH %s, 0, 0 (OK)\n",
target_pid_to_str (ptid));
pid = my_waitpid (GET_LWP (ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
{
pid = my_waitpid (GET_LWP (ptid), &status, __WCLONE);
lp->cloned = 1;
}
gdb_assert (pid == GET_LWP (ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status));
child_post_attach (pid);
lp->stopped = 1;
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"LLAL: waitpid %s received %s\n",
target_pid_to_str (ptid),
status_to_str (status));
}
}
else
{
lp->stopped = 1;
}
}
static void
linux_nat_attach (char *args, int from_tty)
{
struct lwp_info *lp;
pid_t pid;
int status;
deprecated_child_ops.to_attach (args, from_tty);
lp = add_lwp (BUILD_LWP (GET_PID (inferior_ptid), GET_PID (inferior_ptid)));
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));
pid = my_waitpid (GET_PID (inferior_ptid), &status, __WCLONE);
lp->cloned = 1;
}
gdb_assert (pid == GET_PID (inferior_ptid)
&& WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP);
lp->stopped = 1;
lp->status = W_STOPCODE (SIGSTOP);
lp->resumed = 1;
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"LLA: waitpid %ld, faking SIGSTOP\n", (long) pid);
}
}
static int
detach_callback (struct lwp_info *lp, void *data)
{
gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
if (debug_linux_nat && lp->status)
fprintf_unfiltered (gdb_stdlog, "DC: Pending %s for %s on detach.\n",
strsignal (WSTOPSIG (lp->status)),
target_pid_to_str (lp->ptid));
while (lp->signalled && lp->stopped)
{
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),
safe_strerror (errno));
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"DC: PTRACE_CONTINUE (%s, 0, %s) (OK)\n",
target_pid_to_str (lp->ptid),
status_to_str (lp->status));
lp->stopped = 0;
lp->signalled = 0;
lp->status = 0;
gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
}
if (GET_LWP (lp->ptid) != GET_PID (lp->ptid))
{
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),
safe_strerror (errno));
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"PTRACE_DETACH (%s, %s, 0) (OK)\n",
target_pid_to_str (lp->ptid),
strsignal (WSTOPSIG (lp->status)));
delete_lwp (lp->ptid);
}
return 0;
}
static void
linux_nat_detach (char *args, int from_tty)
{
iterate_over_lwps (detach_callback, NULL);
gdb_assert (num_lwps == 1);
trap_ptid = null_ptid;
init_lwp_list ();
sigprocmask (SIG_SETMASK, &normal_mask, NULL);
sigemptyset (&blocked_mask);
inferior_ptid = pid_to_ptid (GET_PID (inferior_ptid));
deprecated_child_ops.to_detach (args, from_tty);
}
static int
resume_callback (struct lwp_info *lp, void *data)
{
if (lp->stopped && lp->status == 0)
{
struct thread_info *tp;
child_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",
target_pid_to_str (lp->ptid));
lp->stopped = 0;
lp->step = 0;
}
return 0;
}
static int
resume_clear_callback (struct lwp_info *lp, void *data)
{
lp->resumed = 0;
return 0;
}
static int
resume_set_callback (struct lwp_info *lp, void *data)
{
lp->resumed = 1;
return 0;
}
static void
linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
{
struct lwp_info *lp;
int resume_all;
resume_all = (PIDGET (ptid) == -1);
if (resume_all)
iterate_over_lwps (resume_set_callback, NULL);
else
iterate_over_lwps (resume_clear_callback, NULL);
if (PIDGET (ptid) == -1)
ptid = inferior_ptid;
lp = find_lwp_pid (ptid);
if (lp)
{
ptid = pid_to_ptid (GET_LWP (lp->ptid));
lp->step = step;
lp->resumed = 1;
if (lp->status)
{
gdb_assert (signo == TARGET_SIGNAL_0);
return;
}
lp->stopped = 0;
}
if (resume_all)
iterate_over_lwps (resume_callback, NULL);
child_resume (ptid, step, signo);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLR: %s %s, %s (resume event thread)\n",
step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
target_pid_to_str (ptid),
signo ? strsignal (signo) : "0");
}
static int tkill_failed;
static int
kill_lwp (int lwpid, int signo)
{
errno = 0;
#ifdef HAVE_TKILL_SYSCALL
if (!tkill_failed)
{
int ret = syscall (__NR_tkill, lwpid, signo);
if (errno != ENOSYS)
return ret;
errno = 0;
tkill_failed = 1;
}
#endif
return kill (lwpid, signo);
}
static int
linux_nat_handle_extended (struct lwp_info *lp, int status)
{
linux_handle_extended_wait (GET_LWP (lp->ptid), status,
&lp->waitstatus);
if (lp->waitstatus.kind == TARGET_WAITKIND_SPURIOUS)
{
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;
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
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);
return 1;
}
return 0;
}
static int
wait_lwp (struct lwp_info *lp)
{
pid_t pid;
int status;
int thread_dead = 0;
gdb_assert (!lp->stopped);
gdb_assert (lp->status == 0);
pid = my_waitpid (GET_LWP (lp->ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
{
pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
if (pid == -1 && errno == ECHILD)
{
thread_dead = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "WL: %s vanished.\n",
target_pid_to_str (lp->ptid));
}
}
if (!thread_dead)
{
gdb_assert (pid == GET_LWP (lp->ptid));
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"WL: waitpid %s received %s\n",
target_pid_to_str (lp->ptid),
status_to_str (status));
}
}
if (WIFEXITED (status) || WIFSIGNALED (status))
{
thread_dead = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "WL: %s exited.\n",
target_pid_to_str (lp->ptid));
}
if (thread_dead)
{
if (in_thread_list (lp->ptid))
{
if (!ptid_equal (lp->ptid, inferior_ptid))
delete_thread (lp->ptid);
printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
delete_lwp (lp->ptid);
return 0;
}
gdb_assert (WIFSTOPPED (status));
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"WL: Handling extended status 0x%06x\n",
status);
if (linux_nat_handle_extended (lp, status))
return wait_lwp (lp);
}
return status;
}
static int
stop_callback (struct lwp_info *lp, void *data)
{
if (!lp->stopped && !lp->signalled)
{
int ret;
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"SC: kill %s **<SIGSTOP>**\n",
target_pid_to_str (lp->ptid));
}
errno = 0;
ret = kill_lwp (GET_LWP (lp->ptid), SIGSTOP);
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"SC: lwp kill %d %s\n",
ret,
errno ? safe_strerror (errno) : "ERRNO-OK");
}
lp->signalled = 1;
gdb_assert (lp->status == 0);
}
return 0;
}
static int
stop_wait_callback (struct lwp_info *lp, void *data)
{
sigset_t *flush_mask = data;
if (!lp->stopped)
{
int status;
status = wait_lwp (lp);
if (status == 0)
return 0;
if (flush_mask && sigismember (flush_mask, WSTOPSIG (status)))
{
if (!lp->signalled)
{
lp->stopped = 1;
return 0;
}
errno = 0;
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"PTRACE_CONT %s, 0, 0 (%s)\n",
target_pid_to_str (lp->ptid),
errno ? safe_strerror (errno) : "OK");
return stop_wait_callback (lp, flush_mask);
}
if (WSTOPSIG (status) != SIGSTOP)
{
if (WSTOPSIG (status) == SIGTRAP)
{
errno = 0;
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"PTRACE_CONT %s, 0, 0 (%s)\n",
target_pid_to_str (lp->ptid),
errno ? safe_strerror (errno) : "OK");
fprintf_unfiltered (gdb_stdlog,
"SWC: Candidate SIGTRAP event in %s\n",
target_pid_to_str (lp->ptid));
}
stop_wait_callback (lp, data);
if (lp->status)
{
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"SWC: kill %s, %s\n",
target_pid_to_str (lp->ptid),
status_to_str ((int) status));
}
kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (lp->status));
}
lp->status = status;
return 0;
}
else
{
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"SWC: Pending event %s in %s\n",
status_to_str ((int) status),
target_pid_to_str (lp->ptid));
}
errno = 0;
ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"SWC: PTRACE_CONT %s, 0, 0 (%s)\n",
target_pid_to_str (lp->ptid),
errno ? safe_strerror (errno) : "OK");
stop_wait_callback (lp, data);
if (lp->status == 0)
lp->status = status;
else
{
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"SWC: kill %s, %s\n",
target_pid_to_str (lp->ptid),
status_to_str ((int) status));
}
kill_lwp (GET_LWP (lp->ptid), WSTOPSIG (status));
}
return 0;
}
}
else
{
lp->stopped = 1;
lp->signalled = 0;
}
}
return 0;
}
static int
linux_nat_has_pending (int pid, sigset_t *pending, sigset_t *flush_mask)
{
sigset_t blocked, ignored;
int i;
linux_proc_pending_signals (pid, pending, &blocked, &ignored);
if (!flush_mask)
return 0;
for (i = 1; i < NSIG; i++)
if (sigismember (pending, i))
if (!sigismember (flush_mask, i)
|| sigismember (&blocked, i)
|| sigismember (&ignored, i))
sigdelset (pending, i);
if (sigisemptyset (pending))
return 0;
return 1;
}
static int
flush_callback (struct lwp_info *lp, void *data)
{
sigset_t *flush_mask = data;
sigset_t pending, intersection, blocked, ignored;
int pid, status;
if (lwp_list == lp && lp->next == NULL)
if (!linux_nat_thread_alive (lp->ptid))
return 0;
if (lp->status)
{
if (debug_linux_nat)
printf_unfiltered (_("FC: LP has pending status %06x\n"), lp->status);
if (WIFSTOPPED (lp->status) && sigismember (flush_mask, WSTOPSIG (lp->status)))
lp->status = 0;
}
while (linux_nat_has_pending (GET_LWP (lp->ptid), &pending, flush_mask))
{
int ret;
errno = 0;
ret = ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0, 0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stderr,
"FC: Sent PTRACE_CONT, ret %d %d\n", ret, errno);
lp->stopped = 0;
stop_wait_callback (lp, flush_mask);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stderr,
"FC: Wait finished; saved status is %d\n",
lp->status);
}
return 0;
}
static int
status_callback (struct lwp_info *lp, void *data)
{
return (lp->status != 0 && lp->resumed);
}
static int
running_callback (struct lwp_info *lp, void *data)
{
return (lp->stopped == 0 || (lp->status != 0 && lp->resumed));
}
static int
count_events_callback (struct lwp_info *lp, void *data)
{
int *count = data;
gdb_assert (count != NULL);
if (lp->status != 0
&& WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP)
(*count)++;
return 0;
}
static int
select_singlestep_lwp_callback (struct lwp_info *lp, void *data)
{
if (lp->step && lp->status != 0)
return 1;
else
return 0;
}
static int
select_event_lwp_callback (struct lwp_info *lp, void *data)
{
int *selector = data;
gdb_assert (selector != NULL);
if (lp->status != 0
&& WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP)
if ((*selector)-- == 0)
return 1;
return 0;
}
static int
cancel_breakpoints_callback (struct lwp_info *lp, void *data)
{
struct lwp_info *event_lp = data;
if (lp == event_lp)
return 0;
if (lp->status != 0
&& WIFSTOPPED (lp->status) && WSTOPSIG (lp->status) == SIGTRAP
&& breakpoint_inserted_here_p (read_pc_pid (lp->ptid) -
DECR_PC_AFTER_BREAK))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"CBC: Push back breakpoint for %s\n",
target_pid_to_str (lp->ptid));
if (DECR_PC_AFTER_BREAK)
write_pc_pid (read_pc_pid (lp->ptid) - DECR_PC_AFTER_BREAK, lp->ptid);
lp->status = 0;
}
return 0;
}
static void
select_event_lwp (struct lwp_info **orig_lp, int *status)
{
int num_events = 0;
int random_selector;
struct lwp_info *event_lp;
(*orig_lp)->status = *status;
event_lp = iterate_over_lwps (select_singlestep_lwp_callback, NULL);
if (event_lp != NULL)
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"SEL: Select single-step %s\n",
target_pid_to_str (event_lp->ptid));
}
else
{
iterate_over_lwps (count_events_callback, &num_events);
random_selector = (int)
((num_events * (double) rand ()) / (RAND_MAX + 1.0));
if (debug_linux_nat && num_events > 1)
fprintf_unfiltered (gdb_stdlog,
"SEL: Found %d SIGTRAP events, selecting #%d\n",
num_events, random_selector);
event_lp = iterate_over_lwps (select_event_lwp_callback,
&random_selector);
}
if (event_lp != NULL)
{
*orig_lp = event_lp;
*status = event_lp->status;
}
(*orig_lp)->status = 0;
}
static int
resumed_callback (struct lwp_info *lp, void *data)
{
return lp->resumed;
}
#ifdef CHILD_WAIT
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 ();
set_sigio_trap ();
pid = my_waitpid (GET_PID (ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
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;
if (pid != -1 && !WIFSTOPPED (status) && pid != GET_PID (inferior_ptid))
{
pid = -1;
save_errno = EINTR;
}
if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP
&& pid != GET_PID (inferior_ptid))
{
linux_record_stopped_pid (pid);
pid = -1;
save_errno = EINTR;
}
if (pid != -1 && WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
&& status >> 16 != 0)
{
linux_handle_extended_wait (pid, status, ourstatus);
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));
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);
}
#endif
static int
stop_and_resume_callback (struct lwp_info *lp, void *data)
{
struct lwp_info *ptr;
if (!lp->stopped && !lp->signalled)
{
stop_callback (lp, NULL);
stop_wait_callback (lp, NULL);
for (ptr = lwp_list; ptr; ptr = ptr->next)
if (lp == ptr)
{
resume_callback (lp, NULL);
resume_set_callback (lp, NULL);
}
}
return 0;
}
static ptid_t
linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
{
struct lwp_info *lp = NULL;
int options = 0;
int status = 0;
pid_t pid = PIDGET (ptid);
sigset_t flush_mask;
sigemptyset (&flush_mask);
if (!sigismember (&blocked_mask, SIGCHLD))
{
sigaddset (&blocked_mask, SIGCHLD);
sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
}
retry:
gdb_assert (num_lwps == 0 || iterate_over_lwps (resumed_callback, NULL));
if (pid == -1)
{
lp = iterate_over_lwps (status_callback, NULL);
if (lp)
{
status = lp->status;
lp->status = 0;
if (debug_linux_nat && status)
fprintf_unfiltered (gdb_stdlog,
"LLW: Using pending wait status %s for %s.\n",
status_to_str (status),
target_pid_to_str (lp->ptid));
}
options = __WCLONE | WNOHANG;
}
else if (is_lwp (ptid))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: Waiting for specific LWP %s.\n",
target_pid_to_str (ptid));
lp = find_lwp_pid (ptid);
gdb_assert (lp);
status = lp->status;
lp->status = 0;
if (debug_linux_nat && status)
fprintf_unfiltered (gdb_stdlog,
"LLW: Using pending wait status %s for %s.\n",
status_to_str (status),
target_pid_to_str (lp->ptid));
options = lp->cloned ? __WCLONE : 0;
pid = GET_LWP (ptid);
}
if (status && lp->signalled)
{
registers_changed ();
child_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",
lp->step ? "PTRACE_SINGLESTEP" : "PTRACE_CONT",
target_pid_to_str (lp->ptid));
lp->stopped = 0;
gdb_assert (lp->resumed);
stop_wait_callback (lp, NULL);
}
set_sigint_trap ();
set_sigio_trap ();
while (status == 0)
{
pid_t lwpid;
lwpid = my_waitpid (pid, &status, options);
if (lwpid > 0)
{
gdb_assert (pid == -1 || lwpid == pid);
if (debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"LLW: waitpid %ld received %s\n",
(long) lwpid, status_to_str (status));
}
lp = find_lwp_pid (pid_to_ptid (lwpid));
if (WIFSTOPPED (status) && !lp)
{
linux_record_stopped_pid (lwpid);
status = 0;
continue;
}
if (!WIFSTOPPED (status) && !lp)
{
status = 0;
continue;
}
if (!lp)
{
lp = add_lwp (BUILD_LWP (lwpid, GET_PID (inferior_ptid)));
if (options & __WCLONE)
lp->cloned = 1;
if (threaded)
{
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 (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: Handling extended status 0x%06x\n",
status);
if (linux_nat_handle_extended (lp, status))
{
status = 0;
continue;
}
}
if ((WIFEXITED (status) || WIFSIGNALED (status)) && num_lwps > 1)
{
if (in_thread_list (lp->ptid))
{
if (!ptid_equal (lp->ptid, inferior_ptid))
delete_thread (lp->ptid);
printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
if (GET_PID (lp->ptid) == GET_LWP (lp->ptid))
{
lp->stopped = 1;
iterate_over_lwps (stop_and_resume_callback, NULL);
}
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s exited.\n",
target_pid_to_str (lp->ptid));
delete_lwp (lp->ptid);
if (num_lwps > 0)
{
gdb_assert (iterate_over_lwps (running_callback, NULL));
status = 0;
continue;
}
}
if (num_lwps > 1 && !linux_nat_thread_alive (lp->ptid))
{
if (in_thread_list (lp->ptid))
{
if (!ptid_equal (lp->ptid, inferior_ptid))
delete_thread (lp->ptid);
printf_unfiltered (_("[%s exited]\n"),
target_pid_to_str (lp->ptid));
}
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: %s exited.\n",
target_pid_to_str (lp->ptid));
delete_lwp (lp->ptid);
gdb_assert (iterate_over_lwps (running_callback, NULL));
status = 0;
continue;
}
if (lp->signalled
&& WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP)
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: Delayed SIGSTOP caught for %s.\n",
target_pid_to_str (lp->ptid));
lp->signalled = 0;
registers_changed ();
child_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",
lp->step ?
"PTRACE_SINGLESTEP" : "PTRACE_CONT",
target_pid_to_str (lp->ptid));
lp->stopped = 0;
gdb_assert (lp->resumed);
status = 0;
continue;
}
break;
}
if (pid == -1)
{
options ^= __WCLONE;
if (options & __WCLONE)
sigsuspend (&suspend_mask);
}
gdb_assert (status == 0);
}
clear_sigio_trap ();
clear_sigint_trap ();
gdb_assert (lp);
if (WIFSTOPPED (status))
{
int signo = target_signal_from_host (WSTOPSIG (status));
if (signal_stop_state (signo) == 0
&& signal_print_state (signo) == 0
&& signal_pass_state (signo) == 1)
{
registers_changed ();
child_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",
lp->step ?
"PTRACE_SINGLESTEP" : "PTRACE_CONT",
target_pid_to_str (lp->ptid),
signo ? strsignal (signo) : "0");
lp->stopped = 0;
status = 0;
goto retry;
}
if (signo == TARGET_SIGNAL_INT && signal_pass_state (signo) == 0)
{
sigaddset (&flush_mask, SIGINT);
}
}
lp->stopped = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "LLW: Candidate event %s in %s.\n",
status_to_str (status), target_pid_to_str (lp->ptid));
iterate_over_lwps (stop_callback, NULL);
iterate_over_lwps (stop_wait_callback, &flush_mask);
iterate_over_lwps (flush_callback, &flush_mask);
if (pid == -1)
select_event_lwp (&lp, &status);
iterate_over_lwps (cancel_breakpoints_callback, lp);
if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP)
{
trap_ptid = (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLW: trap_ptid is %s.\n",
target_pid_to_str (trap_ptid));
}
else
trap_ptid = null_ptid;
if (lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
{
*ourstatus = lp->waitstatus;
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
}
else
store_waitstatus (ourstatus, status);
return (threaded ? lp->ptid : pid_to_ptid (GET_LWP (lp->ptid)));
}
static int
kill_callback (struct lwp_info *lp, void *data)
{
errno = 0;
ptrace (PTRACE_KILL, GET_LWP (lp->ptid), 0, 0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"KC: PTRACE_KILL %s, 0, 0 (%s)\n",
target_pid_to_str (lp->ptid),
errno ? safe_strerror (errno) : "OK");
return 0;
}
static int
kill_wait_callback (struct lwp_info *lp, void *data)
{
pid_t pid;
if (lp->cloned)
{
do
{
pid = my_waitpid (GET_LWP (lp->ptid), NULL, __WCLONE);
if (pid != (pid_t) -1 && debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"KWC: wait %s received unknown.\n",
target_pid_to_str (lp->ptid));
}
}
while (pid == GET_LWP (lp->ptid));
gdb_assert (pid == -1 && errno == ECHILD);
}
do
{
pid = my_waitpid (GET_LWP (lp->ptid), NULL, 0);
if (pid != (pid_t) -1 && debug_linux_nat)
{
fprintf_unfiltered (gdb_stdlog,
"KWC: wait %s received unk.\n",
target_pid_to_str (lp->ptid));
}
}
while (pid == GET_LWP (lp->ptid));
gdb_assert (pid == -1 && errno == ECHILD);
return 0;
}
static void
linux_nat_kill (void)
{
iterate_over_lwps (kill_callback, NULL);
iterate_over_lwps (kill_wait_callback, NULL);
target_mourn_inferior ();
}
static void
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);
}
static void
linux_nat_mourn_inferior (void)
{
trap_ptid = null_ptid;
init_lwp_list ();
sigprocmask (SIG_SETMASK, &normal_mask, NULL);
sigemptyset (&blocked_mask);
deprecated_child_ops.to_mourn_inferior ();
}
static int
linux_nat_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len,
int write, struct mem_attrib *attrib,
struct target_ops *target)
{
struct cleanup *old_chain = save_inferior_ptid ();
int 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);
do_cleanups (old_chain);
return xfer;
}
static int
linux_nat_thread_alive (ptid_t ptid)
{
gdb_assert (is_lwp (ptid));
errno = 0;
ptrace (PTRACE_PEEKUSER, GET_LWP (ptid), 0, 0);
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
"LLTA: PTRACE_PEEKUSER %s, 0, 0 (%s)\n",
target_pid_to_str (ptid),
errno ? safe_strerror (errno) : "OK");
if (errno)
return 0;
return 1;
}
static char *
linux_nat_pid_to_str (ptid_t ptid)
{
static char buf[64];
if (is_lwp (ptid))
{
snprintf (buf, sizeof (buf), "LWP %ld", GET_LWP (ptid));
return buf;
}
return normal_pid_to_str (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 = fetch_inferior_registers;
linux_nat_ops.to_store_registers = store_inferior_registers;
linux_nat_ops.deprecated_xfer_memory = linux_nat_xfer_memory;
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_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)
{
}
char *
child_pid_to_exec_file (int pid)
{
char *name1, *name2;
name1 = xmalloc (MAXPATHLEN);
name2 = xmalloc (MAXPATHLEN);
make_cleanup (xfree, name1);
make_cleanup (xfree, name2);
memset (name2, 0, MAXPATHLEN);
sprintf (name1, "/proc/%d/exe", pid);
if (readlink (name1, name2, MAXPATHLEN) > 0)
return name2;
else
return name1;
}
static int
read_mapping (FILE *mapfile,
long long *addr,
long long *endaddr,
char *permissions,
long long *offset,
char *device, long long *inode, char *filename)
{
int ret = fscanf (mapfile, "%llx-%llx %s %llx %s %llx",
addr, endaddr, permissions, offset, device, inode);
filename[0] = '\0';
if (ret > 0 && ret != EOF)
{
ret += fscanf (mapfile, "%[^\n]\n", filename);
}
return (ret != 0 && ret != EOF);
}
static int
linux_nat_find_memory_regions (int (*func) (CORE_ADDR,
unsigned long,
int, int, int, void *), void *obfd)
{
long long pid = PIDGET (inferior_ptid);
char mapsfilename[MAXPATHLEN];
FILE *mapsfile;
long long addr, endaddr, size, offset, inode;
char permissions[8], device[8], filename[MAXPATHLEN];
int read, write, exec;
int ret;
sprintf (mapsfilename, "/proc/%lld/maps", pid);
if ((mapsfile = fopen (mapsfilename, "r")) == NULL)
error (_("Could not open %s."), mapsfilename);
if (info_verbose)
fprintf_filtered (gdb_stdout,
"Reading memory regions from %s\n", mapsfilename);
while (read_mapping (mapsfile, &addr, &endaddr, &permissions[0],
&offset, &device[0], &inode, &filename[0]))
{
size = endaddr - addr;
read = (strchr (permissions, 'r') != 0);
write = (strchr (permissions, 'w') != 0);
exec = (strchr (permissions, 'x') != 0);
if (info_verbose)
{
fprintf_filtered (gdb_stdout,
"Save segment, %lld bytes at 0x%s (%c%c%c)",
size, paddr_nz (addr),
read ? 'r' : ' ',
write ? 'w' : ' ', exec ? 'x' : ' ');
if (filename && filename[0])
fprintf_filtered (gdb_stdout, " for %s", filename);
fprintf_filtered (gdb_stdout, "\n");
}
func (addr, size, read, write, exec, obfd);
}
fclose (mapsfile);
return 0;
}
static char *
linux_nat_do_thread_registers (bfd *obfd, ptid_t ptid,
char *note_data, int *note_size)
{
gdb_gregset_t gregs;
gdb_fpregset_t fpregs;
#ifdef FILL_FPXREGSET
gdb_fpxregset_t fpxregs;
#endif
unsigned long lwp = ptid_get_lwp (ptid);
fill_gregset (&gregs, -1);
note_data = (char *) elfcore_write_prstatus (obfd,
note_data,
note_size,
lwp,
stop_signal, &gregs);
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);
note_data = (char *) elfcore_write_prxfpreg (obfd,
note_data,
note_size,
&fpxregs, sizeof (fpxregs));
#endif
return note_data;
}
struct linux_nat_corefile_thread_data
{
bfd *obfd;
char *note_data;
int *note_size;
int num_notes;
};
static int
linux_nat_corefile_thread_callback (struct lwp_info *ti, void *data)
{
struct linux_nat_corefile_thread_data *args = data;
ptid_t saved_ptid = inferior_ptid;
inferior_ptid = ti->ptid;
registers_changed ();
target_fetch_registers (-1);
args->note_data = linux_nat_do_thread_registers (args->obfd,
ti->ptid,
args->note_data,
args->note_size);
args->num_notes++;
inferior_ptid = saved_ptid;
registers_changed ();
target_fetch_registers (-1);
return 0;
}
static char *
linux_nat_do_registers (bfd *obfd, ptid_t ptid,
char *note_data, int *note_size)
{
registers_changed ();
target_fetch_registers (-1);
return linux_nat_do_thread_registers (obfd,
ptid_build (ptid_get_pid (inferior_ptid),
ptid_get_pid (inferior_ptid),
0),
note_data, note_size);
return note_data;
}
static char *
linux_nat_make_corefile_notes (bfd *obfd, int *note_size)
{
struct linux_nat_corefile_thread_data thread_args;
struct cleanup *old_chain;
char fname[16] = { '\0' };
char psargs[80] = { '\0' };
char *note_data = NULL;
ptid_t current_ptid = inferior_ptid;
gdb_byte *auxv;
int auxv_len;
if (get_exec_file (0))
{
strncpy (fname, strrchr (get_exec_file (0), '/') + 1, sizeof (fname));
strncpy (psargs, get_exec_file (0), sizeof (psargs));
if (get_inferior_args ())
{
strncat (psargs, " ", sizeof (psargs) - strlen (psargs));
strncat (psargs, get_inferior_args (),
sizeof (psargs) - strlen (psargs));
}
note_data = (char *) elfcore_write_prpsinfo (obfd,
note_data,
note_size, fname, psargs);
}
thread_args.obfd = obfd;
thread_args.note_data = note_data;
thread_args.note_size = note_size;
thread_args.num_notes = 0;
iterate_over_lwps (linux_nat_corefile_thread_callback, &thread_args);
if (thread_args.num_notes == 0)
{
note_data = linux_nat_do_registers (obfd, inferior_ptid,
note_data, note_size);
}
else
{
note_data = thread_args.note_data;
}
auxv_len = target_auxv_read (¤t_target, &auxv);
if (auxv_len > 0)
{
note_data = elfcore_write_note (obfd, note_data, note_size,
"CORE", NT_AUXV, auxv, auxv_len);
xfree (auxv);
}
make_cleanup (xfree, note_data);
return note_data;
}
static void
linux_nat_info_proc_cmd (char *args, int from_tty)
{
long long pid = PIDGET (inferior_ptid);
FILE *procfile;
char **argv = NULL;
char buffer[MAXPATHLEN];
char fname1[MAXPATHLEN], fname2[MAXPATHLEN];
int cmdline_f = 1;
int cwd_f = 1;
int exe_f = 1;
int mappings_f = 0;
int environ_f = 0;
int status_f = 0;
int stat_f = 0;
int all = 0;
struct stat dummy;
if (args)
{
if ((argv = buildargv (args)) == NULL)
nomem (0);
else
make_cleanup_freeargv (argv);
}
while (argv != NULL && *argv != NULL)
{
if (isdigit (argv[0][0]))
{
pid = strtoul (argv[0], NULL, 10);
}
else if (strncmp (argv[0], "mappings", strlen (argv[0])) == 0)
{
mappings_f = 1;
}
else if (strcmp (argv[0], "status") == 0)
{
status_f = 1;
}
else if (strcmp (argv[0], "stat") == 0)
{
stat_f = 1;
}
else if (strcmp (argv[0], "cmd") == 0)
{
cmdline_f = 1;
}
else if (strncmp (argv[0], "exe", strlen (argv[0])) == 0)
{
exe_f = 1;
}
else if (strcmp (argv[0], "cwd") == 0)
{
cwd_f = 1;
}
else if (strncmp (argv[0], "all", strlen (argv[0])) == 0)
{
all = 1;
}
else
{
}
argv++;
}
if (pid == 0)
error (_("No current process: you must name one."));
sprintf (fname1, "/proc/%lld", pid);
if (stat (fname1, &dummy) != 0)
error (_("No /proc directory: '%s'"), fname1);
printf_filtered (_("process %lld\n"), pid);
if (cmdline_f || all)
{
sprintf (fname1, "/proc/%lld/cmdline", pid);
if ((procfile = fopen (fname1, "r")) > 0)
{
fgets (buffer, sizeof (buffer), procfile);
printf_filtered ("cmdline = '%s'\n", buffer);
fclose (procfile);
}
else
warning (_("unable to open /proc file '%s'"), fname1);
}
if (cwd_f || all)
{
sprintf (fname1, "/proc/%lld/cwd", pid);
memset (fname2, 0, sizeof (fname2));
if (readlink (fname1, fname2, sizeof (fname2)) > 0)
printf_filtered ("cwd = '%s'\n", fname2);
else
warning (_("unable to read link '%s'"), fname1);
}
if (exe_f || all)
{
sprintf (fname1, "/proc/%lld/exe", pid);
memset (fname2, 0, sizeof (fname2));
if (readlink (fname1, fname2, sizeof (fname2)) > 0)
printf_filtered ("exe = '%s'\n", fname2);
else
warning (_("unable to read link '%s'"), fname1);
}
if (mappings_f || all)
{
sprintf (fname1, "/proc/%lld/maps", pid);
if ((procfile = fopen (fname1, "r")) > 0)
{
long long addr, endaddr, size, offset, inode;
char permissions[8], device[8], filename[MAXPATHLEN];
printf_filtered (_("Mapped address spaces:\n\n"));
if (TARGET_ADDR_BIT == 32)
{
printf_filtered ("\t%10s %10s %10s %10s %7s\n",
"Start Addr",
" End Addr",
" Size", " Offset", "objfile");
}
else
{
printf_filtered (" %18s %18s %10s %10s %7s\n",
"Start Addr",
" End Addr",
" Size", " Offset", "objfile");
}
while (read_mapping (procfile, &addr, &endaddr, &permissions[0],
&offset, &device[0], &inode, &filename[0]))
{
size = endaddr - addr;
if (TARGET_ADDR_BIT == 32)
{
printf_filtered ("\t%#10lx %#10lx %#10x %#10x %7s\n",
(unsigned long) addr,
(unsigned long) endaddr,
(int) size,
(unsigned int) offset,
filename[0] ? filename : "");
}
else
{
printf_filtered (" %#18lx %#18lx %#10x %#10x %7s\n",
(unsigned long) addr,
(unsigned long) endaddr,
(int) size,
(unsigned int) offset,
filename[0] ? filename : "");
}
}
fclose (procfile);
}
else
warning (_("unable to open /proc file '%s'"), fname1);
}
if (status_f || all)
{
sprintf (fname1, "/proc/%lld/status", pid);
if ((procfile = fopen (fname1, "r")) > 0)
{
while (fgets (buffer, sizeof (buffer), procfile) != NULL)
puts_filtered (buffer);
fclose (procfile);
}
else
warning (_("unable to open /proc file '%s'"), fname1);
}
if (stat_f || all)
{
sprintf (fname1, "/proc/%lld/stat", pid);
if ((procfile = fopen (fname1, "r")) > 0)
{
int itmp;
char ctmp;
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Process: %d\n"), itmp);
if (fscanf (procfile, "%s ", &buffer[0]) > 0)
printf_filtered (_("Exec file: %s\n"), buffer);
if (fscanf (procfile, "%c ", &ctmp) > 0)
printf_filtered (_("State: %c\n"), ctmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Parent process: %d\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Process group: %d\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Session id: %d\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("TTY: %d\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("TTY owner process group: %d\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
printf_filtered (_("Flags: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
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"),
(unsigned int) itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
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"),
(unsigned int) itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered ("utime: %d\n", itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered ("stime: %d\n", itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered ("utime, children: %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"),
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"),
(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"),
itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
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);
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);
if (fscanf (procfile, "%u ", &itmp) > 0)
printf_filtered (_("End of text: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
printf_filtered (_("Start of stack: 0x%x\n"), itmp);
#if 0
if (fscanf (procfile, "%u ", &itmp) > 0)
printf_filtered (_("Kernel stack pointer: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
printf_filtered (_("Kernel instr pointer: 0x%x\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Pending signals bitmap: 0x%x\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Blocked signals bitmap: 0x%x\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Ignored signals bitmap: 0x%x\n"), itmp);
if (fscanf (procfile, "%d ", &itmp) > 0)
printf_filtered (_("Catched signals bitmap: 0x%x\n"), itmp);
if (fscanf (procfile, "%u ", &itmp) > 0)
printf_filtered (_("wchan (system call): 0x%x\n"), itmp);
#endif
fclose (procfile);
}
else
warning (_("unable to open /proc file '%s'"), fname1);
}
}
int
linux_proc_xfer_memory (CORE_ADDR addr, gdb_byte *myaddr, int len, int write,
struct mem_attrib *attrib, struct target_ops *target)
{
int fd, ret;
char filename[64];
if (write)
return 0;
if (len < 3 * sizeof (long))
return 0;
sprintf (filename, "/proc/%d/mem", PIDGET (inferior_ptid));
fd = open (filename, O_RDONLY | O_LARGEFILE);
if (fd == -1)
return 0;
#ifdef HAVE_PREAD64
if (pread64 (fd, myaddr, len, addr) != len)
#else
if (lseek (fd, addr, SEEK_SET) == -1 || read (fd, myaddr, len) != len)
#endif
ret = 0;
else
ret = len;
close (fd);
return ret;
}
static void
add_line_to_sigset (const char *line, sigset_t *sigs)
{
int len = strlen (line) - 1;
const char *p;
int signum;
if (line[len] != '\n')
error (_("Could not parse signal set: %s"), line);
p = line;
signum = len * 4;
while (len-- > 0)
{
int digit;
if (*p >= '0' && *p <= '9')
digit = *p - '0';
else if (*p >= 'a' && *p <= 'f')
digit = *p - 'a' + 10;
else
error (_("Could not parse signal set: %s"), line);
signum -= 4;
if (digit & 1)
sigaddset (sigs, signum + 1);
if (digit & 2)
sigaddset (sigs, signum + 2);
if (digit & 4)
sigaddset (sigs, signum + 3);
if (digit & 8)
sigaddset (sigs, signum + 4);
p++;
}
}
void
linux_proc_pending_signals (int pid, sigset_t *pending, sigset_t *blocked, sigset_t *ignored)
{
FILE *procfile;
char buffer[MAXPATHLEN], fname[MAXPATHLEN];
int signum;
sigemptyset (pending);
sigemptyset (blocked);
sigemptyset (ignored);
sprintf (fname, "/proc/%d/status", pid);
procfile = fopen (fname, "r");
if (procfile == NULL)
error (_("Could not open %s"), fname);
while (fgets (buffer, MAXPATHLEN, procfile) != NULL)
{
if (strncmp (buffer, "SigPnd:\t", 8) == 0)
add_line_to_sigset (buffer + 8, pending);
else if (strncmp (buffer, "ShdPnd:\t", 8) == 0)
add_line_to_sigset (buffer + 8, pending);
else if (strncmp (buffer, "SigBlk:\t", 8) == 0)
add_line_to_sigset (buffer + 8, blocked);
else if (strncmp (buffer, "SigIgn:\t", 8) == 0)
add_line_to_sigset (buffer + 8, ignored);
}
fclose (procfile);
}
void
_initialize_linux_nat (void)
{
struct sigaction action;
extern void thread_db_init (struct target_ops *);
deprecated_child_ops.to_find_memory_regions = linux_nat_find_memory_regions;
deprecated_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\
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."));
init_linux_nat_ops ();
add_target (&linux_nat_ops);
thread_db_init (&linux_nat_ops);
sigprocmask (SIG_SETMASK, NULL, &normal_mask);
action.sa_handler = sigchld_handler;
sigemptyset (&action.sa_mask);
action.sa_flags = SA_RESTART;
sigaction (SIGCHLD, &action, NULL);
sigprocmask (SIG_SETMASK, NULL, &suspend_mask);
sigdelset (&suspend_mask, SIGCHLD);
sigemptyset (&blocked_mask);
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);
}
static int
get_signo (const char *name)
{
struct minimal_symbol *ms;
int signo;
ms = lookup_minimal_symbol (name, NULL, NULL);
if (ms == NULL)
return 0;
if (target_read_memory (SYMBOL_VALUE_ADDRESS (ms), (gdb_byte *) &signo,
sizeof (signo)) != 0)
return 0;
return signo;
}
void
lin_thread_get_thread_signals (sigset_t *set)
{
struct sigaction action;
int restart, cancel;
sigemptyset (set);
restart = get_signo ("__pthread_sig_restart");
if (restart == 0)
return;
cancel = get_signo ("__pthread_sig_cancel");
if (cancel == 0)
return;
sigaddset (set, restart);
sigaddset (set, cancel);
action.sa_handler = sigchld_handler;
sigemptyset (&action.sa_mask);
action.sa_flags = SA_RESTART;
sigaction (cancel, &action, NULL);
sigaddset (&blocked_mask, cancel);
sigprocmask (SIG_BLOCK, &blocked_mask, NULL);
sigdelset (&suspend_mask, cancel);
}