#include <sys/wait.h>
#include <stdio.h>
#include <sys/param.h>
-#include <sys/dir.h>
#include <sys/ptrace.h>
-#include <sys/user.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/syscall.h>
#include <sched.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <dirent.h>
#ifndef PTRACE_GETSIGINFO
# define PTRACE_GETSIGINFO 0x4202
static int linux_wait_for_event (struct thread_info *child);
static int check_removed_breakpoint (struct process_info *event_child);
static void *add_process (unsigned long pid);
+static int my_waitpid (int pid, int *status, int flags);
struct pending_signals
{
#define PTRACE_XFER_TYPE long
#ifdef HAVE_LINUX_REGSETS
-static int use_regsets_p = 1;
+static char *disabled_regsets;
+static int num_regsets;
#endif
#define pid_of(proc) ((proc)->head.id)
if (event == PTRACE_EVENT_CLONE)
{
unsigned long new_pid;
- int ret, status;
+ int ret, status = W_STOPCODE (SIGSTOP);
ptrace (PTRACE_GETEVENTMSG, inferior_pid, 0, &new_pid);
/* 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, __WALL);
- } while (ret == -1 && errno == EINTR);
+ ret = my_waitpid (new_pid, &status, __WALL);
if (ret == -1)
perror_with_name ("waiting for new child");
{
struct process_info *process;
- process = (struct process_info *) malloc (sizeof (*process));
+ process = (struct process_info *) xmalloc (sizeof (*process));
memset (process, 0, sizeof (*process));
process->head.id = pid;
if (new_inferior)
{
the_low_target.arch_setup ();
+#ifdef HAVE_LINUX_REGSETS
+ memset (disabled_regsets, 0, num_regsets);
+#endif
new_inferior = 0;
}
if (debug_threads
&& WIFSTOPPED (*wstatp))
{
+ struct thread_info *saved_inferior = current_inferior;
current_inferior = (struct thread_info *)
find_inferior_id (&all_threads, (*childp)->lwpid);
/* For testing only; i386_stop_pc prints out a diagnostic. */
if (the_low_target.get_pc != NULL)
get_stop_pc ();
+ current_inferior = saved_inferior;
}
}
|| process->bp_reinsert != 0))
{
struct pending_signals *p_sig;
- p_sig = malloc (sizeof (*p_sig));
+ p_sig = xmalloc (sizeof (*p_sig));
p_sig->prev = process->pending_signals;
p_sig->signal = signal;
if (info == NULL)
current_inferior = saved_inferior;
if (errno)
- perror_with_name ("ptrace");
+ {
+ /* ESRCH from ptrace either means that the thread was already
+ running (an error) or that it is gone (a race condition). If
+ it's gone, we will get a notification the next time we wait,
+ so we can ignore the error. We could differentiate these
+ two, but it's tricky without waiting; the thread still exists
+ as a zombie, so sending it signal 0 would succeed. So just
+ ignore ESRCH. */
+ if (errno == ESRCH)
+ return;
+
+ perror_with_name ("ptrace");
+ }
}
static struct thread_resume *resume_ptr;
if (process->resume->sig != 0)
{
struct pending_signals *p_sig;
- p_sig = malloc (sizeof (*p_sig));
+ p_sig = xmalloc (sizeof (*p_sig));
p_sig->prev = process->pending_signals;
p_sig->signal = process->resume->sig;
memset (&p_sig->info, 0, sizeof (siginfo_t));
goto error_exit;
}
}
- if (the_low_target.left_pad_xfer
- && register_size (regno) < sizeof (PTRACE_XFER_TYPE))
- supply_register (regno, (buf + sizeof (PTRACE_XFER_TYPE)
- - register_size (regno)));
+
+ if (the_low_target.supply_ptrace_register)
+ the_low_target.supply_ptrace_register (regno, buf);
else
supply_register (regno, buf);
& - sizeof (PTRACE_XFER_TYPE);
buf = alloca (size);
memset (buf, 0, size);
- if (the_low_target.left_pad_xfer
- && register_size (regno) < sizeof (PTRACE_XFER_TYPE))
- collect_register (regno, (buf + sizeof (PTRACE_XFER_TYPE)
- - register_size (regno)));
+
+ if (the_low_target.collect_ptrace_register)
+ the_low_target.collect_ptrace_register (regno, buf);
else
collect_register (regno, buf);
+
for (i = 0; i < size; i += sizeof (PTRACE_XFER_TYPE))
{
errno = 0;
*(PTRACE_XFER_TYPE *) (buf + i));
if (errno != 0)
{
+ /* At this point, ESRCH should mean the process is already gone,
+ in which case we simply ignore attempts to change its registers.
+ See also the related comment in linux_resume_one_process. */
+ if (errno == ESRCH)
+ return;
+
if ((*the_low_target.cannot_store_register) (regno) == 0)
{
char *err = strerror (errno);
void *buf;
int res;
- if (regset->size == 0)
+ if (regset->size == 0 || disabled_regsets[regset - target_regsets])
{
regset ++;
continue;
}
- buf = malloc (regset->size);
+ buf = xmalloc (regset->size);
+#ifndef __sparc__
res = ptrace (regset->get_request, inferior_pid, 0, buf);
+#else
+ res = ptrace (regset->get_request, inferior_pid, buf, 0);
+#endif
if (res < 0)
{
if (errno == EIO)
{
- /* If we get EIO on the first regset, do not try regsets again.
- If we get EIO on a later regset, disable that regset. */
- if (regset == target_regsets)
- {
- use_regsets_p = 0;
- return -1;
- }
- else
- {
- regset->size = 0;
- continue;
- }
+ /* If we get EIO on a regset, do not try it again for
+ this process. */
+ disabled_regsets[regset - target_regsets] = 1;
+ continue;
}
else
{
void *buf;
int res;
- if (regset->size == 0)
+ if (regset->size == 0 || disabled_regsets[regset - target_regsets])
{
regset ++;
continue;
}
- buf = malloc (regset->size);
+ buf = xmalloc (regset->size);
/* First fill the buffer with the current register set contents,
in case there are any items in the kernel's regset that are
not in gdbserver's regcache. */
+#ifndef __sparc__
res = ptrace (regset->get_request, inferior_pid, 0, buf);
+#else
+ res = ptrace (regset->get_request, inferior_pid, buf, 0);
+#endif
if (res == 0)
{
regset->fill_function (buf);
/* Only now do we write the register set. */
- res = ptrace (regset->set_request, inferior_pid, 0, buf);
+#ifndef __sparc__
+ res = ptrace (regset->set_request, inferior_pid, 0, buf);
+#else
+ res = ptrace (regset->set_request, inferior_pid, buf, 0);
+#endif
}
if (res < 0)
{
if (errno == EIO)
{
- /* If we get EIO on the first regset, do not try regsets again.
- If we get EIO on a later regset, disable that regset. */
- if (regset == target_regsets)
- {
- use_regsets_p = 0;
- return -1;
- }
- else
- {
- regset->size = 0;
- continue;
- }
+ /* If we get EIO on a regset, do not try it again for
+ this process. */
+ disabled_regsets[regset - target_regsets] = 1;
+ continue;
+ }
+ else if (errno == ESRCH)
+ {
+ /* At this point, ESRCH should mean the process is already gone,
+ in which case we simply ignore attempts to change its registers.
+ See also the related comment in linux_resume_one_process. */
+ return 0;
}
else
{
linux_fetch_registers (int regno)
{
#ifdef HAVE_LINUX_REGSETS
- if (use_regsets_p)
- {
- if (regsets_fetch_inferior_registers () == 0)
- return;
- }
+ if (regsets_fetch_inferior_registers () == 0)
+ return;
#endif
#ifdef HAVE_LINUX_USRREGS
usr_fetch_inferior_registers (regno);
linux_store_registers (int regno)
{
#ifdef HAVE_LINUX_REGSETS
- if (use_regsets_p)
- {
- if (regsets_store_inferior_registers () == 0)
- return;
- }
+ if (regsets_store_inferior_registers () == 0)
+ return;
#endif
#ifdef HAVE_LINUX_USRREGS
usr_store_inferior_registers (regno);
= (((memaddr + len) - addr) + sizeof (PTRACE_XFER_TYPE) - 1) / sizeof (PTRACE_XFER_TYPE);
/* Allocate buffer of that many longwords. */
register PTRACE_XFER_TYPE *buffer = (PTRACE_XFER_TYPE *) alloca (count * sizeof (PTRACE_XFER_TYPE));
- extern int errno;
if (debug_threads)
{
{
int child_pid, ret, status;
long second_pid;
- char *stack = malloc (STACK_SIZE * 4);
+ char *stack = xmalloc (STACK_SIZE * 4);
linux_supports_tracefork_flag = 0;
}
#endif
-static const char *
-linux_arch_string (void)
+static int
+linux_qxfer_osdata (const char *annex,
+ unsigned char *readbuf, unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
{
- return the_low_target.arch_string;
+ /* We make the process list snapshot when the object starts to be
+ read. */
+ static const char *buf;
+ static long len_avail = -1;
+ static struct buffer buffer;
+
+ DIR *dirp;
+
+ if (strcmp (annex, "processes") != 0)
+ return 0;
+
+ if (!readbuf || writebuf)
+ return 0;
+
+ if (offset == 0)
+ {
+ if (len_avail != -1 && len_avail != 0)
+ buffer_free (&buffer);
+ len_avail = 0;
+ buf = NULL;
+ buffer_init (&buffer);
+ buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+
+ dirp = opendir ("/proc");
+ if (dirp)
+ {
+ struct dirent *dp;
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ struct stat statbuf;
+ char procentry[sizeof ("/proc/4294967295")];
+
+ if (!isdigit (dp->d_name[0])
+ || strlen (dp->d_name) > sizeof ("4294967295") - 1)
+ continue;
+
+ sprintf (procentry, "/proc/%s", dp->d_name);
+ if (stat (procentry, &statbuf) == 0
+ && S_ISDIR (statbuf.st_mode))
+ {
+ char pathname[128];
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+ struct passwd *entry;
+
+ sprintf (pathname, "/proc/%s/cmdline", dp->d_name);
+ entry = getpwuid (statbuf.st_uid);
+
+ if ((f = fopen (pathname, "r")) != NULL)
+ {
+ size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+ if (len > 0)
+ {
+ int i;
+ for (i = 0; i < len; i++)
+ if (cmd[i] == '\0')
+ cmd[i] = ' ';
+ cmd[len] = '\0';
+
+ buffer_xml_printf (
+ &buffer,
+ "<item>"
+ "<column name=\"pid\">%s</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>"
+ "</item>",
+ dp->d_name,
+ entry ? entry->pw_name : "?",
+ cmd);
+ }
+ fclose (f);
+ }
+ }
+ }
+
+ closedir (dirp);
+ }
+ buffer_grow_str0 (&buffer, "</osdata>\n");
+ buf = buffer_finish (&buffer);
+ len_avail = strlen (buf);
+ }
+
+ if (offset >= len_avail)
+ {
+ /* Done. Get rid of the data. */
+ buffer_free (&buffer);
+ buf = NULL;
+ len_avail = 0;
+ return 0;
+ }
+
+ if (len > len_avail - offset)
+ len = len_avail - offset;
+ memcpy (readbuf, buf + offset, len);
+
+ return len;
}
static struct target_ops linux_target_ops = {
#else
NULL,
#endif
- linux_arch_string,
NULL,
hostio_last_error_from_errno,
+ linux_qxfer_osdata,
};
static void
the_low_target.breakpoint_len);
linux_init_signals ();
linux_test_for_tracefork ();
+#ifdef HAVE_LINUX_REGSETS
+ for (num_regsets = 0; target_regsets[num_regsets].size >= 0; num_regsets++)
+ ;
+ disabled_regsets = xmalloc (num_regsets);
+#endif
}