Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [RFA] -list-target-groups --available
@ 2008-11-14 18:55 Vladimir Prus
  2008-11-21  0:46 ` Daniel Jacobowitz
  0 siblings, 1 reply; 11+ messages in thread
From: Vladimir Prus @ 2008-11-14 18:55 UTC (permalink / raw)
  To: gdb-patches

[-- 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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-14 18:55 [RFA] -list-target-groups --available Vladimir Prus
@ 2008-11-21  0:46 ` Daniel Jacobowitz
  2008-11-23  2:14   ` Vladimir Prus
  0 siblings, 1 reply; 11+ messages in thread
From: Daniel Jacobowitz @ 2008-11-21  0:46 UTC (permalink / raw)
  To: Vladimir Prus; +Cc: gdb-patches

On Fri, Nov 14, 2008 at 08:03:33PM +0300, Vladimir Prus wrote:
> 
> 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.

All the code looks OK to me.  However, I would not like to add this
patch without testcases or documentation.  There should be a DTD
somewhere, too.

> +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."));
> +}

"info os processes" or "info osdata"?


-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-21  0:46 ` Daniel Jacobowitz
@ 2008-11-23  2:14   ` Vladimir Prus
  2008-11-23  5:16     ` Vladimir Prus
                       ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Vladimir Prus @ 2008-11-23  2:14 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: gdb-patches

On Thursday 20 November 2008 17:42:26 Daniel Jacobowitz wrote:
> On Fri, Nov 14, 2008 at 08:03:33PM +0300, Vladimir Prus wrote:
> > 
> > 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.
> 
> All the code looks OK to me.  However, I would not like to add this
> patch without testcases or documentation.  There should be a DTD
> somewhere, too.

This version has DTD. It also has documentation -- Eli, can you review it.
I did you found a good place where new "info os processes" should be documented,
any suggestions?

I've also added a minimal test for the new functionality -- it basically makes
sure we can get some process list, when using gdbserver on linux. Given that it's
very system-dependent, I don't think we can do better.

> 
> > +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."));
> > +}
> 
> "info os processes" or "info osdata"?

I wonder if the alias should be dropped -- "info processes" can
be easily confused with "info inferiors".

- Volodya


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-23  2:14   ` Vladimir Prus
@ 2008-11-23  5:16     ` Vladimir Prus
  2008-11-24 22:11       ` Daniel Jacobowitz
  2008-11-23 16:11     ` Pedro Alves
                       ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Vladimir Prus @ 2008-11-23  5:16 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: gdb-patches

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

On Saturday 22 November 2008 19:38:50 Vladimir Prus wrote:
> On Thursday 20 November 2008 17:42:26 Daniel Jacobowitz wrote:
> > On Fri, Nov 14, 2008 at 08:03:33PM +0300, Vladimir Prus wrote:
> > > 
> > > 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.
> > 
> > All the code looks OK to me.  However, I would not like to add this
> > patch without testcases or documentation.  There should be a DTD
> > somewhere, too.
> 
> This version has DTD. It also has documentation -- Eli, can you review it.
> I did you found a good place where new "info os processes" should be documented,
> any suggestions?
> 
> I've also added a minimal test for the new functionality -- it basically makes
> sure we can get some process list, when using gdbserver on linux. Given that it's
> very system-dependent, I don't think we can do better.
> 
> > 
> > > +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."));
> > > +}
> > 
> > "info os processes" or "info osdata"?
> 
> I wonder if the alias should be dropped -- "info processes" can
> be easily confused with "info inferiors".

And there's the patch.

- Volodya

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

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

    -list-thread-groups --available
    
            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.
    
    	gdb/doc/
    	* gdb.texinfo (Operating System Information): New appendix.
    
    	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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d96d058..5ba1ed0 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -29078,6 +29078,51 @@ contain registers @samp{ev0h} through @samp{ev31h}, @samp{acc}, and
 these to present registers @samp{ev0} through @samp{ev31} to the
 user.
 
