Implement core awareness.
[deliverable/binutils-gdb.git] / gdb / gdbserver / linux-low.c
index c20322e6540d432a4a91820c81494e2a85fbea7e..558469162bd06668722bbf0f09f1da2902946db2 100644 (file)
@@ -140,6 +140,7 @@ static int check_removed_breakpoint (struct lwp_info *event_child);
 static void *add_lwp (ptid_t ptid);
 static int linux_stopped_by_watchpoint (void);
 static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int linux_core_of_thread (ptid_t ptid);
 
 struct pending_signals
 {
@@ -2801,6 +2802,175 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
 }
 #endif
 
+static int
+compare_ints (const void *xa, const void *xb)
+{
+  int a = *(const int *)xa;
+  int b = *(const int *)xb;
+
+  return a - b;
+}
+
+static int *
+unique (int *b, int *e)
+{
+  int *d = b;
+  while (++b != e)
+    if (*d != *b)
+      *++d = *b;
+  return ++d;
+}
+
+/* Given PID, iterates over all threads in that process.
+
+   Information about each thread, in a format suitable for qXfer:osdata:thread
+   is printed to BUFFER, if it's not NULL.  BUFFER is assumed to be already
+   initialized, and the caller is responsible for finishing and appending '\0'
+   to it.
+
+   The list of cores that threads are running on is assigned to *CORES, if it
+   is not NULL.  If no cores are found, *CORES will be set to NULL.  Caller
+   should free *CORES.  */
+
+static void
+list_threads (int pid, struct buffer *buffer, char **cores)
+{
+  int count = 0;
+  int allocated = 10;
+  int *core_numbers = xmalloc (sizeof (int) * allocated);
+  char pathname[128];
+  DIR *dir;
+  struct dirent *dp;
+  struct stat statbuf;
+
+  sprintf (pathname, "/proc/%d/task", pid);
+  if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+    {
+      dir = opendir (pathname);
+      if (!dir)
+       {
+         free (core_numbers);
+         return;
+       }
+
+      while ((dp = readdir (dir)) != NULL)
+       {
+         unsigned long lwp = strtoul (dp->d_name, NULL, 10);
+
+         if (lwp != 0)
+           {
+             unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
+
+             if (core != -1)
+               {
+                 char s[sizeof ("4294967295")];
+                 sprintf (s, "%u", core);
+
+                 if (count == allocated)
+                   {
+                     allocated *= 2;
+                     core_numbers = realloc (core_numbers,
+                                             sizeof (int) * allocated);
+                   }
+                 core_numbers[count++] = core;
+                 if (buffer)
+                   buffer_xml_printf (buffer,
+                                      "<item>"
+                                      "<column name=\"pid\">%d</column>"
+                                      "<column name=\"tid\">%s</column>"
+                                      "<column name=\"core\">%s</column>"
+                                      "</item>", pid, dp->d_name, s);
+               }
+             else
+               {
+                 if (buffer)
+                   buffer_xml_printf (buffer,
+                                      "<item>"
+                                      "<column name=\"pid\">%d</column>"
+                                      "<column name=\"tid\">%s</column>"
+                                      "</item>", pid, dp->d_name);
+               }
+           }
+       }
+    }
+
+  if (cores)
+    {
+      *cores = NULL;
+      if (count > 0)
+       {
+         struct buffer buffer2;
+         int *b;
+         int *e;
+         qsort (core_numbers, count, sizeof (int), compare_ints);
+
+         /* Remove duplicates. */
+         b = core_numbers;
+         e = unique (b, core_numbers + count);
+
+         buffer_init (&buffer2);
+
+         for (b = core_numbers; b != e; ++b)
+           {
+             char number[sizeof ("4294967295")];
+             sprintf (number, "%u", *b);
+             buffer_xml_printf (&buffer2, "%s%s",
+                                (b == core_numbers) ? "" : ",", number);
+           }
+         buffer_grow_str0 (&buffer2, "");
+
+         *cores = buffer_finish (&buffer2);
+       }
+    }
+  free (core_numbers);
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+  char pathname[128];
+  FILE *f;
+  char cmd[MAXPATHLEN + 1];
+
+  sprintf (pathname, "/proc/%d/cmdline", pid);
+
+  if ((f = fopen (pathname, "r")) != NULL)
+    {
+      size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+      if (len > 0)
+       {
+         char *cores = 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\">%d</column>"
+                            "<column name=\"user\">%s</column>"
+                            "<column name=\"command\">%s</column>",
+                            pid,
+                            username,
+                            cmd);
+
+         /* This only collects core numbers, and does not print threads.  */
+         list_threads (pid, NULL, &cores);
+
+         if (cores)
+           {
+             buffer_xml_printf (buffer,
+                                "<column name=\"cores\">%s</column>", cores);
+             free (cores);
+           }
+
+         buffer_xml_printf (buffer, "</item>");
+       }
+      fclose (f);
+    }
+}
+
 static int
 linux_qxfer_osdata (const char *annex,
                    unsigned char *readbuf, unsigned const char *writebuf,
@@ -2811,10 +2981,16 @@ linux_qxfer_osdata (const char *annex,
   static const char *buf;
   static long len_avail = -1;
   static struct buffer buffer;
+  int processes = 0;
+  int threads = 0;
 
   DIR *dirp;
 
-  if (strcmp (annex, "processes") != 0)
+  if (strcmp (annex, "processes") == 0)
+    processes = 1;
+  else if (strcmp (annex, "threads") == 0)
+    threads = 1;
+  else
     return 0;
 
   if (!readbuf || writebuf)
@@ -2827,7 +3003,10 @@ linux_qxfer_osdata (const char *annex,
       len_avail = 0;
       buf = NULL;
       buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+      if (processes)
+       buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+      else if (threads)
+       buffer_grow_str (&buffer, "<osdata type=\"threads\">");
 
       dirp = opendir ("/proc");
       if (dirp)
@@ -2846,37 +3025,16 @@ linux_qxfer_osdata (const char *annex,
             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);
+                int pid = (int) strtoul (dp->d_name, NULL, 10);
 
-                if ((f = fopen (pathname, "r")) != NULL)
+                if (processes)
                   {
-                    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);
+                    struct passwd *entry = getpwuid (statbuf.st_uid);
+                    show_process (pid, entry ? entry->pw_name : "?", &buffer);
+                  }
+                else if (threads)
+                  {
+                    list_threads (pid, &buffer, NULL);
                   }
               }
           }
@@ -3152,6 +3310,55 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
   return ret;
 }
 
+static int
+linux_core_of_thread (ptid_t ptid)
+{
+  char filename[sizeof ("/proc//task//stat")
+                + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
+                + 1];
+  FILE *f;
+  char *content = NULL;
+  char *p;
+  char *ts = 0;
+  int content_read = 0;
+  int i;
+  int core;
+
+  sprintf (filename, "/proc/%d/task/%ld/stat",
+          ptid_get_pid (ptid), ptid_get_lwp (ptid));
+  f = fopen (filename, "r");
+  if (!f)
+    return -1;
+
+  for (;;)
+    {
+      int n;
+      content = realloc (content, content_read + 1024);
+      n = fread (content + content_read, 1, 1024, f);
+      content_read += n;
+      if (n < 1024)
+       {
+         content[content_read] = '\0';
+         break;
+       }
+    }
+
+  p = strchr (content, '(');
+  p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+  p = strtok_r (p, " ", &ts);
+  for (i = 0; i != 36; ++i)
+    p = strtok_r (NULL, " ", &ts);
+
+  if (sscanf (p, "%d", &core) == 0)
+    core = -1;
+
+  free (content);
+  fclose (f);
+
+  return core;
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_attach,
@@ -3191,10 +3398,11 @@ static struct target_ops linux_target_ops = {
   linux_start_non_stop,
   linux_supports_multi_process,
 #ifdef USE_THREAD_DB
-  thread_db_handle_monitor_command
+  thread_db_handle_monitor_command,
 #else
-  NULL
+  NULL,
 #endif
+  linux_core_of_thread
 };
 
 static void
This page took 0.026689 seconds and 4 git commands to generate.