aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/gdbserver')
-rw-r--r--gdb/gdbserver/ChangeLog14
-rw-r--r--gdb/gdbserver/linux-low.c274
-rw-r--r--gdb/gdbserver/remote-utils.c22
-rw-r--r--gdb/gdbserver/server.c120
-rw-r--r--gdb/gdbserver/target.h3
5 files changed, 400 insertions, 33 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 3ec151c..f947937 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,17 @@
+2010-01-13 Vladimir Prus <vladimir@codesourcery.com>
+
+ * linux-low.c (linux_core_of_thread): New.
+ (compare_ints, show_process, list_threads): New.
+ (linux_qxfer_osdata): Report threads and cores.
+ (linux_target_op): Register linux_core_of_thread.
+ * remote-utils.c (prepare_resume_reply): Report the core.
+ (buffer_xml_printf): Support %d specifier.
+ * server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
+ New.
+ (handle_query): Handle qXfer:threads. Announce availability
+ thereof.
+ * target.h (struct target_ops): New field core_of_thread.
+
2010-01-04 Ulrich Weigand <uweigand@de.ibm.com>
* Makefile.in (clean): Remove new generated files.
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index c20322e6..5584691 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -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
{
@@ -2802,6 +2803,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,
CORE_ADDR offset, int len)
@@ -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
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 9b5bad8..76953ae 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
gdbserver to know what inferior_ptid is. */
if (1 || !ptid_equal (general_thread, ptid))
{
+ int core = -1;
/* In non-stop, don't change the general thread behind
GDB's back. */
if (!non_stop)
@@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
buf = write_ptid (buf, ptid);
strcat (buf, ";");
buf += strlen (buf);
+
+ if (the_target->core_of_thread)
+ core = (*the_target->core_of_thread) (ptid);
+ if (core != -1)
+ {
+ sprintf (buf, "core:");
+ buf += strlen (buf);
+ sprintf (buf, "%x", core);
+ strcat (buf, ";");
+ buf += strlen (buf);
+ }
}
}
@@ -1604,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...)
prev = f + 1;
}
break;
+ case 'd':
+ {
+ int i = va_arg (ap, int);
+ char b[sizeof ("4294967295")];
+
+ buffer_grow (buffer, prev, f - prev - 1);
+ sprintf (b, "%d", i);
+ buffer_grow_str (buffer, b);
+ prev = f + 1;
+ }
}
percent = 0;
}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 9254121..c781552 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
}
}
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+ struct inferior_list_entry *thread;
+
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for (thread = all_threads.head; thread; thread = thread->next)
+ {
+ ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+ char ptid_s[100];
+ int core = -1;
+ char core_s[21];
+
+ write_ptid (ptid_s, ptid);
+
+ if (the_target->core_of_thread)
+ core = (*the_target->core_of_thread) (ptid);
+
+ if (core != -1)
+ {
+ sprintf (core_s, "%d", core);
+ buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+ ptid_s, core_s);
+ }
+ else
+ {
+ buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+ ptid_s);
+ }
+ }
+
+ buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+ unsigned char *readbuf,
+ CORE_ADDR offset, int length)
+{
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (annex && strcmp (annex, "") != 0)
+ return 0;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+ /* When asked for data at offset 0, generate everything and store into
+ 'result'. Successive reads will be served off 'result'. */
+ if (result)
+ free (result);
+
+ buffer_init (&buffer);
+
+ handle_threads_qxfer_proper (&buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (length > result_length - offset)
+ length = result_length - offset;
+
+ memcpy (readbuf, result + offset, length);
+
+ return length;
+
+}
+
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}
+ if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+ {
+ unsigned char *data;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+ char *annex;
+
+ require_running (own_buf);
+
+ /* Reject any annex; grab the offset and length. */
+ if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+ || annex[0] != '\0')
+ {
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ /* Read one extra byte, as an indicator of whether there is
+ more. */
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ data = malloc (len + 1);
+ if (!data)
+ return;
+ n = handle_threads_qxfer (annex, data, ofs, len + 1);
+ if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
+ strcat (own_buf, ";qXfer:threads:read+");
+
return;
}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index ad21eb7..0c761e9 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -283,6 +283,9 @@ struct target_ops
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
+
+ /* Returns the core given a thread, or -1 if not known. */
+ int (*core_of_thread) (ptid_t);
};
extern struct target_ops *the_target;