]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Implement -list-thread-groups --available
authorVladimir Prus <vladimir@codesourcery.com>
Tue, 2 Dec 2008 07:57:38 +0000 (07:57 +0000)
committerVladimir Prus <vladimir@codesourcery.com>
Tue, 2 Dec 2008 07:57:38 +0000 (07:57 +0000)
        * 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.

22 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/NEWS
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/features/osdata.dtd [new file with mode: 0644]
gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c
gdb/gdbserver/remote-utils.c
gdb/gdbserver/server.c
gdb/gdbserver/server.h
gdb/gdbserver/target.h
gdb/linux-nat.c
gdb/mi/mi-main.c
gdb/osdata.c [new file with mode: 0644]
gdb/osdata.h [new file with mode: 0644]
gdb/remote.c
gdb/target.c
gdb/target.h
gdb/testsuite/gdb.server/ext-run.exp
gdb/xml-support.c
gdb/xml-support.h

index 46c6e4f335876611b780fde5c3ae1d615de7129d..b03502a33f341a3f2c06bdb441508780ef88c34e 100644 (file)
@@ -1,3 +1,33 @@
+2008-12-02  Pedro Alves  <pedro@codesourcery.com>
+           Vladimir Prus  <vladimir@codesourcery.com>
+
+       Implement -list-thread-groups --available
+
+        * 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.
+
 2008-12-01  Doug Evans  <dje@google.com>
 
        * infrun.c (proceed): Delete unused local stop_signal.
index 5432c888208cb62f45aaa59c4947601f9e64f509..47b3be08538a0a06c64d3a2129ae34832b9fcc32 100644 (file)
@@ -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
 
index 657c20408dab4a80e903ede5a655b9a31c27af78..415a4bed14c45d6084b336004fd3510e25e30617 100644 (file)
--- 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
@@ -176,6 +179,9 @@ macro undef
 
 x86/x86_64 Darwin              i[34567]86-*-darwin*
 
+info os processes
+  Show operating system information about processes.
+
 * New targets
 
 x86 DICOS                      i[34567]86-*-dicos*
index 9f4ec6d5bfb56cf4c19e4269c7815cd4b4abcfcd..2819d883d91f26c12ee1dcc2b240ee2cb6e9d211 100644 (file)
@@ -1,3 +1,10 @@
+2008-12-02  Vladimir Prus  <vladimir@codesourcery.com>
+
+        * 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.
+
 2008-11-27  Tristan Gingold  <gingold@adacore.com>
 
        * gdb.texinfo (Darwin): Document Darwin specific features.
index 2a86017cf54672a628c4239c65213fb169c1d928..73e2bb4c070e0d1d1ecb16afaeea5a79eccea937 100644 (file)
@@ -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
@@ -26243,6 +26261,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::
@@ -26381,7 +26403,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:
@@ -28780,6 +28809,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 whose name is @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 (file)
index 0000000..f5f9dc0
--- /dev/null
@@ -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>
index 66a66f441778c522cc271ae0b1670ca085f55fed..b5afa51fb85b6641b5bb1efb84696ac7eb40c5b6 100644 (file)
@@ -1,3 +1,19 @@
+2008-12-02  Pedro Alves  <pedro@codesourcery.com>
+
+        * 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.
+
 2008-11-24  Doug Evans  <dje@google.com>
 
        * Makefile.in (VERSION,DIST,LINT,LINTFLAGS): Delete, unused.
index 4766cc91e9e222efc7b16ccc297daf4f469ce006..a518217fdbddf48cf6e2b0ae194fb9ea76d1a214 100644 (file)
 #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
index b5665f5895693c978002c3dd6f7ba5d577056627..d37d56dd4a745d880356d370bc35ce36701f3d7d 100644 (file)
@@ -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);
+}
index f8059e45e7d96a2acb7d201a1ffa3542609bcc7b..33db8a78dcf56bb025b5d63f51142d51adf34dd8 100644 (file)
@@ -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;
     }
 
index 817b5c47ef998ab61861763dac26fa9d7f561d0e..ca5530dea095a12d5aec65ad6ef75e915bfbb116 100644 (file)
@@ -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);
index 9f3b89969dc4fd0a39aa8e5c7ae30233875b0513..8b45f29891e5a11c4b21a6f97f1ba1671fcf8033 100644 (file)
@@ -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;
index 913bfecc659863d5d65b961c5ec23d9f198e64ea..e537c6f0b86d1f786ba7199eca31af4e443a76f8 100644 (file)
 #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>
@@ -3994,6 +3998,113 @@ linux_proc_pending_signals (int pid, sigset_t *pending, sigset_t *blocked, sigse
   do_cleanups (cleanup);
 }
 
+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,
@@ -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)
index 7f5ec2f6778e4fe7217749327a54849541a8cd52..ed9255948398fdd3f2005921895867236b68a05e 100644 (file)
@@ -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 (file)
index 0000000..761f34b
--- /dev/null
@@ -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 (file)
index 0000000..a48dca2
--- /dev/null
@@ -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 */
index 06ab78317fc9ce51098a340dc3ad9080fdb87eb0..c8a46eedc46e74d7fa11ae2b0f660ac659d69db1 100644 (file)
@@ -992,6 +992,7 @@ enum {
   PACKET_qXfer_memory_map,
   PACKET_qXfer_spu_read,
   PACKET_qXfer_spu_write,
+  PACKET_qXfer_osdata,
   PACKET_qGetTLSAddr,
   PACKET_qSupported,
   PACKET_QPassSignals,
@@ -2958,6 +2959,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,
@@ -7355,6 +7358,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;
     }
@@ -8647,6 +8657,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)
 {
@@ -8732,6 +8751,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
@@ -9041,6 +9061,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);
index 3901ee7585b4bdb1c7372a4a355d9309f69a27a0..966ab7b1596439903d10f9d2c4a641ad7f49356b 100644 (file)
@@ -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)
 {
index 05b681d0447ced9b26e3ef6c13531e2d0411d19e..65201eb645c091e190a122c980635d8871e53e6b 100644 (file)
@@ -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.  */
 
index 5af561159d49d939422d6966dfdea84cb2d88102..735c28fb1f861fa4e39feab0e933d48d696e7d93 100644 (file)
@@ -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" ""
index 0809844e7c574110c481de2cb3146d5f790c94df..e7f6279ae89d1100f79b0f94b9b07b7f2cd48232 100644 (file)
@@ -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
index bdedb8684c82f4fc76caf5685769ec2429512861..f06b4a7b7ba874a1e55777c8e7b0d07d55e65fcb 100644 (file)
@@ -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