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.
This commit is contained in:
Vladimir Prus 2008-12-02 07:57:38 +00:00
parent f3c85de60a
commit 07e059b5a9
22 changed files with 1093 additions and 7 deletions

View 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.

View 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

View File

@ -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*

View 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.

View 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

16
gdb/features/osdata.dtd Normal file
View File

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

View 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.

View File

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

View 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);
}

View 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;
}

View 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);

View 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;

View File

@ -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>
@ -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)

View 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))

357
gdb/osdata.c Normal file
View File

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

52
gdb/osdata.h Normal file
View File

@ -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 */

View 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);

View 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)
{

View 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);
/* Stuff that should be shared among the various remote targets. */

View 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" ""

View 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

View 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