From 07e059b5a9cbdeab8bf2980e07e79f892a2da854 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Tue, 2 Dec 2008 07:57:38 +0000 Subject: [PATCH] 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. --- gdb/ChangeLog | 30 +++ gdb/Makefile.in | 6 +- gdb/NEWS | 6 + gdb/doc/ChangeLog | 7 + gdb/doc/gdb.texinfo | 80 +++++- gdb/features/osdata.dtd | 16 ++ gdb/gdbserver/ChangeLog | 16 ++ gdb/gdbserver/linux-low.c | 108 ++++++++ gdb/gdbserver/remote-utils.c | 92 +++++++ gdb/gdbserver/server.c | 36 +++ gdb/gdbserver/server.h | 34 +++ gdb/gdbserver/target.h | 5 + gdb/linux-nat.c | 115 +++++++++ gdb/mi/mi-main.c | 37 ++- gdb/osdata.c | 357 +++++++++++++++++++++++++++ gdb/osdata.h | 52 ++++ gdb/remote.c | 23 ++ gdb/target.c | 20 ++ gdb/target.h | 9 +- gdb/testsuite/gdb.server/ext-run.exp | 5 + gdb/xml-support.c | 39 +++ gdb/xml-support.h | 7 + 22 files changed, 1093 insertions(+), 7 deletions(-) create mode 100644 gdb/features/osdata.dtd create mode 100644 gdb/osdata.c create mode 100644 gdb/osdata.h diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 46c6e4f3358..b03502a33f3 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,33 @@ +2008-12-02 Pedro Alves + Vladimir Prus + + 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 * infrun.c (proceed): Delete unused local stop_signal. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 5432c888208..47b3be08538 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 657c20408da..415a4bed14c 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 @@ -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* diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 9f4ec6d5bfb..2819d883d91 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2008-12-02 Vladimir Prus + + * 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 * gdb.texinfo (Darwin): Document Darwin specific features. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 2a86017cf54..73e2bb4c070 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 @@ -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 + + + + + 1 + root + /sbin/init + + +@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 index 00000000000..f5f9dc0ab48 --- /dev/null +++ b/gdb/features/osdata.dtd @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 66a66f44177..b5afa51fb85 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,19 @@ +2008-12-02 Pedro Alves + + * 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 * Makefile.in (VERSION,DIST,LINT,LINTFLAGS): Delete, unused. diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 4766cc91e9e..a518217fdbd 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -33,6 +33,10 @@ #include #include #include +#include +#include +#include +#include #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, ""); + + 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, + "" + "%s" + "%s" + "%s" + "", + dp->d_name, + entry ? entry->pw_name : "?", + cmd); + } + fclose (f); + } + } + } + + closedir (dirp); + } + buffer_grow_str0 (&buffer, "\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 b5665f58956..d37d56dd4a7 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 f8059e45e7d..33db8a78dcf 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 817b5c47ef9..ca5530dea09 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 9f3b89969dc..8b45f29891e 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 913bfecc659..e537c6f0b86 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 +#include +#include "gdb_dirent.h" +#include "xml-support.h" #ifdef HAVE_PERSONALITY # include @@ -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, "\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, + "" + "%s" + "%s" + "%s" + "", + dp->d_name, + entry ? entry->pw_name : "?", + cmd); + } + fclose (f); + } + + xfree (pathname); + } + } + + closedir (dirp); + } + + obstack_grow_str0 (&obstack, "\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) diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c index 7f5ec2f6778..ed925594839 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 #include @@ -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 00000000000..761f34ba53f --- /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 . */ + +#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 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 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 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 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 . */ + +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 00000000000..a48dca27bc7 --- /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 . */ + +#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 06ab78317fc..c8a46eedc46 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -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); diff --git a/gdb/target.c b/gdb/target.c index 3901ee7585b..966ab7b1596 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 (¤t_target)) + t = ¤t_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 05b681d0447..65201eb645c 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); + /* 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 5af561159d4..735c28fb1f8 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 0809844e7c5..e7f6279ae89 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 bdedb8684c8..f06b4a7b7ba 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 -- 2.30.2