+@node Operating System Information
+@appendix Operating System Information
+@cindex operating system information
+
+Users of @value{GDBN} often wish to obtain information about state of
+the operating system running on the target---for example the list of
+processes, or the list open files.  This section describes the
+mechanism that makes it possible.  This mechanism is similar to the 
+target features mechanism (@pxref{Target Descriptions}), but focuses
+on different aspect of target.
+
+Operating system information is retrived from the target via the
+remote protocol, using @samp{qXfer} requests (@pxref{General Query Packets,
+qXfer}).  The object name in the request should be @samp{osdata}, and
+the @var{annex} identifies the data to be fetched.
+
+@node Process list
+@section Process list
+@cindex operating system information, process list
+
+When requesting process list, the @var{annex} field in the
+@samp{qXfer} request should be @samp{processes}.  The returned data is
+an XML document.  The formal syntax of this document is defined in
+@file{gdb/features/gdb-target.dtd}.
+
+The example content is:
+
+@smallexample
+<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "gdb-target.dtd">
+<osdata type="processes">
+  <item>
+    <column name="pid">1</column>
+    <column name="user">root</column>
+    <column name="command">/sbin/init</column>
+  </item>
+</osdata>
+@end smallexample
+
+Each item is required to include a column with name of @samp{pid},
+that identifies the process.  The @samp{user} and @samp{command}
+columns may be provided, and will be displayed by @value{GDBN}.
+Target may provide additional columns, but no processing of them
+is performed by the current version of @value{GDBN}.
+
 @include gpl.texi
 
 @raisesections
diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd
new file mode 100644
index 0000000..0a8eecb
--- /dev/null
+++ b/gdb/features/osdata.dtd
@@ -0,0 +1,15 @@
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- process-list: Root element with versioning -->
+
+<!ELEMENT osdata (item*)>
+<!ATTLIST osdata type CDATA #REQUIRED>
+
+<!ELEMENT item (column*)>
+
+<!ELEMENT column (#PCDATA)>
+<!ATTLIST column name CDATA #REQUIRED>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 4766cc9..a518217 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
@@ -2049,6 +2053,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,
@@ -2081,6 +2188,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 7f5ec2f..ed92559 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 4580c77..ec600e4 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,
@@ -7341,6 +7344,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;
     }
