[RFA] -list-target-groups --available

Vladimir Prus
2008-11-14T17:04:47Z

[-- Attachment #1: Type: text/plain, Size: 1498 bytes --]

This patch implements MI '-list-target-groups --available' -- for
native linux and remote targets. In both cases, the command actually
returns the list of processes. The bulk of the patch was written by
Pedro, I've just added MI bits to it.

The information about processes is passed from target via XML, using new
'osdata' target object with 'processes' annex. The data returned looks
like this:

<osdata type=\"processes\">
<item>
<column name="pid">1</column>
<column name="user">root</column>
<column name="command">/sbin/init</column>
</item>
</osdata>

This representation is fairly open-ended -- new information can be added
without changing DTD or parser. Right now, only bare essentials are provided,
and '-list-target-groups --available' will print just those.

Of course, this flexibility will lead to problems later -- frontend is unlikely
to nicely present an arbitrary set of columns target may report. So in future,
we might need to add up some more detailed specification what data target
may report, and how it will advertise that this data is available, and how MI
should show that data, and how frontend should display that. However, that problem
is general "OS-awareness" thing, and this patch allows the frontend to pick
a process to attach to.

Comments?

- Volodya

It's also slightly under-specified -- I
don't really know how a frontend can make use of any extra information about
processes unless it knows about specific new columns that will be avai

[-- Attachment #2: available.patch --]
[-- Type: text/x-diff, Size: 35568 bytes --]

commit 5524fe092aa53f861e22b52da881083dfb41499b
Author: Vladimir Prus <vladimir@codesourcery.com>
Date: Fri Nov 14 19:42:24 2008 +0300

gdb/
* Makefile.in (XMLFILES): Add osdata.dtd.
(SFILES): Add osdata.c.
(COMMON_OBS): Add osdata.o.
* linux-nat.c: Include pwd.h, sys/types.h, gdb_dirent.h and xml-support.h.
(linux_nat_xfer_osdata): New function.
(linux_xfer_partial): Handle TARGET_OBJECT_OSDATA.
* osdata.c: New file.
* osdata.h: New file.
* remote.c (PACKET_qXfer_osdata): New packet enum.
(remote_protocol_features): Add "qXfer:osdata:read".
(remote_read_qxfer): Handle TARGET_OBJECT_OSDATA.
(extended_remote_can_run): New.
(init_extended_remote_ops): Set to_can_run to
extended_remote_can_run.
(_initialize_remote): Add packet config command for
"qXfer:osdata:read".
* xml-support.c (obstack_xml_printf): New function.
* xml-support.h (obstack_xml_printf): Declare.
* target.c (target_get_osdata): New function.
* target.h (enum target_object): Add TARGET_OBJECT_OSDATA.
(target_os_data): Declare.
* features/osdata.dtd: New file.

* mi/mi-main.c (mi_list_thread_groups): Handle the --available
option.

gdbserver/
* target.h (struct target_ops): Add qxfer_osdata member.
* linux-low.c: Include ctype.h and pwd.h and sys/types.h
and dirent.h.
(linux_qxfer_osdata): New functions.
(linux_target_ops): Register linux_qxfer_osdata as qxfer_osdata
callback.
* server.c (handle_query): Handle "qXfer:osdata:read:".
* remote-utils.c (buffer_grow, buffer_free, buffer_init, buffer_finish)
(buffer_xml_printf): New functions.
* server.h (struct buffer): New.
(buffer_grow_str, buffer_grow_str0): New macros.
(buffer_grow, buffer_free, buffer_init, buffer_finish)
(buffer_xml_printf): Declare.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 5432c88..47b3be0 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -434,7 +434,7 @@ RUNTESTFLAGS=

# XML files to build in to GDB.
XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd \
- $(srcdir)/features/library-list.dtd
+ $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd

# This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX
# interface to the serial port. Hopefully if get ported to OS/2, VMS,
@@ -637,7 +637,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
macrotab.c macroexp.c macrocmd.c macroscope.c main.c maint.c \
mdebugread.c memattr.c mem-break.c minsyms.c mipsread.c memory-map.c \
objc-exp.y objc-lang.c \
- objfiles.c osabi.c observer.c \
+ objfiles.c osabi.c observer.c osdata.c \
p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.c \
prologue-value.c \
regcache.c reggroups.c remote.c remote-fileio.c reverse.c \
@@ -808,7 +808,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
- inferior.o
+ inferior.o osdata.o

TSOBS = inflow.o

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 722953e..7d732d0 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -33,6 +33,10 @@
#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
@@ -2037,6 +2041,109 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
}
#endif

+static int
+linux_qxfer_osdata (const char *annex,
+ unsigned char *readbuf, unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ /* 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 = {
linux_create_inferior,
linux_attach,
@@ -2069,6 +2176,7 @@ static struct target_ops linux_target_ops = {
#endif
NULL,
hostio_last_error_from_errno,
+ linux_qxfer_osdata,
};

static void
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index b5665f5..d37d56d 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1291,3 +1291,95 @@ xml_escape_text (const char *text)

return result;
}
+
+void
+buffer_grow (struct buffer *buffer, const char *data, size_t size)
+{
+ char *new_buffer;
+ size_t new_buffer_size;
+
+ if (size == 0)
+ return;
+
+ new_buffer_size = buffer->buffer_size;
+
+ if (new_buffer_size == 0)
+ new_buffer_size = 1;
+
+ while (buffer->used_size + size > new_buffer_size)
+ new_buffer_size *= 2;
+ new_buffer = realloc (buffer->buffer, new_buffer_size);
+ if (!new_buffer)
+ abort ();
+ memcpy (new_buffer + buffer->used_size, data, size);
+ buffer->buffer = new_buffer;
+ buffer->buffer_size = new_buffer_size;
+ buffer->used_size += size;
+}
+
+void
+buffer_free (struct buffer *buffer)
+{
+ if (!buffer)
+ return;
+
+ free (buffer->buffer);
+ buffer->buffer = NULL;
+ buffer->buffer_size = 0;
+ buffer->used_size = 0;
+}
+
+void
+buffer_init (struct buffer *buffer)
+{
+ memset (buffer, 0, sizeof (*buffer));
+}
+
+char*
+buffer_finish (struct buffer *buffer)
+{
+ char *ret = buffer->buffer;
+ buffer->buffer = NULL;
+ buffer->buffer_size = 0;
+ buffer->used_size = 0;
+ return ret;
+}
+
+void
+buffer_xml_printf (struct buffer *buffer, const char *format, ...)
+{
+ va_list ap;
+ const char *f;
+ const char *prev;
+ int percent = 0;
+
+ va_start (ap, format);
+
+ prev = format;
+ for (f = format; *f; f++)
+ {
+ if (percent)
+ {
+ switch (*f)
+ {
+ case 's':
+ {
+ char *p;
+ char *a = va_arg (ap, char *);
+ buffer_grow (buffer, prev, f - prev - 1);
+ p = xml_escape_text (a);
+ buffer_grow_str (buffer, p);
+ free (p);
+ prev = f + 1;
+ }
+ break;
+ }
+ percent = 0;
+ }
+ else if (*f == '%')
+ percent = 1;
+ }
+
+ buffer_grow_str (buffer, prev);
+ va_end (ap);
+}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 4adbf51..1967d64 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -768,6 +768,38 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}

+ if (the_target->qxfer_osdata != NULL
+ && strncmp ("qXfer:osdata:read:", own_buf, 18) == 0)
+ {
+ char *annex;
+ int n;
+ unsigned int len;
+ CORE_ADDR ofs;
+ unsigned char *workbuf;
+
+ strcpy (own_buf, "E00");
+ if (decode_xfer_read (own_buf + 18, &annex, &ofs, &len) < 0)
+ return;
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ workbuf = malloc (len + 1);
+ if (!workbuf)
+ return;
+
+ n = (*the_target->qxfer_osdata) (annex, workbuf, NULL, ofs, len + 1);
+ if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response
+ (own_buf, workbuf, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response
+ (own_buf, workbuf, n, 0);
+
+ free (workbuf);
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -792,6 +824,10 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)

if (transport_is_reliable)
strcat (own_buf, ";QStartNoAckMode+");
+
+ if (the_target->qxfer_osdata != NULL)
+ strcat (own_buf, ";qXfer:osdata:read+");
+
return;
}

diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 817b5c4..ca5530d 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -223,6 +223,40 @@ void monitor_output (const char *msg);

char *xml_escape_text (const char *text);

+/* Simple growing buffer. */
+
+struct buffer
+{
+ char *buffer;
+ size_t buffer_size; /* allocated size */
+ size_t used_size; /* actually used size */
+};
+
+/* Append DATA of size SIZE to the end of BUFFER. Grows the buffer to
+ accommodate the new data. */
+void buffer_grow (struct buffer *buffer, const char *data, size_t size);
+
+/* Release any memory held by BUFFER. */
+void buffer_free (struct buffer *buffer);
+
+/* Initialize BUFFER. BUFFER holds no memory afterwards. */
+void buffer_init (struct buffer *buffer);
+
+/* Return a pointer into BUFFER data, effectivelly transfering
+ ownership of the buffer memory to the caller. Calling buffer_free
+ afterwards has no effect on the returned data. */
+char* buffer_finish (struct buffer *buffer);
+
+/* Simple printf to BUFFER function. Current implemented formatters:
+ %s - grow an xml escaped text in OBSTACK. */
+void buffer_xml_printf (struct buffer *buffer, const char *format, ...)
+ ATTR_FORMAT (printf, 2, 3);;
+
+#define buffer_grow_str(BUFFER,STRING) \
+ buffer_grow (BUFFER, STRING, strlen (STRING))
+#define buffer_grow_str0(BUFFER,STRING) \
+ buffer_grow (BUFFER, STRING, strlen (STRING) + 1)
+
/* Functions from ``signals.c''. */
enum target_signal target_signal_from_host (int hostsig);
int target_signal_to_host_p (enum target_signal oursig);
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 9f3b899..8b45f29 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -188,6 +188,11 @@ struct target_ops
/* Fill BUF with an hostio error packet representing the last hostio
error. */
void (*hostio_last_error) (char *buf);
+
+ /* Read/Write OS data using qXfer packets. */
+ int (*qxfer_osdata) (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf, CORE_ADDR offset,
+ int len);
};

extern struct target_ops *the_target;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 913bfec..e537c6f 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -49,6 +49,10 @@
#include "inf-loop.h"
#include "event-loop.h"
#include "event-top.h"
+#include <pwd.h>
+#include <sys/types.h>
+#include "gdb_dirent.h"
+#include "xml-support.h"

#ifdef HAVE_PERSONALITY
# include <sys/personality.h>
@@ -3995,6 +3999,113 @@ linux_proc_pending_signals (int pid, sigset_t *pending, sigset_t *blocked, sigse
}

static LONGEST
+linux_nat_xfer_osdata (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
+{
+ /* We make the process list snapshot when the object starts to be
+ read. */
+ static const char *buf;
+ static LONGEST len_avail = -1;
+ static struct obstack obstack;
+
+ DIR *dirp;
+
+ gdb_assert (object == TARGET_OBJECT_OSDATA);
+
+ if (strcmp (annex, "processes") != 0)
+ return 0;
+
+ gdb_assert (readbuf && !writebuf);
+
+ if (offset == 0)
+ {
+ if (len_avail != -1 && len_avail != 0)
+ obstack_free (&obstack, NULL);
+ len_avail = 0;
+ buf = NULL;
+ obstack_init (&obstack);
+ obstack_grow_str (&obstack, "<osdata type=\"processes\">\n");
+
+ 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;
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+ struct passwd *entry;
+
+ pathname = xstrprintf ("/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';
+
+ obstack_xml_printf (
+ &obstack,
+ "<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);
+ }
+
+ xfree (pathname);
+ }
+ }
+
+ closedir (dirp);
+ }
+
+ obstack_grow_str0 (&obstack, "</osdata>\n");
+ buf = obstack_finish (&obstack);
+ len_avail = strlen (buf);
+ }
+
+ if (offset >= len_avail)
+ {
+ /* Done. Get rid of the obstack. */
+ obstack_free (&obstack, NULL);
+ buf = NULL;
+ len_avail = 0;
+ return 0;
+ }
+
+ if (len > len_avail - offset)
+ len = len_avail - offset;
+ memcpy (readbuf, buf + offset, len);
+
+ return len;
+}
+
+static LONGEST
linux_xfer_partial (struct target_ops *ops, enum target_object object,
const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
@@ -4005,6 +4116,10 @@ linux_xfer_partial (struct target_ops *ops, enum target_object object,
return procfs_xfer_auxv (ops, object, annex, readbuf, writebuf,
offset, len);

+ if (object == TARGET_OBJECT_OSDATA)
+ return linux_nat_xfer_osdata (ops, object, annex, readbuf, writebuf,
+ offset, len);
+
xfer = linux_proc_xfer_partial (ops, object, annex, readbuf, writebuf,
offset, len);
if (xfer != 0)
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 52935fc..3edbf1e 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -48,6 +48,7 @@
#include "language.h"
#include "valprint.h"
#include "inferior.h"
+#include "osdata.h"

#include <ctype.h>
#include <sys/time.h>
@@ -378,7 +379,41 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)

back_to = make_cleanup (&null_cleanup, NULL);

- if (id)
+ if (available && id)
+ {
+ error (_("Can only report top-level available thread groups"));
+ }
+ else if (available)
+ {
+ struct osdata *data = get_osdata ("processes");
+ struct osdata_item *item;
+ int ix_items;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "groups");
+
+ for (ix_items = 0;
+ VEC_iterate (osdata_item_s, data->items,
+ ix_items, item);
+ ix_items++)
+ {
+ struct cleanup *back_to =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *pid = get_osdata_column (item, "pid");
+ const char *cmd = get_osdata_column (item, "command");
+ const char *user = get_osdata_column (item, "user");
+
+ ui_out_field_fmt (uiout, "id", "%s", pid);
+ ui_out_field_string (uiout, "type", "process");
+ if (cmd)
+ ui_out_field_string (uiout, "description", cmd);
+ if (user)
+ ui_out_field_string (uiout, "user", user);
+
+ do_cleanups (back_to);
+ }
+ }
+ else if (id)
{
int pid = atoi (id);
if (!in_inferior_list (pid))
diff --git a/gdb/osdata.c b/gdb/osdata.c
new file mode 100644
index 0000000..761f34b
--- /dev/null
+++ b/gdb/osdata.c
@@ -0,0 +1,357 @@
+/* Routines for handling XML generic OS data provided by target.
+
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "target.h"
+#include "vec.h"
+#include "xml-support.h"
+#include "osdata.h"
+#include "gdb_string.h"
+#include "ui-out.h"
+#include "gdbcmd.h"
+
+#if !defined(HAVE_LIBEXPAT)
+
+struct osdata *
+osdata_parse (const char *xml)
+{
+ static int have_warned;
+
+ if (!have_warned)
+ {
+ have_warned = 1;
+ warning (_("Can not parse XML OS data; XML support was disabled "
+ "at compile time"));
+ }
+
+ return NULL;
+}
+
+#else /* HAVE_LIBEXPAT */
+
+#include "xml-support.h"
+
+/* Internal parsing data passed to all XML callbacks. */
+struct osdata_parsing_data
+ {
+ struct osdata *osdata;
+ char *property_name;
+ };
+
+static void
+osdata_item_clear (struct osdata_item *item)
+{
+ if (item->columns != NULL)
+ {
+ struct osdata_column *col;
+ int ix;
+ for (ix = 0;
+ VEC_iterate (osdata_column_s, item->columns,
+ ix, col);
+ ix++)
+ {
+ xfree (col->name);
+ xfree (col->value);
+ }
+ VEC_free (osdata_column_s, item->columns);
+ item->columns = NULL;
+ }
+}
+
+/* Handle the start of a <osdata> element. */
+
+static void
+osdata_start_osdata (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct osdata_parsing_data *data = user_data;
+ char *type;
+ struct osdata *osdata;
+
+ if (data->osdata)
+ gdb_xml_error (parser, _("Seen more than on osdata element"));
+
+ type = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ osdata = XZALLOC (struct osdata);
+ osdata->type = xstrdup (type);
+ data->osdata = osdata;
+}
+
+/* Handle the start of a <item> element. */
+
+static void
+osdata_start_item (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct osdata_parsing_data *data = user_data;
+ struct osdata_item item = { NULL };
+ VEC_safe_push (osdata_item_s, data->osdata->items, &item);
+}
+
+/* Handle the start of a <column> element. */
+
+static void
+osdata_start_column (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct osdata_parsing_data *data = user_data;
+ const char *name = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ data->property_name = xstrdup (name);
+}
+
+/* Handle the end of a <column> element. */
+
+static void
+osdata_end_column (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, const char *body_text)
+{
+ struct osdata_parsing_data *data = user_data;
+ struct osdata *osdata = data->osdata;
+ struct osdata_item *item = VEC_last (osdata_item_s, osdata->items);
+ struct osdata_column *col = VEC_safe_push (osdata_column_s,
+ item->columns, NULL);
+
+ /* Transfer memory ownership. NAME was already strdup'ed. */
+ col->name = data->property_name;
+ col->value = xstrdup (body_text);
+ data->property_name = NULL;
+}
+
+/* Discard the constructed osdata (if an error occurs). */
+
+static void
+clear_parsing_data (void *p)
+{
+ struct osdata_parsing_data *data = p;
+ osdata_free (data->osdata);
+ data->osdata = NULL;
+ xfree (data->property_name);
+ data->property_name = NULL;
+}
+
+/* The allowed elements and attributes for OS data object.
+ The root element is a <osdata>. */
+
+const struct gdb_xml_attribute column_attributes[] = {
+ { "name", GDB_XML_AF_NONE, NULL, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element item_children[] = {
+ { "column", column_attributes, NULL,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ osdata_start_column, osdata_end_column },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_attribute osdata_attributes[] = {
+ { "type", GDB_XML_AF_NONE, NULL, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element osdata_children[] = {
+ { "item", NULL, item_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ osdata_start_item, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element osdata_elements[] = {
+ { "osdata", osdata_attributes, osdata_children,
+ GDB_XML_EF_NONE, osdata_start_osdata, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+struct osdata *
+osdata_parse (const char *xml)
+{
+ struct gdb_xml_parser *parser;
+ struct cleanup *before_deleting_result, *back_to;
+ struct osdata_parsing_data data = { NULL };
+
+ back_to = make_cleanup (null_cleanup, NULL);
+ parser = gdb_xml_create_parser_and_cleanup (_("osdata"),
+ osdata_elements, &data);
+ gdb_xml_use_dtd (parser, "osdata.dtd");
+
+ before_deleting_result = make_cleanup (clear_parsing_data, &data);
+
+ if (gdb_xml_parse (parser, xml) == 0)
+ /* Parsed successfully, don't need to delete the result. */
+ discard_cleanups (before_deleting_result);
+
+ do_cleanups (back_to);
+ return data.osdata;
+}
+#endif
+
+void
+osdata_free (struct osdata *osdata)
+{
+ if (osdata == NULL)
+ return;
+
+ if (osdata->items != NULL)
+ {
+ struct osdata_item *item;
+ int ix;
+ for (ix = 0;
+ VEC_iterate (osdata_item_s, osdata->items,
+ ix, item);
+ ix++)
+ osdata_item_clear (item);
+ VEC_free (osdata_item_s, osdata->items);
+ }
+
+ xfree (osdata);
+}
+
+struct osdata *get_osdata (const char *type)
+{
+ struct osdata * osdata = NULL;
+ char *xml = target_get_osdata (type);
+ if (xml)
+ {
+ if (xml[0] == '\0')
+ warning (_("Empty data returned by target. Wrong osdata type?"));
+
+ osdata = osdata_parse (xml);
+ }
+
+ if (!osdata)
+ error (_("Can not fetch data now.\n"));
+
+ return osdata;
+}
+
+const char *
+get_osdata_column (struct osdata_item *item, const char *name)
+{
+ struct osdata_column *col;
+ int ix_cols;
+
+ for (ix_cols = 0;
+ VEC_iterate (osdata_column_s, item->columns,
+ ix_cols, col);
+ ix_cols++)
+ if (strcmp (col->name, name) == 0)
+ return col->value;
+
+ return NULL;
+}
+
+void
+info_osdata_command (char *type, int from_tty)
+{
+ struct osdata * osdata = NULL;
+ struct cleanup *proc_tbl_chain;
+ struct osdata_item *last;
+ int ncols;
+ int nprocs;
+
+ if (type == 0)
+ /* TODO: No type could mean "list availables types". */
+ error (_("Argument required."));
+
+ osdata = get_osdata (type);
+
+ nprocs = VEC_length (osdata_item_s, osdata->items);
+
+ last = VEC_last (osdata_item_s, osdata->items);
+ if (last && last->columns)
+ ncols = VEC_length (osdata_column_s, last->columns);
+ else
+ ncols = 0;
+
+ proc_tbl_chain
+ = make_cleanup_ui_out_table_begin_end (uiout, ncols, nprocs,
+ "OSDataTable");
+
+ if (last && last->columns)
+ {
+ struct osdata_column *col;
+ int ix;
+ for (ix = 0;
+ VEC_iterate (osdata_column_s, last->columns,
+ ix, col);
+ ix++)
+ ui_out_table_header (uiout, 10, ui_left,
+ col->name, col->name);
+ }
+
+ ui_out_table_body (uiout);
+
+ if (nprocs != 0)
+ {
+ struct osdata_item *item;
+ int ix_items;
+ for (ix_items = 0;
+ VEC_iterate (osdata_item_s, osdata->items,
+ ix_items, item);
+ ix_items++)
+ {
+ struct cleanup *old_chain, *chain;
+ struct ui_stream *stb;
+ int ix_cols;
+ struct osdata_column *col;
+
+ stb = ui_out_stream_new (uiout);
+ old_chain = make_cleanup_ui_out_stream_delete (stb);
+ chain = make_cleanup_ui_out_tuple_begin_end (uiout, "item");
+
+ for (ix_cols = 0;
+ VEC_iterate (osdata_column_s, item->columns,
+ ix_cols, col);
+ ix_cols++)
+ ui_out_field_string (uiout, col->name, col->value);
+
+ do_cleanups (chain);
+ do_cleanups (old_chain);
+
+ ui_out_text (uiout, "\n");
+ }
+ }
+
+ do_cleanups (proc_tbl_chain);
+
+ osdata_free (osdata);
+}
+
+static void
+info_processes_command (char *args, int from_tty)
+{
+ info_osdata_command ("processes", from_tty);
+}
+
+extern initialize_file_ftype _initialize_osdata; /* -Wmissing-prototypes */
+
+void
+_initialize_osdata (void)
+{
+ add_info ("os", info_osdata_command,
+ _("Show OS data ARG."));
+
+ /* An alias for "info osdata processes". */
+ add_info ("processes", info_processes_command,
+ _("List running processes on the target."));
+}
diff --git a/gdb/osdata.h b/gdb/osdata.h
new file mode 100644
index 0000000..a48dca2
--- /dev/null
+++ b/gdb/osdata.h
@@ -0,0 +1,52 @@
+/* Routines for handling XML generic OS data provided by target.
+
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef OSDATA_H
+#define OSDATA_H
+
+#include "vec.h"
+
+typedef struct osdata_column
+{
+ char *name;
+ char *value;
+} osdata_column_s;
+DEF_VEC_O(osdata_column_s);
+
+typedef struct osdata_item
+{
+ VEC(osdata_column_s) *columns;
+} osdata_item_s;
+DEF_VEC_O(osdata_item_s);
+
+struct osdata
+{
+ char *type;
+
+ VEC(osdata_item_s) *items;
+};
+typedef struct osdata *osdata_p;
+DEF_VEC_P(osdata_p);
+
+struct osdata *osdata_parse (const char *xml);
+void osdata_free (struct osdata *);
+struct osdata *get_osdata (const char *type);
+const char *get_osdata_column (struct osdata_item *item, const char *name);
+
+#endif /* OSDATA_H */
diff --git a/gdb/remote.c b/gdb/remote.c
index 5cb36b8..c56357a 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -990,6 +990,7 @@ enum {
PACKET_qXfer_memory_map,
PACKET_qXfer_spu_read,
PACKET_qXfer_spu_write,
+ PACKET_qXfer_osdata,
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
@@ -2945,6 +2946,8 @@ static struct protocol_feature remote_protocol_features[] = {
PACKET_qXfer_spu_read },
{ "qXfer:spu:write", PACKET_DISABLE, remote_supported_packet,
PACKET_qXfer_spu_write },
+ { "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
+ PACKET_qXfer_osdata },
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
PACKET_QPassSignals },
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -7294,6 +7297,13 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
return remote_read_qxfer (ops, "memory-map", annex, readbuf, offset, len,
&remote_protocol_packets[PACKET_qXfer_memory_map]);

+ case TARGET_OBJECT_OSDATA:
+ /* Should only get here if we're connected. */
+ gdb_assert (remote_desc);
+ return remote_read_qxfer
+ (ops, "osdata", annex, readbuf, offset, len,
+ &remote_protocol_packets[PACKET_qXfer_osdata]);
+
default:
return -1;
}
@@ -8566,6 +8576,15 @@ remote_supports_multi_process (void)
return remote_multi_process_p (rs);
}

+static int
+extended_remote_can_run (void)
+{
+ if (remote_desc != NULL)
+ return 1;
+
+ return 0;
+}
+
static void
init_remote_ops (void)
{
@@ -8651,6 +8670,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_detach = extended_remote_detach;
extended_remote_ops.to_attach = extended_remote_attach;
extended_remote_ops.to_kill = extended_remote_kill;
+ extended_remote_ops.to_can_run = extended_remote_can_run;
}

static int
@@ -8960,6 +8980,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_spu_write],
"qXfer:spu:write", "write-spu-object", 0);

+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
+ "qXfer:osdata:read", "osdata", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_qGetTLSAddr],
"qGetTLSAddr", "get-thread-local-storage-address",
0);
diff --git a/gdb/target.c b/gdb/target.c
index 3901ee7..966ab7b 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -2224,6 +2224,26 @@ target_supports_non_stop ()
}

+char *
+target_get_osdata (const char *type)
+{
+ char *document;
+ struct target_ops *t;
+
+ if (target_can_run (&current_target))
+ t = &current_target;
+ else
+ t = find_default_run_target ("get OS data");
+
+ if (!t)
+ return NULL;
+
+ document = target_read_stralloc (t,
+ TARGET_OBJECT_OSDATA,
+ type);
+ return document;
+}
+
static int
default_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
{
diff --git a/gdb/target.h b/gdb/target.h
index 05b681d..65201eb 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -214,8 +214,11 @@ enum target_object
See "target-descriptions.c". ANNEX should never be empty. */
TARGET_OBJECT_AVAILABLE_FEATURES,
/* Currently loaded libraries, in XML format. */
- TARGET_OBJECT_LIBRARIES
- /* Possible future objects: TARGET_OBJECT_FILE, TARGET_OBJECT_PROC, ... */
+ TARGET_OBJECT_LIBRARIES,
+ /* Get OS specific data. The ANNEX specifies the type (running
+ processes, etc.). */
+ TARGET_OBJECT_OSDATA
+ /* Possible future objects: TARGET_OBJECT_FILE, ... */
};

/* Request that OPS transfer up to LEN 8-bit bytes of the target's
@@ -1283,6 +1286,8 @@ extern int target_resize_to_sections (struct target_ops *target,

extern void remove_target_sections (bfd *abfd);

+extern char *target_get_osdata (const char *type);
+
\f
/* Stuff that should be shared among the various remote targets. */

diff --git a/gdb/xml-support.c b/gdb/xml-support.c
index 0809844..e7f6279 100644
--- a/gdb/xml-support.c
+++ b/gdb/xml-support.c
@@ -997,6 +997,45 @@ xml_escape_text (const char *text)
return result;
}

+void
+obstack_xml_printf (struct obstack *obstack, const char *format, ...)
+{
+ va_list ap;
+ const char *f;
+ const char *prev;
+ int percent = 0;
+
+ va_start (ap, format);
+
+ prev = format;
+ for (f = format; *f; f++)
+ {
+ if (percent)
+ {
+ switch (*f)
+ {
+ case 's':
+ {
+ char *p;
+ char *a = va_arg (ap, char *);
+ obstack_grow (obstack, prev, f - prev - 1);
+ p = xml_escape_text (a);
+ obstack_grow_str (obstack, p);
+ xfree (p);
+ prev = f + 1;
+ }
+ break;
+ }
+ percent = 0;
+ }
+ else if (*f == '%')
+ percent = 1;
+ }
+
+ obstack_grow_str (obstack, prev);
+ va_end (ap);
+}
+
void _initialize_xml_support (void);

void
diff --git a/gdb/xml-support.h b/gdb/xml-support.h
index bdedb86..f06b4a7 100644
--- a/gdb/xml-support.h
+++ b/gdb/xml-support.h
@@ -233,4 +233,11 @@ extern gdb_xml_attribute_handler gdb_xml_parse_attr_enum;
ULONGEST gdb_xml_parse_ulongest (struct gdb_xml_parser *parser,
const char *value);

+/* Simple printf to obstack function. Current implemented formatters:
+ %s - grow an xml escaped text in OBSTACK. */
+
+extern void obstack_xml_printf (struct obstack *obstack,
+ const char *format, ...)
+ ATTRIBUTE_PRINTF_2;
+
#endif