* utils.c (xmalloc,xcalloc,xstrdup): New fns.
[deliverable/binutils-gdb.git] / gdb / gdbserver / linux-low.c
index 732aafbc80f88e263bfbc177289c4044b36e8953..120656a55e5c73f33c5c99de90c2408a8c10d40c 100644 (file)
@@ -23,9 +23,7 @@
 #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
@@ -119,6 +121,7 @@ static void stop_all_processes (void);
 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
 {
@@ -131,7 +134,8 @@ 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)
@@ -148,7 +152,7 @@ handle_extended_wait (struct process_info *event_child, int wstat)
   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);
 
@@ -158,9 +162,7 @@ handle_extended_wait (struct process_info *event_child, int wstat)
          /* 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");
@@ -247,7 +249,7 @@ add_process (unsigned long pid)
 {
   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;
@@ -631,17 +633,22 @@ retry:
   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;
     }
 }
 
@@ -1113,7 +1120,7 @@ linux_resume_one_process (struct inferior_list_entry *entry,
          || 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)
@@ -1191,7 +1198,19 @@ linux_resume_one_process (struct inferior_list_entry *entry,
 
   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;
@@ -1272,7 +1291,7 @@ linux_queue_one_thread (struct inferior_list_entry *entry)
   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));
@@ -1401,10 +1420,9 @@ fetch_register (int regno)
          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);
 
@@ -1448,12 +1466,12 @@ usr_store_inferior_registers (int regno)
             & - 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;
@@ -1461,6 +1479,12 @@ usr_store_inferior_registers (int regno)
                  *(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);
@@ -1497,30 +1521,26 @@ regsets_fetch_inferior_registers ()
       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
            {
@@ -1554,18 +1574,22 @@ regsets_store_inferior_registers ()
       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)
        {
@@ -1573,25 +1597,28 @@ regsets_store_inferior_registers ()
          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
            {
@@ -1617,11 +1644,8 @@ void
 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);
@@ -1632,11 +1656,8 @@ void
 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);
@@ -1723,7 +1744,6 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
   = (((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)
     {
@@ -1812,7 +1832,7 @@ linux_test_for_tracefork (void)
 {
   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;
 
@@ -2034,10 +2054,107 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
 }
 #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 = {
@@ -2070,9 +2187,9 @@ static struct target_ops linux_target_ops = {
 #else
   NULL,
 #endif
-  linux_arch_string,
   NULL,
   hostio_last_error_from_errno,
+  linux_qxfer_osdata,
 };
 
 static void
@@ -2092,4 +2209,9 @@ initialize_low (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
 }
This page took 0.042211 seconds and 4 git commands to generate.