@@ -8613,6 +8623,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)
 {
@@ -8698,6 +8717,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
@@ -9007,6 +9027,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/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp
index 5af5611..24ed5dd 100644
--- a/gdb/testsuite/gdb.server/ext-run.exp
+++ b/gdb/testsuite/gdb.server/ext-run.exp
@@ -44,5 +44,11 @@ gdb_test "set remote exec-file $target_exec" "" "set remote exec-file"
 gdb_breakpoint main
 gdb_test "run" "Breakpoint.* main .*" "continue to main"
 
+if { [istarget *-*-linux*] } {
+    # On Linux, gdbserver can also report the list of processes.
+# 1 +root +/sbin/init\n.*
+    gdb_test "info os processes" ".*pid +user +command.*1 +root +/sbin/init.*" "get process list"
+}
+
 gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y"
 gdb_test "monitor exit" ""
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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-23  2:14   ` Vladimir Prus
  2008-11-23  5:16     ` Vladimir Prus
@ 2008-11-23 16:11     ` Pedro Alves
  2008-11-24 23:13     ` Eli Zaretskii
  2008-11-25  6:13     ` Eli Zaretskii
  3 siblings, 0 replies; 11+ messages in thread
From: Pedro Alves @ 2008-11-23 16:11 UTC (permalink / raw)
  To: gdb-patches; +Cc: Vladimir Prus, Daniel Jacobowitz

On Saturday 22 November 2008 16:38:50, Vladimir Prus wrote:
> I wonder if the alias should be dropped -- "info processes" can
> be easily confused with "info inferiors".

I agree.  The original patch (and that alias) was written
long before any real multi-process work had been done.  At the time,
the alias seemed like a good idea.  It doesn't look that good
anymore.

-- 
Pedro Alves


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-23  5:16     ` Vladimir Prus
@ 2008-11-24 22:11       ` Daniel Jacobowitz
  0 siblings, 0 replies; 11+ messages in thread
From: Daniel Jacobowitz @ 2008-11-24 22:11 UTC (permalink / raw)
  To: Vladimir Prus; +Cc: gdb-patches

On Sat, Nov 22, 2008 at 07:39:41PM +0300, Vladimir Prus wrote:
> > This version has DTD. It also has documentation -- Eli, can you review it.
> > I did you found a good place where new "info os processes" should be documented,
> > any suggestions?

In addition to the new info commands, there's also a new set remote
command to control use of the packet; the new packet; and the new
qSupported response.  All of these need documentation.  Sorry,
back to you :-(

> +@node Operating System Information
> +@appendix Operating System Information
> +@cindex operating system information
> +
> +Users of @value{GDBN} often wish to obtain information about state of

about the state

> +the operating system running on the target---for example the list of
> +processes, or the list open files.  This section describes the

list of open files

> +mechanism that makes it possible.  This mechanism is similar to the 
> +target features mechanism (@pxref{Target Descriptions}), but focuses
> +on different aspect of target.

on a different

> +Operating system information is retrived from the target via the
> +remote protocol, using @samp{qXfer} requests (@pxref{General Query Packets,
> +qXfer}).  The object name in the request should be @samp{osdata}, and
> +the @var{annex} identifies the data to be fetched.

You can add an anchor to the new packet when it's documented and use
that here; iirc that's already done for some qXfer packets.

> +@node Process list
> +@section Process list
> +@cindex operating system information, process list
> +
> +When requesting process list, the @var{annex} field in the

requesting the process list

> +@samp{qXfer} request should be @samp{processes}.  The returned data is
> +an XML document.  The formal syntax of this document is defined in
> +@file{gdb/features/gdb-target.dtd}.

Wrong DTD.

> +The example content is:

Not sure - "An example document is"?

> +@smallexample
> +<?xml version="1.0"?>
> +<!DOCTYPE target SYSTEM "gdb-target.dtd">

Wrong DTD again.

> +<!-- process-list: Root element with versioning -->

It's <osdata> not <process-list>.  Also there's no version - you
should probably add one, like done for the existing target description
DTD (<target>).

> diff --git a/gdb/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp
> index 5af5611..24ed5dd 100644
> --- a/gdb/testsuite/gdb.server/ext-run.exp
> +++ b/gdb/testsuite/gdb.server/ext-run.exp
> @@ -44,5 +44,11 @@ gdb_test "set remote exec-file $target_exec" "" "set remote exec-file"
>  gdb_breakpoint main
>  gdb_test "run" "Breakpoint.* main .*" "continue to main"
>  
> +if { [istarget *-*-linux*] } {
> +    # On Linux, gdbserver can also report the list of processes.
> +# 1 +root +/sbin/init\n.*
> +    gdb_test "info os processes" ".*pid +user +command.*1 +root +/sbin/init.*" "get process list"
> +}

Stray commented line?  Testcase looks fine otherwise.

-- 
Daniel Jacobowitz
CodeSourcery


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-23  2:14   ` Vladimir Prus
  2008-11-23  5:16     ` Vladimir Prus
  2008-11-23 16:11     ` Pedro Alves
@ 2008-11-24 23:13     ` Eli Zaretskii
  2008-11-25  2:41       ` Eli Zaretskii
  2008-11-25  6:13     ` Eli Zaretskii
  3 siblings, 1 reply; 11+ messages in thread
From: Eli Zaretskii @ 2008-11-24 23:13 UTC (permalink / raw)
  To: Vladimir Prus; +Cc: drow, gdb-patches

> From: Vladimir Prus <vladimir@codesourcery.com>
> Date: Sat, 22 Nov 2008 19:38:50 +0300
> Cc: gdb-patches@sources.redhat.com
> 
> This version has DTD. It also has documentation -- Eli, can you review it.

I will, in a few days.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-24 23:13     ` Eli Zaretskii
@ 2008-11-25  2:41       ` Eli Zaretskii
  2008-12-01  8:12         ` Vladimir Prus
  0 siblings, 1 reply; 11+ messages in thread
From: Eli Zaretskii @ 2008-11-25  2:41 UTC (permalink / raw)
  To: vladimir, drow, gdb-patches

> Date: Mon, 24 Nov 2008 22:07:19 +0200
> From: Eli Zaretskii <eliz@gnu.org>
> Cc: drow@false.org, gdb-patches@sources.redhat.com
> 
> > From: Vladimir Prus <vladimir@codesourcery.com>
> > Date: Sat, 22 Nov 2008 19:38:50 +0300
> > Cc: gdb-patches@sources.redhat.com
> > 
> > This version has DTD. It also has documentation -- Eli, can you review it.
> 
> I will, in a few days.

Actually, it is small enough to review now.

> +@node Operating System Information
> +@appendix Operating System Information
> +@cindex operating system information
> + [...]
> +@node Process list
> +@section Process list

I take it that this last @node is supposed to be a sub-node of
"Operating System Information", right?  If so, there are 2 problems
here:

  . @appendix is an equivalent of @section, so its immediate sub-nodes
    cannot be @section's.  They should be @appendixsubsec instead.

  . There should be a @menu in the parent @node that lists all its
    immediate sub-nodes.  Otherwise, makeinfo will barf or do the
    wrong thing, because it will not be able to intuit the correct
    node structure of the document.


> +Each item is required to include a column with name of @samp{pid},
> +that identifies the process.

"name of `pid'" does not sound right.  Do you mean " the name of the
process identified by @var{pid}"?

> +                            The @samp{user} and @samp{command}
> +columns may be provided

  "The @samp{user} and @samp{command} columns are optional..."

> +Target may provide additional columns, but no processing of them
> +is performed by the current version of @value{GDBN}.

Too much passive tense.  Suggest to rephrase:

  Target may provide additional columns which @value{GDBN} currently
  ignores.

Thanks.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-23  2:14   ` Vladimir Prus
                       ` (2 preceding siblings ...)
  2008-11-24 23:13     ` Eli Zaretskii
@ 2008-11-25  6:13     ` Eli Zaretskii
  3 siblings, 0 replies; 11+ messages in thread
From: Eli Zaretskii @ 2008-11-25  6:13 UTC (permalink / raw)
  To: Vladimir Prus; +Cc: drow, gdb-patches

> From: Vladimir Prus <vladimir@codesourcery.com>
> Date: Sat, 22 Nov 2008 19:38:50 +0300
> Cc: gdb-patches@sources.redhat.com
> 
> I did you found a good place where new "info os processes" should be documented,
> any suggestions?

Since this seems to be about remote targets, how about putting it
inside "Remote Debugging"?

An appendix, like your original suggestion, is also OK, but I think
this stuff is not obscure enough to stash it in an appendix.


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-11-25  2:41       ` Eli Zaretskii
@ 2008-12-01  8:12         ` Vladimir Prus
  2008-12-01 20:39           ` Eli Zaretskii
  0 siblings, 1 reply; 11+ messages in thread
From: Vladimir Prus @ 2008-12-01  8:12 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: drow, gdb-patches

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

On Tuesday 25 November 2008 00:18:00 Eli Zaretskii wrote:

>  . @appendix is an equivalent of @section, so its immediate sub-nodes
>     cannot be @section's.  They should be @appendixsubsec instead.

That lead to texinfo/tex errors. @appendixsection, however, appears to
work fine both to info and pdf output.

> > +Each item is required to include a column with name of @samp{pid},
> > +that identifies the process.
> 
> "name of `pid'" does not sound right.  Do you mean " the name of the
> process identified by @var{pid}"?

I mean that:

- the name of the column should be 'pid'
- the value of the column should identify the process

I've clarified this and also addressed Dan's comments. Revised patch
attached, OK?

- Volodya

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

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

    -list-thread-groups --available
    
            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.
    
    	gdb/doc/
    	* gdb.texinfo (Operating System Information): New appendix.
    	(Operating System Auxiliary Information): Document 'info os processes'
    	(Remote Configuration): Document 'osdata'
    	(General Query Packets): Document qXfer:osdata:read.
    
    	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/NEWS b/gdb/NEWS
index 86125c9..a8c3f05 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -36,6 +36,9 @@ QStartNoAckMode
   operation over reliable transport links.  Use of this packet is
   controlled by the `set remote noack-packet' command.
 
+qXfer:osdata:read
+  Obtains additional operating system information
+
 * Removed remote protocol undocumented extension
 
   An undocumented extension to the remote protocol's `S' stop reply
@@ -172,6 +175,9 @@ macro undef
   These allow macros to be defined, undefined, and listed
   interactively.
 
+info os processes
+  Show operating system information about processes.
+
 * New targets
 
 x86 DICOS			i[34567]86-*-dicos*
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d96d058..6e3ed9d 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -178,6 +178,8 @@ software in general.  We will miss him.
 * Agent Expressions::           The GDB Agent Expression Mechanism
 * Target Descriptions::         How targets can describe themselves to
                                 @value{GDBN}
+* Operating System Information:: Getting additional information from
+                                 the operating system
 * Copying::			GNU General Public License says
                                 how you can copy and share GDB
 * GNU Free Documentation License::  The license for this documentation
@@ -7595,6 +7597,18 @@ most appropriate form for a recognized tag, and in hexadecimal for
 an unrecognized tag.
 @end table
 
+On some targets, @value{GDBN} can access operating-system-specific information
+and display it to user, without interpretation.  For remote targets,
+this functionality depends on the remote stub's support of the 
+@samp{qXfer:osdata:read} packet, see @ref{qXfer osdata read}.
+
+@table @code
+@kindex info os processes
+@item info os processes
+Display the list of processes on the target.  For each process,
+@value{GDBN} prints the process identifier, the name of the user, and
+the command corresponding to the process.
+@end table
 
 @node Memory Region Attributes
 @section Memory Region Attributes
@@ -14332,6 +14346,10 @@ are:
 @item @code{noack-packet}
 @tab @code{QStartNoAckMode}
 @tab Packet acknowledgment
+
+@item @code{osdata}
+@tab @code{qXfer:osdata:read}
+@tab @code{info os}
 @end multitable
 
 @node Remote Stub
@@ -26541,6 +26559,10 @@ debugging of more than one process at a time.  The stub must not use
 multiprocess extensions in packet replies unless @value{GDBN} has also
 indicated it supports them in its @samp{qSupported} request.
 
+@item qXfer:osdata:read
+The remote stub understands the @samp{qXfer:osdata:read} packet
+((@pxref{qXfer osdata read}).
+
 @end table
 
 @item qSymbol::
@@ -26679,7 +26701,14 @@ in the target process, and @var{name} identifes the @code{spufs} file
 in that context to be accessed.
 
 This packet is not probed by default; the remote stub must request it,
-by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
+by supplying an appropriate @samp{qSupported} response
+(@pxref{qSupported}).
+
+@item qXfer:osdata:read::@var{offset},@var{length}
+@anchor{qXfer osdata read}
+Access the target's @dfn{operating system information}.  
+@xref{Operating System Information}.
+
 @end table
 
 Reply:
@@ -29078,6 +29107,55 @@ contain registers @samp{ev0h} through @samp{ev31h}, @samp{acc}, and
 these to present registers @samp{ev0} through @samp{ev31} to the
 user.
 
+@node Operating System Information
+@appendix Operating System Information
+@cindex operating system information
+
+@menu
+* Process list::
+@end menu
+
+Users of @value{GDBN} often wish to obtain information about the state of
+the operating system running on the target---for example the list of
+processes, or the list of open files.  This section describes the
+mechanism that makes it possible.  This mechanism is similar to the 
+target features mechanism (@pxref{Target Descriptions}), but focuses
+on a different aspect of target.
+
+Operating system information is retrived from the target via the
+remote protocol, using @samp{qXfer} requests (@pxref{qXfer osdata
+read}).  The object name in the request should be @samp{osdata}, and
+the @var{annex} identifies the data to be fetched.
+
+@node Process list
+@appendixsection Process list
+@cindex operating system information, process list
+
+When requesting the process list, the @var{annex} field in the
+@samp{qXfer} request should be @samp{processes}.  The returned data is
+an XML document.  The formal syntax of this document is defined in
+@file{gdb/features/osdata.dtd}.
+
+An example document is:
+
+@smallexample
+<?xml version="1.0"?>
+<!DOCTYPE target SYSTEM "osdata.dtd">
+<osdata type="processes">
+  <item>
+    <column name="pid">1</column>
+    <column name="user">root</column>
+    <column name="command">/sbin/init</column>
+  </item>
+</osdata>
+@end smallexample
+
+Each item should include a column with name of @samp{pid}.  The value
+of that column should identify the process on the target.  The
+@samp{user} and @samp{command} columns are optional, and will be
+displayed by @value{GDBN}.  Target may provide additional columns,
+which @value{GDBN} currently ignores.
+
 @include gpl.texi
 
 @raisesections
diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd
new file mode 100644
index 0000000..f5f9dc0
--- /dev/null
+++ b/gdb/features/osdata.dtd
@@ -0,0 +1,16 @@
+<!-- Copyright (C) 2008 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!-- osdata: Root element with versioning -->
+
+<!ELEMENT osdata (item*)>
+<!ATTLIST osdata version CDATA #FIXED "1.0">
+<!ATTLIST osdata type CDATA #REQUIRED>
+
+<!ELEMENT item (column*)>
+
+<!ELEMENT column (#PCDATA)>
+<!ATTLIST column name CDATA #REQUIRED>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 4766cc9..a518217 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
@@ -2049,6 +2053,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,
@@ -2081,6 +2188,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 7f5ec2f..ed92559 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 4580c77..ec600e4 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,
@@ -7341,6 +7344,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;
     }
@@ -8613,6 +8623,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)
 {
@@ -8698,6 +8717,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
@@ -9007,6 +9027,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/testsuite/gdb.server/ext-run.exp b/gdb/testsuite/gdb.server/ext-run.exp
index 5af5611..735c28f 100644
--- a/gdb/testsuite/gdb.server/ext-run.exp
+++ b/gdb/testsuite/gdb.server/ext-run.exp
@@ -44,5 +44,10 @@ gdb_test "set remote exec-file $target_exec" "" "set remote exec-file"
 gdb_breakpoint main
 gdb_test "run" "Breakpoint.* main .*" "continue to main"
 
+if { [istarget *-*-linux*] } {
+    # On Linux, gdbserver can also report the list of processes.
+    gdb_test "info os processes" ".*pid +user +command.*1 +root +/sbin/init.*" "get process list"
+}
+
 gdb_test "kill" "" "kill" "Kill the program being debugged.*" "y"
 gdb_test "monitor exit" ""
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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [RFA] -list-target-groups --available
  2008-12-01  8:12         ` Vladimir Prus
@ 2008-12-01 20:39           ` Eli Zaretskii
  0 siblings, 0 replies; 11+ messages in thread
From: Eli Zaretskii @ 2008-12-01 20:39 UTC (permalink / raw)
  To: Vladimir Prus; +Cc: drow, gdb-patches

> From: Vladimir Prus <vladimir@codesourcery.com>
> Date: Mon, 1 Dec 2008 11:11:20 +0300
> Cc: drow@false.org,  gdb-patches@sources.redhat.com
> 
> >  . @appendix is an equivalent of @section, so its immediate sub-nodes
> >     cannot be @section's.  They should be @appendixsubsec instead.
> 
> That lead to texinfo/tex errors. @appendixsection, however, appears to
> work fine both to info and pdf output.

Sorry, my bad.  Yes, @appendixsection is the right command.

> > > +Each item is required to include a column with name of @samp{pid},
> > > +that identifies the process.
> > 
> > "name of `pid'" does not sound right.  Do you mean " the name of the
> > process identified by @var{pid}"?
> 
> I mean that:
> 
> - the name of the column should be 'pid'
> - the value of the column should identify the process
> 
> I've clarified this and also addressed Dan's comments. Revised patch
> attached, OK?

Yes, but I have a better suggestion:

> +Each item should include a column with name of @samp{pid}.

  Each item should include a column whose name is @samp{pid}.


^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2008-12-01 20:39 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-11-14 18:55 [RFA] -list-target-groups --available Vladimir Prus
2008-11-21  0:46 ` Daniel Jacobowitz
2008-11-23  2:14   ` Vladimir Prus
2008-11-23  5:16     ` Vladimir Prus
2008-11-24 22:11       ` Daniel Jacobowitz
2008-11-23 16:11     ` Pedro Alves
2008-11-24 23:13     ` Eli Zaretskii
2008-11-25  2:41       ` Eli Zaretskii
2008-12-01  8:12         ` Vladimir Prus
2008-12-01 20:39           ` Eli Zaretskii
2008-11-25  6:13     ` Eli Zaretskii

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox