* [MI] core awareness
@ 2009-12-16 20:53 Vladimir Prus
2009-12-17 15:31 ` Pedro Alves
2009-12-18 11:41 ` Eli Zaretskii
0 siblings, 2 replies; 17+ messages in thread
From: Vladimir Prus @ 2009-12-16 20:53 UTC (permalink / raw)
To: gdb-patches
[-- Attachment #1: Type: Text/Plain, Size: 711 bytes --]
The attached patch implements the core awareness, as discussed at:
http://thread.gmane.org/gmane.comp.gdb.devel/27468/
The most current spec can be found at:
http://article.gmane.org/gmane.comp.gdb.devel/27548
To summarize, with this patch, when debugging against gdbserver:
* The -list-thread-groups command will report the cores that each
process runs on (with or without --available)
* It's possible to pass several thread groups to -list-thread-groups
* The *stopped output includes core number
For native debugging on Linux, roughly the same is also possible.
This is mostly MI change, but includes some remote protocol tweaks,
and documentation. I'd appreciate review of those parts.
- Volodya
[-- Attachment #2: core-awareness.diff --]
[-- Type: text/x-patch, Size: 58239 bytes --]
commit 533abb7a66898cde158ea5556604ea20d56f2a47
Author: Vladimir Prus <vladimir@codesourcery.com>
Date: Wed Dec 16 23:39:19 2009 +0300
Implement core awareness.
gdb/
* bcache.c (compare_ints): Remove
(print_percentage): Use compare_positive_ints.
* defs.h (compare_positive_ints): Declare.
* linux-nat.c (linux_nat_core_of_thread): New.
(linux_nat_add_target): Register the above.
* osdata.c (osdata_parsing_data): New fields
top_osdata and current_item.
(osdata_start_threads, osdata_end_threads): New.
(osdata_start_osdata): Init top_osdata.
(osdata_start_item): Record current item.
(osdata_children): New.
(item_children): Permit 'threads' child.
* osdata.h (osdata_item): New field 'children'.
* remote.c (struct private_thread_info): New.
(PACKET_qXfer_threads, use_osdata_threads): New.
(struct thread_item, threads_parsing_context
(start_thread, end_thread, thread_attributes)
(thread_children, threads_children, threads_elements): New.
(remote_threads_info): Try qXfer:threads before anything
else.
(remote_protocol_packets): Register qXfer:threads.
(remote_open_1): Init use_osdata_threads.
(struct stop_reply): New field 'core'.
(remote_parse_stop_reply): Parse core number.
(process_stop_reply): Record core number.
(remote_xfer_partial): Handle qXfer:threads.
(remote_core_of_thread): New.
(init_remote_ops): Register remote_core_of_thread.
(_initialize_remote): Register qXfer:read.
* target.c (target_core_of_thread): New
* target.h (enum target_object): New value TARGET_OBJECT_THREADS.
(struct target_ops): New field to_core_of_threads.
(target_core_of_thread): Declare.
* thread.c (print_thread_info): Report the core.
* ui-out.c (MAX_UI_OUT_LEVELS): Increase.
* utils.c (compare_positive_ints): New.
* features/osdata.dtd: Allow nested threads.
* features/threads.dtd: New.
* mi/mi-interp.c (mi_on_normal_stop): Report the core.
* mi/mi-main.c (struct collect_cores_data, collect_cores)
(print_one_inferior_data): New.
(print_one_inferior_data): Implementing printing of selected
inferiors. Collect and print cores.
(output_cores): New.
(mi_cmd_list_thread_groups): Support --recurse. Permit specifying
thread groups together with --available.
gdbserver/
* linux-low.c (linux_core_of_thread): New.
(compare_ints, list_threads): New.
(linux_qxfer_osdata): Report threads and cores.
(linux_target_op): Register linux_core_of_thread.
* remote-utils.c (prepare_resume_reply): Report the core.
* server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
New.
(handle_query): Handle qXfer:threads. Announce availability
thereof.
* target.h (struct target_ops): New field core_for_threads.
gdb/doc
* gdb.texinfo (GDB/MI Thread Information): New.
(GDB/MI Async Records): Document the core field in *stopped.
(GDB/MI Miscellaneous Commands): Expand -list-thread-groups
documentation
(Process list): Document that osdata document may contain
threads.
(Remote Serial Protocol): Document qXfer:threads.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 348d853..882be85 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -444,7 +444,8 @@ 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/osdata.dtd
+ $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+ $(srcdir)/features/threads.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,
diff --git a/gdb/bcache.c b/gdb/bcache.c
index d56df07..e467c5b 100644
--- a/gdb/bcache.c
+++ b/gdb/bcache.c
@@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
\f
/* Printing statistics. */
-static int
-compare_ints (const void *ap, const void *bp)
-{
- /* Because we know we're comparing two ints which are positive,
- there's no danger of overflow here. */
- return * (int *) ap - * (int *) bp;
-}
-
-
static void
print_percentage (int portion, int total)
{
@@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
/* To compute the median, we need the set of chain lengths sorted. */
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
- compare_ints);
+ compare_positive_ints);
qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
- compare_ints);
+ compare_positive_ints);
if (c->num_buckets > 0)
{
diff --git a/gdb/defs.h b/gdb/defs.h
index b944ffb..396e9a8 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -417,6 +417,8 @@ char *ldirname (const char *filename);
char **gdb_buildargv (const char *);
+int compare_positive_ints (const void *ap, const void *bp);
+
/* From demangle.c */
extern void set_demangling_style (char *);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e880838..d1d5075 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -15402,6 +15402,10 @@ are:
@tab @code{qXfer:siginfo:write}
@tab @code{set $_siginfo}
+@item @code{threads}
+@tab @code{qXfer:threads:read}
+@tab @code{info threads}
+
@item @code{get-thread-local-@*storage-address}
@tab @code{qGetTLSAddr}
@tab Displaying @code{__thread} variables
@@ -21688,6 +21692,7 @@ follow development on @email{gdb@@sourceware.org} and
* GDB/MI Stream Records::
* GDB/MI Async Records::
* GDB/MI Frame Information::
+* GDB/MI Thread Information::
@end menu
@node GDB/MI Result Records
@@ -21780,7 +21785,7 @@ several times, either for different threads, because it cannot resume
all threads together, or even for a single thread, if the thread must
be stepped though some code before letting it run freely.
-@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
+@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
The target has stopped. The @var{reason} field can have one of the
following values:
@@ -21820,7 +21825,9 @@ If all threads are stopped, the @var{stopped} field will have the
value of @code{"all"}. Otherwise, the value of the @var{stopped}
field will be a list of thread identifiers. Presently, this list will
always include a single thread, but frontend should be prepared to see
-several threads in the list.
+several threads in the list. The @var{core} field reports on which
+processor core the stop event has happened. This field may be absent
+if such information is not available.
@item =thread-group-created,id="@var{id}"
@itemx =thread-group-exited,id="@var{id}"
@@ -21897,6 +21904,34 @@ corresponds to the frame's code address. This field may be absent.
@end table
+@node GDB/MI Thread Information
+@subsection @sc{gdb/mi} Thread Information
+
+Whenver @value{GDBN} has to report an information about a thread, it
+uses a tuple with the following fields:
+
+@table @code
+@item id
+The numeric id assigned to the thread by @value{GDBN}. This field is
+always present.
+
+@item target-id
+Target-specific string identifying the thread. This field is always present.
+
+@item details
+Additional information about the thread provided by the target.
+It is supposed to be human-readable and not interpreted by the
+frontend. This field is optional.
+
+@item state
+Either @samp{stopped} or @samp{running}, depending on whether the
+thread is presently running. This field is always present.
+
+@item cores
+The value of this field is an integer number of the processor core the
+thread was last seen on. This field is optional.
+@end table
+
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Simple Examples
@@ -26202,20 +26237,83 @@ while the target is running.
@subheading Synopsis
@smallexample
--list-thread-groups [ --available ] [ @var{group} ]
+-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
@end smallexample
-When used without the @var{group} parameter, lists top-level thread
-groups that are being debugged. When used with the @var{group}
-parameter, the children of the specified group are listed. The
-children can be either threads, or other groups. At present,
-@value{GDBN} will not report both threads and groups as children at
-the same time, but it may change in future.
+Lists thread groups (@pxref{Thread groups}). When a single thread
+group is passed as the argument, lists the children specified one.
+When several thread group are passed, lists information about those
+thread groups. Without any parameters, lists information about all
+top-level thread groups.
+
+Normally, thread groups that are being debugged are reported.
+With the @samp{--available} option, @value{GDBN} reports thread groups
+available on the target.
+
+The output of this command may have either a @samp{threads} result or
+a @samp{groups} result. The @samp{thread} result has a list of tuples
+as value, with each tuple describing a thread (@pxref{GDB/MI Thread
+Information}). The @samp{groups} result has a list of tuples as value,
+each tuple describing a thread group. If top-level groups are
+requested (that is, no parameter is passed), or when several groups
+are passed, the output always have a @samp{groups} result. The format
+of the @samp{group} result is described below.
+
+To reduce the number of roundtrips it's possible to list thread groups
+together with their children, by passing the @samp{--recurse} option
+and the recursion depth. Presently, only recursion depth of 1 is
+permitted. If this option is present, then every reported thread group
+will also include its children, either as @samp{group} or
+@samp{threads} field.
+
+In general, any combination of option and parameters is permitted, with
+the following caveats:
+
+@itemize @bullet
+@item
+When a single thread group is passed, the output will typically
+be the @samp{threads} result. Because threads may not contain
+anything, the @samp{recurse} option will be ignored.
+
+@item
+When the @samp{--available} option is passed, limited information may
+be available. In particular, the list of threads of a process might
+be inaccessible. Further, specifying specific thread groups might
+not give any performance advantage over listing all thread groups.
+The frontend should assume that @samp{-list-thread-groups --available}
+is always an expensive operation and cache the results.
+
+@end itemize
+
+The @samp{groups} result is a list of tuples, where each tuple may
+have the following fields:
+
+@table @code
+@item id
+Identifier of the thread group. This field is always present.
+
+@item type
+The type of the thread group. At present, only @samp{process} is a
+valid type.
+
+@item pid
+The target-specific process identifier. This field is only present
+for thread groups of type @samp{process}.
+
+@item num_children
+The number of children this thread group has. This field may be
+absent for an available thread group.
+
+@item threads
+This field has a list of tuples as value, each tuple describing a
+thread. It may be present
-With the @samp{--available} option, instead of reporting groups that
-are been debugged, GDB will report all thread groups available on the
-target. Using the @samp{--available} option together with @var{group}
-is not allowed.
+@item cores
+This field is a list of integers, each identifying a core that one
+thread of the group is running on. This field may be absent if
+such information is not available.
+
+@end table
@subheading Example
@@ -26229,6 +26327,16 @@ is not allowed.
@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
+-list-thread-groups --available
+^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
+-list-thread-groups --available --recurse 1
+ ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
+-list-thread-groups --available --recurse 1 17 18
+^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
@end smallexample
@subheading The @code{-interpreter-exec} Command
@@ -27978,6 +28086,7 @@ Show the current setting of the target wait timeout.
* File-I/O Remote Protocol Extension::
* Library List Format::
* Memory Map Format::
+* Thread List Format::
@end menu
@node Overview
@@ -28878,6 +28987,10 @@ If @var{n} is @samp{thread}, then @var{r} is the @var{thread-id} of
the stopped thread, as specified in @ref{thread-id syntax}.
@item
+If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
+the core on which the stop event was detected.
+
+@item
If @var{n} is a recognized @dfn{stop reason}, it describes a more
specific event that stopped the target. The currently defined stop
reasons are listed below. @var{aa} should be @samp{05}, the trap
@@ -28911,8 +29024,6 @@ logged execution events, because it has reached the end (or the
beginning when executing backward) of the log. The value of @var{r}
will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
for more information.
-
-
@end table
@item W @var{AA}
@@ -29452,6 +29563,12 @@ These are the currently defined stub features and their properties:
@tab @samp{-}
@tab Yes
+@item @samp{qXfer:threads:read}
+@tab No
+@tab @samp{-}
+@tab Yes
+
+
@item @samp{QNonStop}
@tab No
@tab @samp{-}
@@ -29535,6 +29652,10 @@ The remote stub understands the @samp{qXfer:siginfo:read} packet
The remote stub understands the @samp{qXfer:siginfo:write} packet
(@pxref{qXfer siginfo write}).
+@item qXfer:threads:read
+The remote stub understands the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}).
+
@item QNonStop
The remote stub understands the @samp{QNonStop} packet
(@pxref{QNonStop}).
@@ -29727,6 +29848,15 @@ This packet is not probed by default; the remote stub must request it,
by supplying an appropriate @samp{qSupported} response
(@pxref{qSupported}).
+@item qXfer:threads:read::@var{offset},@var{length}
+@anchor{qXfer threads read}
+Access the list of threads on target. @xref{Thread List Format}. The
+annex part of the generic @samp{qXfer} packet must be empty
+(@pxref{qXfer read}).
+
+This packet is not probed by default; the remote stub must request it,
+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}.
@@ -31710,6 +31840,32 @@ The formal DTD for memory map format is given below:
<!ATTLIST property name CDATA #REQUIRED>
@end smallexample
+@node Thread List Format
+@section Thread List Format
+@cindex thread list format
+
+To efficiently update the list of threads and their attributes,
+@value{GDBN} gdb requests, using the @samp{qXfer:threads:read}
+package (@pxref{qXfer threads read}) and XML document with the
+format described below.
+
+The top-level structure of the document is shown below:
+
+@smallexample
+<?xml version="1.0"?>
+<threads>
+ <thread id="id" core="0">
+ some details
+ </thread>
+</memory-map>
+@end smallexample
+
+Each @samp{thread} element must have the @samp{id} attribute that
+identifies the thread (@pxref{thread-id syntax}). The
+@samp{core} attribute, if present, specifies which process core
+the thread was last executing on. The content of the of @samp{thread}
+element is interpreted as hunam-readable auxilliary information.
+
@include agentexpr.texi
@node Target Descriptions
@@ -32269,15 +32425,34 @@ An example document is:
<column name="pid">1</column>
<column name="user">root</column>
<column name="command">/sbin/init</column>
+ <column name="cores">1,2,3</column>
+ <threads>
+ <item>
+ <column name="tid">12</column>
+ <column name="core">3</column>
+ </item>
+ </threads>
</item>
</osdata>
@end smallexample
-Each item should include a column whose name is @samp{pid}. The value
+The document has an @samp{osdata} element that contains a number of
+@samp{item} elements describing processes. Each process @samp{item}
+may contain @samp{threads} element with items describing threads of
+that process. Both process and thread items contain a number of
+@samp{column} elements with various information.
+
+Each process 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.
+displayed by @value{GDBN}. The @samp{cores} column, if present,
+should contain a comma-separated list of cores that this process
+is running on.
+
+Each thread item should include a column whose name is @samp{tid}.
+The value of that is a thread identifier, specific to the operating
+system. The @samp{core} column, if present, is the integer number of
+the core the thread is running on.
@include gpl.texi
diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd
index a04506b..67de837 100644
--- a/gdb/features/osdata.dtd
+++ b/gdb/features/osdata.dtd
@@ -10,7 +10,10 @@
<!ATTLIST osdata version CDATA #FIXED "1.0">
<!ATTLIST osdata type CDATA #REQUIRED>
-<!ELEMENT item (column*)>
+<!ELEMENT item (column*, threads?)>
<!ELEMENT column (#PCDATA)>
<!ATTLIST column name CDATA #REQUIRED>
+
+<!ELEMENT threads (item*)>
+
diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
new file mode 100644
index 0000000..e7c3a11
--- /dev/null
+++ b/gdb/features/threads.dtd
@@ -0,0 +1,14 @@
+<!-- Copyright (C) 2008, 2009 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. -->
+
+<!ELEMENT threads (thread*)>
+<!ATTLIST threads version CDATA #FIXED "1.0">
+
+<!ELEMENT thread (#PCDATA)>
+
+<!ATTLIST thread id CDATA #REQUIRED>
+<!ATTLIST thread core CDATA #IMPLIED>
+
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 45dd9f7..d1d853d 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -137,6 +137,7 @@ static void *add_lwp (ptid_t ptid);
static int my_waitpid (int pid, int *status, int flags);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int linux_core_of_thread (ptid_t ptid);
struct pending_signals
{
@@ -2789,6 +2790,111 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
#endif
static int
+compare_ints (const void *xa, const void *xb)
+{
+ int a = *(const int *)xa;
+ int b = *(const int *)xb;
+
+ return a - b;
+}
+
+/* Given PID, print information about every thread in that process to BUFFER.
+ Set *CORES to a string with a comma-separated list of cores that threads
+ of PID execute on. The string must be freed by the caller. BUFFER must
+ be initialized. If no cores are found, *CORES will be set to NULL. */
+static void
+list_threads (int pid, struct buffer *buffer, char **cores)
+{
+ int count = 0;
+ int allocated = 10;
+ int *core_numbers = xmalloc (sizeof (int) * allocated);
+ char pathname[128];
+ DIR *dir;
+ struct dirent *dp;
+ struct stat statbuf;
+
+ sprintf (pathname, "/proc/%d/task", pid);
+ if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+ {
+ dir = opendir (pathname);
+ if (!dir)
+ return;
+
+ buffer_xml_printf (buffer, "<threads>");
+ while ((dp = readdir (dir)) != NULL)
+ {
+ unsigned long lwp = strtoul (dp->d_name, NULL, 10);
+
+ if (lwp != 0)
+ {
+ unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
+
+ if (core != -1)
+ {
+ char s[11];
+ sprintf (s, "%u", core);
+
+ if (count == allocated)
+ core_numbers = realloc (core_numbers,
+ sizeof (int ) * (allocated *= 2));
+ core_numbers[count++] = core;
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"tid\">%s</column>"
+ "<column name=\"core\">%s</column>"
+ "</item>", dp->d_name, s);
+ }
+ else
+ {
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"tid\">%s</column>"
+ "</item>", dp->d_name);
+ }
+ }
+ }
+ buffer_xml_printf (buffer, "</threads>");
+ }
+
+ *cores = NULL;
+ if (count > 0)
+ {
+ struct buffer buffer2;
+ int *b;
+ int *e;
+ qsort (core_numbers, count, sizeof (int), compare_ints);
+
+ /* Remove duplicates. */
+ b = core_numbers;
+ e = core_numbers + count;
+
+ while (b != e) {
+ if ((b + 1 < e) && (*b == *(b+1)))
+ {
+ *(b + 1) = *(e - 1);
+ --e;
+ }
+ else
+ {
+ ++b;
+ }
+ }
+
+ buffer_init (&buffer2);
+
+ for (b = core_numbers; b != e; ++b) {
+ char number[11];
+ sprintf (number, "%u", *b);
+ buffer_xml_printf (&buffer2, "%s%s",
+ (b == core_numbers) ? "" : ",", number);
+ }
+ buffer_grow_str0 (&buffer2, "");
+
+ *cores = buffer_finish (&buffer2);
+ }
+}
+
+static int
linux_qxfer_osdata (const char *annex,
unsigned char *readbuf, unsigned const char *writebuf,
CORE_ADDR offset, int len)
@@ -2837,15 +2943,20 @@ linux_qxfer_osdata (const char *annex,
FILE *f;
char cmd[MAXPATHLEN + 1];
struct passwd *entry;
+ int pid;
sprintf (pathname, "/proc/%s/cmdline", dp->d_name);
entry = getpwuid (statbuf.st_uid);
+ pid = (int)strtoul (dp->d_name, NULL, 10);
+
if ((f = fopen (pathname, "r")) != NULL)
{
size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
if (len > 0)
{
+ struct buffer thread_buffer;
+ char *cores = 0;
int i;
for (i = 0; i < len; i++)
if (cmd[i] == '\0')
@@ -2857,11 +2968,29 @@ linux_qxfer_osdata (const char *annex,
"<item>"
"<column name=\"pid\">%s</column>"
"<column name=\"user\">%s</column>"
- "<column name=\"command\">%s</column>"
- "</item>",
+ "<column name=\"command\">%s</column>",
dp->d_name,
entry ? entry->pw_name : "?",
cmd);
+
+ buffer_init (&thread_buffer);
+ list_threads (pid, &thread_buffer, &cores);
+
+ if (cores)
+ {
+ buffer_xml_printf (
+ &buffer,
+ "<column name=\"cores\">%s</column>", cores);
+ free (cores);
+ }
+
+ buffer_grow (&buffer,
+ thread_buffer.buffer,
+ thread_buffer.used_size);
+ free (buffer_finish (&thread_buffer));
+
+
+ buffer_xml_printf (&buffer, "</item>");
}
fclose (f);
}
@@ -3139,6 +3268,53 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
return ret;
}
+static int
+linux_core_of_thread (ptid_t ptid)
+{
+ char filename[sizeof ("/proc//task//stat")
+ + 2*20 /* decimal digits for 2 numbers, max 2^64 bit each */
+ + 1];
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ sprintf (filename, "/proc/%d/task/%ld/stat",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid));
+ f = fopen (filename, "r");
+
+ for (;;)
+ {
+ int n;
+ content = realloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ free (content);
+ fclose (f);
+
+ return core;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -3178,10 +3354,11 @@ static struct target_ops linux_target_ops = {
linux_start_non_stop,
linux_supports_multi_process,
#ifdef USE_THREAD_DB
- thread_db_handle_monitor_command
+ thread_db_handle_monitor_command,
#else
- NULL
+ NULL,
#endif
+ linux_core_of_thread
};
static void
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 158b653..97c076d 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
gdbserver to know what inferior_ptid is. */
if (1 || !ptid_equal (general_thread, ptid))
{
+ int core = -1;
/* In non-stop, don't change the general thread behind
GDB's back. */
if (!non_stop)
@@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
buf = write_ptid (buf, ptid);
strcat (buf, ";");
buf += strlen (buf);
+
+ if (the_target->core_for_thread)
+ core = (*the_target->core_for_thread) (ptid);
+ if (core != -1)
+ {
+ sprintf (buf, "core:");
+ buf += strlen (buf);
+ sprintf (buf, "%x", core);
+ strcat (buf, ";");
+ buf += strlen (buf);
+ }
}
}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 14bc7e7..62e3e3d 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
}
}
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+ struct inferior_list_entry *thread;
+
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for (thread = all_threads.head; thread; thread = thread->next)
+ {
+ ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+ char ptid_s[100];
+ int core = -1;
+ char core_s[21];
+
+ write_ptid (ptid_s, ptid);
+
+ if (the_target->core_for_thread)
+ core = (*the_target->core_for_thread) (ptid);
+
+ if (core != -1)
+ {
+ sprintf (core_s, "%d", core);
+ buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+ ptid_s, core_s);
+ }
+ else
+ {
+ buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+ ptid_s);
+ }
+ }
+
+ buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+ unsigned char *readbuf,
+ CORE_ADDR offset, int length)
+{
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (annex && strcmp (annex, "") != 0)
+ return 0;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+ /* When asked for data at offset 0, generate everything and store into
+ 'result'. Successive reads will be served off 'result'. */
+ if (result)
+ free (result);
+
+ buffer_init (&buffer);
+
+ handle_threads_qxfer_proper (&buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (length > result_length - offset)
+ length = result_length - offset;
+
+ memcpy (readbuf, result + offset, length);
+
+ return length;
+
+}
+
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}
+ if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+ {
+ unsigned char *data;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+ char *annex;
+
+ require_running (own_buf);
+
+ /* Reject any annex; grab the offset and length. */
+ if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+ || annex[0] != '\0')
+ {
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ /* Read one extra byte, as an indicator of whether there is
+ more. */
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ data = malloc (len + 1);
+ if (!data)
+ return;
+ n = handle_threads_qxfer (annex, data, ofs, len + 1);
+ if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
+ strcat (own_buf, ";qXfer:threads:read+");
+
return;
}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 0e4b127..8e8778a 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -283,6 +283,9 @@ struct target_ops
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
+
+ /* Returns the core given a thread, or -1 if not known. */
+ int (*core_for_thread) (ptid_t);
};
extern struct target_ops *the_target;
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 2f388fb..bd0044b 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -346,4 +346,6 @@ extern struct cleanup *make_cleanup_restore_current_thread (void);
INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
extern struct thread_info* inferior_thread (void);
+extern void update_thread_list (void);
+
#endif /* GDBTHREAD_H */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index c0afecd..b232caf 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -5421,6 +5421,59 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
return inf->aspace;
}
+/* Return the core for a thread. */
+static int
+linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct cleanup *back_to;
+ char *filename;
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ filename = xstrprintf ("/proc/%d/task/%ld/stat",
+ GET_PID (ptid), GET_LWP (ptid));
+ back_to = make_cleanup (xfree, filename);
+
+ f = fopen (filename, "r");
+ make_cleanup_fclose (f);
+
+ for (;;)
+ {
+ int n;
+ content = xrealloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ make_cleanup (xfree, content);
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ /* If the first field after program name has index 0, then core number is
+ the field with index 36. There's no constant for that anywhere. */
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ do_cleanups (back_to);
+
+ return core;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -5461,6 +5514,8 @@ linux_nat_add_target (struct target_ops *t)
t->to_supports_multi_process = linux_nat_supports_multi_process;
+ t->to_core_of_thread = linux_nat_core_of_thread;
+
/* We don't change the stratum; this target will sit at
process_stratum and thread_db will set at thread_stratum. This
is a little strange, since this is a multi-threaded-capable
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 248cd66..af51bde 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
if (print_frame)
{
+ int core;
if (uiout != mi_uiout)
{
/* The normal_stop function has printed frame information into
@@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
}
else
ui_out_field_string (mi_uiout, "stopped-threads", "all");
+
+ core = target_core_of_thread (inferior_ptid);
+ if (core != -1)
+ ui_out_field_int (mi_uiout, "core", core);
}
fputs_unfiltered ("*stopped", raw_stdout);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 2332752..b152d98 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -359,11 +359,44 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
print_thread_info (uiout, thread, -1);
}
+struct collect_cores_data
+{
+ int pid;
+
+ VEC (int) *cores;
+};
+
+static int collect_cores (struct thread_info *ti, void *xdata)
+{
+ struct collect_cores_data *data = xdata;
+
+ if (ptid_get_pid (ti->ptid) == data->pid)
+ {
+ int core = target_core_of_thread (ti->ptid);
+ if (core != -1)
+ VEC_safe_push (int, data->cores, core);
+ }
+
+ return 0;
+}
+
+struct print_one_inferior_data
+{
+ int recurse;
+ VEC (int) *inferiors;
+};
+
static int
-print_one_inferior (struct inferior *inferior, void *arg)
+print_one_inferior (struct inferior *inferior, void *xdata)
{
- if (inferior->pid != 0)
+ struct print_one_inferior_data *top_data = xdata;
+
+ if (VEC_length (int, top_data->inferiors) == 0
+ || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
+ VEC_length (int, top_data->inferiors), sizeof (int),
+ compare_positive_ints))
{
+ struct collect_cores_data data;
struct cleanup *back_to
= make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -371,36 +404,128 @@ print_one_inferior (struct inferior *inferior, void *arg)
ui_out_field_string (uiout, "type", "process");
ui_out_field_int (uiout, "pid", inferior->pid);
+ data.pid = inferior->pid;
+ data.cores = 0;
+ iterate_over_threads (collect_cores, &data);
+
+ if (VEC_length (int, data.cores))
+ {
+ int elt;
+ int i;
+ int *b, *e;
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_list_begin_end (uiout, "cores");
+
+ qsort (VEC_address (int, data.cores),
+ VEC_length (int, data.cores), sizeof (int),
+ compare_positive_ints);
+
+ b = VEC_address (int, data.cores);
+ e = b + VEC_length (int, data.cores);
+
+ /* Remove duplicates. */
+ while (b != e) {
+ if ((b + 1 < e) && (*b == *(b+1)))
+ {
+ *(b + 1) = *(e - 1);
+ --e;
+ }
+ else
+ {
+ ++b;
+ }
+ }
+
+ for (i = 0; VEC_iterate (int, data.cores, i, elt); ++i)
+ ui_out_field_int (uiout, NULL, elt);
+
+ do_cleanups (back_to_2);
+ }
+
+ if (top_data->recurse)
+ print_thread_info (uiout, -1, inferior->pid);
+
do_cleanups (back_to);
}
return 0;
}
+/* Output a field named 'cores' with a list as the value. The elements of
+ the list are obtained by splitting 'cores' on comma. */
+static void
+output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
+{
+ struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+ field_name);
+ char *cores = xstrdup (xcores);
+ char *p = cores;
+ char *ts = 0;
+
+ make_cleanup (xfree, cores);
+
+ for (p = strtok_r (p, ",", &ts); p; p = strtok_r (NULL, ",", &ts))
+ ui_out_field_string (uiout, NULL, p);
+
+ do_cleanups (back_to);
+}
+
void
mi_cmd_list_thread_groups (char *command, char **argv, int argc)
{
struct cleanup *back_to;
int available = 0;
- char *id = NULL;
+ int recurse = 0;
+ VEC (int) *ids = 0;
- if (argc > 0 && strcmp (argv[0], "--available") == 0)
+ enum opt
{
- ++argv;
- --argc;
- available = 1;
- }
+ AVAILABLE_OPT, RECURSE_OPT
+ };
+ static struct mi_opt opts[] =
+ {
+ {"-available", AVAILABLE_OPT, 0},
+ {"-recurse", RECURSE_OPT, 1},
+ { 0, 0, 0 }
+ };
- if (argc > 0)
- id = argv[0];
+ int optind = 0;
+ char *optarg;
- back_to = make_cleanup (null_cleanup, NULL);
+ while (1)
+ {
+ int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+ &optind, &optarg);
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case AVAILABLE_OPT:
+ available = 1;
+ break;
+ case RECURSE_OPT:
+ if (strcmp (optarg, "1") != 0)
+ error ("only '1' is permitted value for the '--recurse' option");
+ recurse = 1;
+ break;
+ }
+ }
- if (available && id)
+ for (; optind < argc; ++optind)
{
- error (_("Can only report top-level available thread groups"));
+ char *end;
+ int inf = strtoul (argv[optind], &end, 0);
+ if (*end != '\0')
+ error ("invalid group id '%s'", argv[optind]);
+ VEC_safe_push (int, ids, inf);
}
- else if (available)
+ qsort (VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints);
+
+ back_to = make_cleanup ((void (*)(void *))VEC_OP (int, free), &ids);
+
+ if (available)
{
struct osdata *data;
struct osdata_item *item;
@@ -416,12 +541,25 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
ix_items, item);
ix_items++)
{
- struct cleanup *back_to =
- make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+ struct cleanup *back_to;
const char *pid = get_osdata_column (item, "pid");
const char *cmd = get_osdata_column (item, "command");
const char *user = get_osdata_column (item, "user");
+ const char *cores = get_osdata_column (item, "cores");
+
+ int pid_i = strtoul (pid, NULL, 0);
+
+ /* At present, the target will return all available processes
+ and if information about specific ones was required, we filter
+ undesired processes here. */
+ if (ids && bsearch (&pid_i, VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints) == NULL)
+ continue;
+
+
+ back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
ui_out_field_fmt (uiout, "id", "%s", pid);
ui_out_field_string (uiout, "type", "process");
@@ -429,21 +567,57 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
ui_out_field_string (uiout, "description", cmd);
if (user)
ui_out_field_string (uiout, "user", user);
+ if (cores)
+ output_cores (uiout, "cores", cores);
+
+ if (item->children && recurse)
+ {
+ struct osdata_item *child;
+ int ix_child;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+ for (ix_child = 0; VEC_iterate (osdata_item_s,
+ item->children->items,
+ ix_child, child); ++ix_child)
+ {
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *tid = get_osdata_column (child, "tid");
+ const char *tcore = get_osdata_column (child, "core");
+ ui_out_field_string (uiout, "id", tid);
+ if (tcore)
+ ui_out_field_string (uiout, "core", tcore);
+
+ do_cleanups (back_to_2);
+ }
+ }
do_cleanups (back_to);
}
}
- else if (id)
+ else if (VEC_length (int, ids) == 1)
{
- int pid = atoi (id);
+ /* Local thread groups, single id. */
+ int pid = *VEC_address (int, ids);
if (!in_inferior_list (pid))
- error ("Invalid thread group id '%s'", id);
+ error ("Invalid thread group id '%d'", pid);
print_thread_info (uiout, -1, pid);
}
else
{
+ struct print_one_inferior_data data;
+ data.recurse = recurse;
+ data.inferiors = ids;
+
+ /* Local thread groups. Either no explicit ids -- and we
+ print everything, or several explicit ids. In both cases,
+ we print more than one group, and have to use 'groups'
+ as the top-level element. */
make_cleanup_ui_out_list_begin_end (uiout, "groups");
- iterate_over_inferiors (print_one_inferior, NULL);
+ update_thread_list ();
+ iterate_over_inferiors (print_one_inferior, &data);
}
do_cleanups (back_to);
diff --git a/gdb/osdata.c b/gdb/osdata.c
index 3d2c23c..394fc4d 100644
--- a/gdb/osdata.c
+++ b/gdb/osdata.c
@@ -50,8 +50,10 @@ osdata_parse (const char *xml)
/* Internal parsing data passed to all XML callbacks. */
struct osdata_parsing_data
{
+ struct osdata *top_osdata;
struct osdata *osdata;
char *property_name;
+ struct osdata_item *current_item;
};
/* Handle the start of a <osdata> element. */
@@ -71,9 +73,37 @@ osdata_start_osdata (struct gdb_xml_parser *parser,
type = VEC_index (gdb_xml_value_s, attributes, 0)->value;
osdata = XZALLOC (struct osdata);
osdata->type = xstrdup (type);
+ data->top_osdata = data->osdata = osdata;
+}
+
+/* Handle the start of a <threads> element. */
+
+static void
+osdata_start_threads (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;
+
+ osdata = XZALLOC (struct osdata);
+ osdata->type = xstrdup ("threads");
data->osdata = osdata;
}
+static void
+osdata_end_threads (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;
+
+ data->current_item->children = data->osdata;
+ data->osdata = data->top_osdata;
+}
+
+
/* Handle the start of a <item> element. */
static void
@@ -84,6 +114,8 @@ osdata_start_item (struct gdb_xml_parser *parser,
struct osdata_parsing_data *data = user_data;
struct osdata_item item = { NULL };
VEC_safe_push (osdata_item_s, data->osdata->items, &item);
+ if (data->osdata == data->top_osdata)
+ data->current_item = VEC_last (osdata_item_s, data->osdata->items);
}
/* Handle the start of a <column> element. */
@@ -137,10 +169,15 @@ const struct gdb_xml_attribute column_attributes[] = {
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
};
+extern const struct gdb_xml_element osdata_children[];
+
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 },
+ { "threads", NULL, osdata_children,
+ GDB_XML_EF_OPTIONAL, osdata_start_threads,
+ osdata_end_threads },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
diff --git a/gdb/osdata.h b/gdb/osdata.h
index 4ada454..485789e 100644
--- a/gdb/osdata.h
+++ b/gdb/osdata.h
@@ -22,6 +22,9 @@
#include "vec.h"
+typedef struct osdata *osdata_p;
+DEF_VEC_P(osdata_p);
+
typedef struct osdata_column
{
char *name;
@@ -32,6 +35,13 @@ DEF_VEC_O(osdata_column_s);
typedef struct osdata_item
{
VEC(osdata_column_s) *columns;
+
+ /* Nested items. Presently, a given item may only contain
+ children of specific kind, and children->type gives
+ the kind of children. In fact, only 'threads' may be
+ the kind of children right now. */
+ osdata_p children;
+
} osdata_item_s;
DEF_VEC_O(osdata_item_s);
@@ -41,8 +51,7 @@ struct osdata
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 *);
diff --git a/gdb/remote.c b/gdb/remote.c
index 9fa92fb..eb47713 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -60,6 +60,7 @@
#include "remote-fileio.h"
#include "gdb/fileio.h"
#include "gdb_stat.h"
+#include "xml-support.h"
#include "memory-map.h"
@@ -303,6 +304,13 @@ struct remote_state
int ctrlc_pending_p;
};
+/* Private data that we'll store in (struct thread_info)->private. */
+struct private_thread_info
+{
+ char *extra;
+ int core;
+};
+
/* Returns true if the multi-process extensions are in effect. */
static int
remote_multi_process_p (struct remote_state *rs)
@@ -1054,6 +1062,7 @@ enum {
PACKET_qXfer_spu_read,
PACKET_qXfer_spu_write,
PACKET_qXfer_osdata,
+ PACKET_qXfer_threads,
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
@@ -2303,6 +2312,76 @@ remote_find_new_threads (void)
CRAZY_MAX_THREADS);
}
+typedef struct thread_item
+{
+ ptid_t ptid;
+ char *extra;
+ int core;
+} thread_item_t;
+DEF_VEC_O(thread_item_t);
+
+struct threads_parsing_context
+{
+ VEC (thread_item_t) *items;
+};
+
+static void
+start_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct threads_parsing_context *data = user_data;
+
+ struct thread_item item;
+ char *id;
+
+ id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ item.ptid = read_ptid (id, NULL);
+
+ if (VEC_length (gdb_xml_value_s, attributes) > 1)
+ item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
+ else
+ item.core = -1;
+
+ item.extra = 0;
+
+ VEC_safe_push (thread_item_t, data->items, &item);
+}
+
+static void
+end_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, const char *body_text)
+{
+ struct threads_parsing_context *data = user_data;
+
+ if (body_text && *body_text)
+ VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
+}
+
+const struct gdb_xml_attribute thread_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element thread_children[] = {
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_children[] = {
+ { "thread", thread_attributes, thread_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ start_thread, end_thread },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_elements[] = {
+ { "threads", NULL, threads_children,
+ GDB_XML_EF_NONE, NULL, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
/*
* Find all threads for info threads command.
* Uses new thread protocol contributed by Cisco.
@@ -2320,6 +2399,66 @@ remote_threads_info (struct target_ops *ops)
if (remote_desc == 0) /* paranoia */
error (_("Command can only be used when connected to the remote target."));
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ char *xml = target_read_stralloc (¤t_target,
+ TARGET_OBJECT_THREADS, NULL);
+
+ struct cleanup *back_to = make_cleanup (xfree, xml);
+ if (xml && *xml)
+ {
+ struct gdb_xml_parser *parser;
+ struct threads_parsing_context context;
+ struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+ context.items = 0;
+ parser = gdb_xml_create_parser_and_cleanup (_("threads"),
+ threads_elements,
+ &context);
+
+ gdb_xml_use_dtd (parser, "threads.dtd");
+
+ if (gdb_xml_parse (parser, xml) == 0)
+ {
+ int i;
+ struct thread_item *item;
+
+ for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
+ {
+ if (!ptid_equal (item->ptid, null_ptid))
+ {
+ struct thread_info *info;
+ /* In non-stop mode, we assume new found threads
+ are running until proven otherwise with a
+ stop reply. In all-stop, we can only get
+ here if all threads are stopped. */
+ int running = non_stop ? 1 : 0;
+
+ remote_notice_new_inferior (item->ptid, running);
+
+ info = find_thread_ptid (item->ptid);
+ if (info)
+ {
+ if (!info->private)
+ info->private = (struct private_thread_info *)
+ xmalloc (sizeof (struct private_thread_info));
+
+ info->private->extra = item->extra;
+ item->extra = 0;
+ info->private->core = item->core;
+ }
+ }
+ xfree (item->extra);
+ }
+ }
+
+ VEC_free (thread_item_t, context.items);
+ }
+
+ do_cleanups (back_to);
+ return;
+ }
+
if (use_threadinfo_query)
{
putpkt ("qfThreadInfo");
@@ -2392,6 +2531,15 @@ remote_threads_extra_info (struct thread_info *tp)
server doesn't know about it. */
return NULL;
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ struct thread_info *info = find_thread_ptid (tp->ptid);
+ if (info && info->private)
+ return info->private->extra;
+ else
+ return NULL;
+ }
+
if (use_threadextra_query)
{
char *b = rs->buf;
@@ -3152,6 +3300,8 @@ static struct protocol_feature remote_protocol_features[] = {
PACKET_qXfer_spu_write },
{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
PACKET_qXfer_osdata },
+ { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
+ PACKET_qXfer_threads },
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
PACKET_QPassSignals },
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -4262,6 +4412,8 @@ struct stop_reply
int solibs_changed;
int replay_event;
+
+ int core;
};
/* The list of already fetched and acknowledged stop events. */
@@ -4425,6 +4577,7 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
event->replay_event = 0;
event->stopped_by_watchpoint_p = 0;
event->regcache = NULL;
+ event->core = -1;
switch (buf[0])
{
@@ -4451,7 +4604,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
/* If this packet is an awatch packet, don't parse the 'a'
as a register number. */
- if (strncmp (p, "awatch", strlen("awatch")) != 0)
+ if (strncmp (p, "awatch", strlen("awatch")) != 0
+ && strncmp (p, "core", strlen ("core") != 0))
{
/* Read the ``P'' register number. */
pnum = strtol (p, &p_temp, 16);
@@ -4497,6 +4651,12 @@ Packet: '%s'\n"),
if (p_temp)
p = p_temp;
}
+ else if (strncmp (p, "core", p1 - p) == 0)
+ {
+ ULONGEST c;
+ p = unpack_varlen_hex (++p1, &c);
+ event->core = c;
+ }
else
{
/* Silently skip unknown optional info. */
@@ -4706,6 +4866,7 @@ process_stop_reply (struct stop_reply *stop_reply,
struct target_waitstatus *status)
{
ptid_t ptid;
+ struct thread_info *info;
*status = stop_reply->ws;
ptid = stop_reply->ptid;
@@ -4736,6 +4897,12 @@ process_stop_reply (struct stop_reply *stop_reply,
remote_stopped_by_watchpoint_p = stop_reply->stopped_by_watchpoint_p;
remote_watch_data_address = stop_reply->watch_data_address;
+ /* Update the core associated with a thread when we process stop
+ event in that thread. */
+ info = find_thread_ptid (ptid);
+ if (info && info->private)
+ info->private->core = stop_reply->core;
+
remote_notice_new_inferior (ptid, 0);
}
@@ -7579,6 +7746,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
(ops, "osdata", annex, readbuf, offset, len,
&remote_protocol_packets[PACKET_qXfer_osdata]);
+ case TARGET_OBJECT_THREADS:
+ gdb_assert (annex == NULL);
+ return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
+ &remote_protocol_packets[PACKET_qXfer_threads]);
+
default:
return -1;
}
@@ -8895,6 +9067,15 @@ remote_supports_cond_tracepoints (void)
return rs->cond_tracepoints;
}
+static int
+remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+ if (info && info->private)
+ return info->private->core;
+ return -1;
+}
+
static void
init_remote_ops (void)
{
@@ -8958,6 +9139,7 @@ Specify the serial device it is connected to\n\
remote_ops.to_terminal_ours = remote_terminal_ours;
remote_ops.to_supports_non_stop = remote_supports_non_stop;
remote_ops.to_supports_multi_process = remote_supports_multi_process;
+ remote_ops.to_core_of_thread = remote_core_of_thread;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -9317,6 +9499,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
"qXfer:osdata:read", "osdata", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
+ "qXfer:threads:read", "threads", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
"qXfer:siginfo:read", "read-siginfo-object", 0);
diff --git a/gdb/target.c b/gdb/target.c
index cd1614b..4fd82b9 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3024,6 +3024,26 @@ target_store_registers (struct regcache *regcache, int regno)
noprocess ();
}
+int
+target_core_of_thread (ptid_t ptid)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_core_of_thread != NULL)
+ {
+ int retval = t->to_core_of_thread (t, ptid);
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
+ PIDGET (ptid), retval);
+ return retval;
+ }
+ }
+
+ return -1;
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
diff --git a/gdb/target.h b/gdb/target.h
index ebe6056..7435433 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -255,7 +255,9 @@ enum target_object
/* Extra signal info. Usually the contents of `siginfo_t' on unix
platforms. */
TARGET_OBJECT_SIGNAL_INFO,
- /* Possible future objects: TARGET_OBJECT_FILE, ... */
+ /* The list of threads that are being debugged. */
+ TARGET_OBJECT_THREADS,
+ /* Possible future objects: TARGET_OBJECT_FILE, ... */
};
/* Request that OPS transfer up to LEN 8-bit bytes of the target's
@@ -597,6 +599,14 @@ struct target_ops
struct address_space *(*to_thread_address_space) (struct target_ops *,
ptid_t);
+ /* Return the core that thread PTID is on, or -1 if such information
+ is not available. For a stopped thread, this is supposed to return
+ the core the thread was last running on. For running threads, it
+ should return one of the cores that the thread was running between
+ the call to this function and return -- and if it was running on
+ several cores, any other may be returned. */
+ int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
+
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@@ -1246,6 +1256,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
(*current_target.to_log_command) (p); \
while (0)
+
+extern int target_core_of_thread (ptid_t ptid);
+
/* Routines for maintenance of the target structures...
add_target: Add a target to the list of all possible targets.
diff --git a/gdb/thread.c b/gdb/thread.c
index 689e9d5..fc2ee2a 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -468,8 +468,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
struct cleanup *cleanup_chain;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
@@ -748,8 +747,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
char *extra_info;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
current_ptid = inferior_ptid;
/* We'll be switching threads temporarily. */
@@ -759,6 +757,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
for (tp = thread_list; tp; tp = tp->next)
{
struct cleanup *chain2;
+ int core;
if (requested_thread != -1 && tp->num != requested_thread)
continue;
@@ -817,6 +816,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
ui_out_field_string (uiout, "state", state);
}
+ core = target_core_of_thread (tp->ptid);
+ if (ui_out_is_mi_like_p (uiout) && core != -1)
+ ui_out_field_int (uiout, "core", core);
+
do_cleanups (chain2);
}
@@ -1058,8 +1061,7 @@ thread_apply_all_command (char *cmd, int from_tty)
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
old_chain = make_cleanup_restore_current_thread ();
@@ -1245,6 +1247,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
return GDB_RC_OK;
}
+void
+update_thread_list (void)
+{
+ prune_threads ();
+ target_find_new_threads ();
+}
+
/* Commands with a prefix of `thread'. */
struct cmd_list_element *thread_cmd_list = NULL;
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index 19a4644..6ecdd1f 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -44,7 +44,7 @@ struct ui_out_hdr
is always available. Stack/nested level 0 is reserved for the
top-level result. */
-enum { MAX_UI_OUT_LEVELS = 6 };
+enum { MAX_UI_OUT_LEVELS = 8 };
struct ui_out_level
{
diff --git a/gdb/utils.c b/gdb/utils.c
index 16ad084..c9aa612 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
return argv;
}
+int
+compare_positive_ints (const void *ap, const void *bp)
+{
+ /* Because we know we're comparing two ints which are positive,
+ there's no danger of overflow here. */
+ return * (int *) ap - * (int *) bp;
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_utils;
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2009-12-16 20:53 [MI] core awareness Vladimir Prus
@ 2009-12-17 15:31 ` Pedro Alves
2009-12-18 11:41 ` Eli Zaretskii
1 sibling, 0 replies; 17+ messages in thread
From: Pedro Alves @ 2009-12-17 15:31 UTC (permalink / raw)
To: gdb-patches; +Cc: Vladimir Prus
On Wednesday 16 December 2009 20:52:38, Vladimir Prus wrote:
> The attached patch implements the core awareness, as discussed at:
>
> http://thread.gmane.org/gmane.comp.gdb.devel/27468/
>
> The most current spec can be found at:
>
> http://article.gmane.org/gmane.comp.gdb.devel/27548
>
> To summarize, with this patch, when debugging against gdbserver:
>
> * The -list-thread-groups command will report the cores that each
> process runs on (with or without --available)
> * It's possible to pass several thread groups to -list-thread-groups
> * The *stopped output includes core number
>
> For native debugging on Linux, roughly the same is also possible.
>
> This is mostly MI change, but includes some remote protocol tweaks,
> and documentation. I'd appreciate review of those parts.
More like 80% not-MI changes. :-)
>
> - Volodya
> core-awareness.diff
> commit 533abb7a66898cde158ea5556604ea20d56f2a47
> Author: Vladimir Prus <vladimir@codesourcery.com>
> Date: Wed Dec 16 23:39:19 2009 +0300
>
> Implement core awareness.
>
> gdb/
> * bcache.c (compare_ints): Remove
> (print_percentage): Use compare_positive_ints.
Fishy indentation.
>
> * defs.h (compare_positive_ints): Declare.
> * linux-nat.c (linux_nat_core_of_thread): New.
> (linux_nat_add_target): Register the above.
> * osdata.c (osdata_parsing_data): New fields
> top_osdata and current_item.
> (osdata_start_threads, osdata_end_threads): New.
> (osdata_start_osdata): Init top_osdata.
> (osdata_start_item): Record current item.
> (osdata_children): New.
> (item_children): Permit 'threads' child.
> * osdata.h (osdata_item): New field 'children'.
> * remote.c (struct private_thread_info): New.
> (PACKET_qXfer_threads, use_osdata_threads): New.
> (struct thread_item, threads_parsing_context
> (start_thread, end_thread, thread_attributes)
> (thread_children, threads_children, threads_elements): New.
> (remote_threads_info): Try qXfer:threads before anything
> else.
> (remote_protocol_packets): Register qXfer:threads.
> (remote_open_1): Init use_osdata_threads.
> (struct stop_reply): New field 'core'.
> (remote_parse_stop_reply): Parse core number.
> (process_stop_reply): Record core number.
> (remote_xfer_partial): Handle qXfer:threads.
> (remote_core_of_thread): New.
> (init_remote_ops): Register remote_core_of_thread.
> (_initialize_remote): Register qXfer:read.
> * target.c (target_core_of_thread): New
> * target.h (enum target_object): New value TARGET_OBJECT_THREADS.
> (struct target_ops): New field to_core_of_threads.
> (target_core_of_thread): Declare.
> * thread.c (print_thread_info): Report the core.
> * ui-out.c (MAX_UI_OUT_LEVELS): Increase.
> * utils.c (compare_positive_ints): New.
> * features/osdata.dtd: Allow nested threads.
> * features/threads.dtd: New.
> * mi/mi-interp.c (mi_on_normal_stop): Report the core.
> * mi/mi-main.c (struct collect_cores_data, collect_cores)
> (print_one_inferior_data): New.
> (print_one_inferior_data): Implementing printing of selected
> inferiors. Collect and print cores.
> (output_cores): New.
> (mi_cmd_list_thread_groups): Support --recurse. Permit specifying
> thread groups together with --available.
>
> gdbserver/
> * linux-low.c (linux_core_of_thread): New.
> (compare_ints, list_threads): New.
> (linux_qxfer_osdata): Report threads and cores.
> (linux_target_op): Register linux_core_of_thread.
> * remote-utils.c (prepare_resume_reply): Report the core.
> * server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
> New.
> (handle_query): Handle qXfer:threads. Announce availability
> thereof.
> * target.h (struct target_ops): New field core_for_threads.
>
> gdb/doc
> * gdb.texinfo (GDB/MI Thread Information): New.
> (GDB/MI Async Records): Document the core field in *stopped.
> (GDB/MI Miscellaneous Commands): Expand -list-thread-groups
> documentation
> (Process list): Document that osdata document may contain
> threads.
> (Remote Serial Protocol): Document qXfer:threads.
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 348d853..882be85 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -444,7 +444,8 @@ 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/osdata.dtd
> + $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
> + $(srcdir)/features/threads.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,
> diff --git a/gdb/bcache.c b/gdb/bcache.c
> index d56df07..e467c5b 100644
> --- a/gdb/bcache.c
> +++ b/gdb/bcache.c
> @@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
> \f
> /* Printing statistics. */
>
> -static int
> -compare_ints (const void *ap, const void *bp)
> -{
> - /* Because we know we're comparing two ints which are positive,
> - there's no danger of overflow here. */
> - return * (int *) ap - * (int *) bp;
> -}
> -
> -
> static void
> print_percentage (int portion, int total)
> {
> @@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
>
> /* To compute the median, we need the set of chain lengths sorted. */
> qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
> - compare_ints);
> + compare_positive_ints);
> qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
> - compare_ints);
> + compare_positive_ints);
>
> if (c->num_buckets > 0)
> {
> diff --git a/gdb/defs.h b/gdb/defs.h
> index b944ffb..396e9a8 100644
> --- a/gdb/defs.h
> +++ b/gdb/defs.h
> @@ -417,6 +417,8 @@ char *ldirname (const char *filename);
>
> char **gdb_buildargv (const char *);
>
> +int compare_positive_ints (const void *ap, const void *bp);
> +
> /* From demangle.c */
>
> extern void set_demangling_style (char *);
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index e880838..d1d5075 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -15402,6 +15402,10 @@ are:
> @tab @code{qXfer:siginfo:write}
> @tab @code{set $_siginfo}
>
> +@item @code{threads}
> +@tab @code{qXfer:threads:read}
> +@tab @code{info threads}
> +
> @item @code{get-thread-local-@*storage-address}
> @tab @code{qGetTLSAddr}
> @tab Displaying @code{__thread} variables
> @@ -21688,6 +21692,7 @@ follow development on @email{gdb@@sourceware.org} and
> * GDB/MI Stream Records::
> * GDB/MI Async Records::
> * GDB/MI Frame Information::
> +* GDB/MI Thread Information::
> @end menu
>
> @node GDB/MI Result Records
> @@ -21780,7 +21785,7 @@ several times, either for different threads, because it cannot resume
> all threads together, or even for a single thread, if the thread must
> be stepped though some code before letting it run freely.
>
> -@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
> +@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
> The target has stopped. The @var{reason} field can have one of the
> following values:
>
> @@ -21820,7 +21825,9 @@ If all threads are stopped, the @var{stopped} field will have the
> value of @code{"all"}. Otherwise, the value of the @var{stopped}
> field will be a list of thread identifiers. Presently, this list will
> always include a single thread, but frontend should be prepared to see
> -several threads in the list.
> +several threads in the list. The @var{core} field reports on which
> +processor core the stop event has happened. This field may be absent
> +if such information is not available.
>
> @item =thread-group-created,id="@var{id}"
> @itemx =thread-group-exited,id="@var{id}"
> @@ -21897,6 +21904,34 @@ corresponds to the frame's code address. This field may be absent.
>
> @end table
>
> +@node GDB/MI Thread Information
> +@subsection @sc{gdb/mi} Thread Information
> +
> +Whenver @value{GDBN} has to report an information about a thread, it
^ typo: Whenever
> +uses a tuple with the following fields:
> +
> +@table @code
> +@item id
> +The numeric id assigned to the thread by @value{GDBN}. This field is
> +always present.
> +
> +@item target-id
> +Target-specific string identifying the thread. This field is always present.
> +
> +@item details
> +Additional information about the thread provided by the target.
> +It is supposed to be human-readable and not interpreted by the
> +frontend. This field is optional.
> +
> +@item state
> +Either @samp{stopped} or @samp{running}, depending on whether the
> +thread is presently running. This field is always present.
> +
> +@item cores
Typo: "core"?
> +The value of this field is an integer number of the processor core the
> +thread was last seen on. This field is optional.
> +@end table
> +
>
> @c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> @node GDB/MI Simple Examples
> @@ -26202,20 +26237,83 @@ while the target is running.
> @subheading Synopsis
>
> @smallexample
> --list-thread-groups [ --available ] [ @var{group} ]
> +-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
> @end smallexample
>
> -When used without the @var{group} parameter, lists top-level thread
> -groups that are being debugged. When used with the @var{group}
> -parameter, the children of the specified group are listed. The
> -children can be either threads, or other groups. At present,
> -@value{GDBN} will not report both threads and groups as children at
> -the same time, but it may change in future.
> +Lists thread groups (@pxref{Thread groups}). When a single thread
Double space after period. Here and elsewhere.
> +group is passed as the argument, lists the children specified one.
> +When several thread group are passed, lists information about those
> +thread groups. Without any parameters, lists information about all
> +top-level thread groups.
> +
> +Normally, thread groups that are being debugged are reported.
> +With the @samp{--available} option, @value{GDBN} reports thread groups
> +available on the target.
> +
> +The output of this command may have either a @samp{threads} result or
> +a @samp{groups} result. The @samp{thread} result has a list of tuples
> +as value, with each tuple describing a thread (@pxref{GDB/MI Thread
> +Information}). The @samp{groups} result has a list of tuples as value,
> +each tuple describing a thread group. If top-level groups are
> +requested (that is, no parameter is passed), or when several groups
> +are passed, the output always have a @samp{groups} result. The format
> +of the @samp{group} result is described below.
> +
> +To reduce the number of roundtrips it's possible to list thread groups
> +together with their children, by passing the @samp{--recurse} option
> +and the recursion depth. Presently, only recursion depth of 1 is
> +permitted. If this option is present, then every reported thread group
> +will also include its children, either as @samp{group} or
> +@samp{threads} field.
> +
> +In general, any combination of option and parameters is permitted, with
> +the following caveats:
> +
> +@itemize @bullet
> +@item
> +When a single thread group is passed, the output will typically
> +be the @samp{threads} result. Because threads may not contain
> +anything, the @samp{recurse} option will be ignored.
> +
> +@item
> +When the @samp{--available} option is passed, limited information may
> +be available. In particular, the list of threads of a process might
> +be inaccessible. Further, specifying specific thread groups might
> +not give any performance advantage over listing all thread groups.
> +The frontend should assume that @samp{-list-thread-groups --available}
> +is always an expensive operation and cache the results.
> +
> +@end itemize
> +
> +The @samp{groups} result is a list of tuples, where each tuple may
> +have the following fields:
> +
> +@table @code
> +@item id
> +Identifier of the thread group. This field is always present.
> +
> +@item type
> +The type of the thread group. At present, only @samp{process} is a
> +valid type.
> +
> +@item pid
> +The target-specific process identifier. This field is only present
> +for thread groups of type @samp{process}.
> +
> +@item num_children
> +The number of children this thread group has. This field may be
> +absent for an available thread group.
> +
> +@item threads
> +This field has a list of tuples as value, each tuple describing a
> +thread. It may be present
Looks like an unfinished sentence.
>
> -With the @samp{--available} option, instead of reporting groups that
> -are been debugged, GDB will report all thread groups available on the
> -target. Using the @samp{--available} option together with @var{group}
> -is not allowed.
> +@item cores
> +This field is a list of integers, each identifying a core that one
> +thread of the group is running on. This field may be absent if
> +such information is not available.
> +
> +@end table
>
> @subheading Example
>
> @@ -26229,6 +26327,16 @@ is not allowed.
> @{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
> frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
> file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
> +-list-thread-groups --available
> +^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
> +-list-thread-groups --available --recurse 1
> + ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
> + threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
> + @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
> +-list-thread-groups --available --recurse 1 17 18
> +^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
> + threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
> + @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
> @end smallexample
>
> @subheading The @code{-interpreter-exec} Command
> @@ -27978,6 +28086,7 @@ Show the current setting of the target wait timeout.
> * File-I/O Remote Protocol Extension::
> * Library List Format::
> * Memory Map Format::
> +* Thread List Format::
> @end menu
>
> @node Overview
> @@ -28878,6 +28987,10 @@ If @var{n} is @samp{thread}, then @var{r} is the @var{thread-id} of
> the stopped thread, as specified in @ref{thread-id syntax}.
>
> @item
> +If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
> +the core on which the stop event was detected.
> +
> +@item
> If @var{n} is a recognized @dfn{stop reason}, it describes a more
> specific event that stopped the target. The currently defined stop
> reasons are listed below. @var{aa} should be @samp{05}, the trap
> @@ -28911,8 +29024,6 @@ logged execution events, because it has reached the end (or the
> beginning when executing backward) of the log. The value of @var{r}
> will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
> for more information.
> -
> -
> @end table
>
> @item W @var{AA}
> @@ -29452,6 +29563,12 @@ These are the currently defined stub features and their properties:
> @tab @samp{-}
> @tab Yes
>
> +@item @samp{qXfer:threads:read}
> +@tab No
> +@tab @samp{-}
> +@tab Yes
> +
> +
> @item @samp{QNonStop}
> @tab No
> @tab @samp{-}
> @@ -29535,6 +29652,10 @@ The remote stub understands the @samp{qXfer:siginfo:read} packet
> The remote stub understands the @samp{qXfer:siginfo:write} packet
> (@pxref{qXfer siginfo write}).
>
> +@item qXfer:threads:read
> +The remote stub understands the @samp{qXfer:threads:read} packet
> +(@pxref{qXfer threads read}).
> +
> @item QNonStop
> The remote stub understands the @samp{QNonStop} packet
> (@pxref{QNonStop}).
> @@ -29727,6 +29848,15 @@ This packet is not probed by default; the remote stub must request it,
> by supplying an appropriate @samp{qSupported} response
> (@pxref{qSupported}).
>
> +@item qXfer:threads:read::@var{offset},@var{length}
> +@anchor{qXfer threads read}
> +Access the list of threads on target. @xref{Thread List Format}. The
> +annex part of the generic @samp{qXfer} packet must be empty
> +(@pxref{qXfer read}).
> +
> +This packet is not probed by default; the remote stub must request it,
What's the advantage of this, rather than probing it? I would
see it nice for GDB to know in advance that the target can
report core data, but that's not that reporting support
for this packet means, since the "core" field is optional.
> +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}.
> @@ -31710,6 +31840,32 @@ The formal DTD for memory map format is given below:
> <!ATTLIST property name CDATA #REQUIRED>
> @end smallexample
>
> +@node Thread List Format
> +@section Thread List Format
> +@cindex thread list format
> +
> +To efficiently update the list of threads and their attributes,
> +@value{GDBN} gdb requests, using the @samp{qXfer:threads:read}
> +package (@pxref{qXfer threads read}) and XML document with the
> +format described below.
> +
> +The top-level structure of the document is shown below:
> +
> +@smallexample
> +<?xml version="1.0"?>
> +<threads>
> + <thread id="id" core="0">
> + some details
> + </thread>
> +</memory-map>
s/memory-map/threads
"some details" ?
> +@end smallexample
> +
> +Each @samp{thread} element must have the @samp{id} attribute that
> +identifies the thread (@pxref{thread-id syntax}). The
> +@samp{core} attribute, if present, specifies which process core
s/process core/processor core/ ?
> +the thread was last executing on. The content of the of @samp{thread}
> +element is interpreted as hunam-readable auxilliary information.
typo: hunam
> +
> @include agentexpr.texi
>
> @node Target Descriptions
> @@ -32269,15 +32425,34 @@ An example document is:
> <column name="pid">1</column>
> <column name="user">root</column>
> <column name="command">/sbin/init</column>
> + <column name="cores">1,2,3</column>
> + <threads>
> + <item>
> + <column name="tid">12</column>
> + <column name="core">3</column>
> + </item>
> + </threads>
> </item>
> </osdata>
> @end smallexample
>
> -Each item should include a column whose name is @samp{pid}. The value
> +The document has an @samp{osdata} element that contains a number of
> +@samp{item} elements describing processes. Each process @samp{item}
> +may contain @samp{threads} element with items describing threads of
> +that process. Both process and thread items contain a number of
> +@samp{column} elements with various information.
> +
> +Each process 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.
> +displayed by @value{GDBN}. The @samp{cores} column, if present,
> +should contain a comma-separated list of cores that this process
> +is running on.
> +Each thread item should include a column whose name is @samp{tid}.
> +The value of that is a thread identifier, specific to the operating
> +system.
> The @samp{core} column, if present, is the integer number of
> +the core the thread is running on.
>
> @include gpl.texi
>
> diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd
> index a04506b..67de837 100644
> --- a/gdb/features/osdata.dtd
> +++ b/gdb/features/osdata.dtd
> @@ -10,7 +10,10 @@
> <!ATTLIST osdata version CDATA #FIXED "1.0">
> <!ATTLIST osdata type CDATA #REQUIRED>
>
> -<!ELEMENT item (column*)>
> +<!ELEMENT item (column*, threads?)>
>
> <!ELEMENT column (#PCDATA)>
> <!ATTLIST column name CDATA #REQUIRED>
> +
> +<!ELEMENT threads (item*)>
> +
This dtd change isn't right. "osdata" isn't specific to
processes, it is just a generic table. A "threads" element
doesn't fit in, it wouldn't make sense for anything other
than listing processes. Two alternative solutions:
- Allow multiple sub-table elements in the osdata schema, and
allow a "name" attribute on this element. In the listing
processes case, you'd have a sub-table whose type/name
attribute would be "threads". You should change
info_osdata_command to descend into subtables somehow.
- Don't nest "threads" within processes osdata. Treat it as
a different request / table (e.g., <osdata type="threads">).
That is, on the CLI, "info os processes" works as is, unmodified, and,
you'd add support for "info os threads". No schema changes
are necessary with this option. At the MI implementation level, you'd
do two requests to get the same data: get_osdata("processes"), and
get_osdata("threads").
> diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
> new file mode 100644
> index 0000000..e7c3a11
> --- /dev/null
> +++ b/gdb/features/threads.dtd
> @@ -0,0 +1,14 @@
> +<!-- Copyright (C) 2008, 2009 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. -->
> +
> +<!ELEMENT threads (thread*)>
> +<!ATTLIST threads version CDATA #FIXED "1.0">
> +
> +<!ELEMENT thread (#PCDATA)>
> +
> +<!ATTLIST thread id CDATA #REQUIRED>
> +<!ATTLIST thread core CDATA #IMPLIED>
> +
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index 45dd9f7..d1d853d 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -137,6 +137,7 @@ static void *add_lwp (ptid_t ptid);
> static int my_waitpid (int pid, int *status, int flags);
> static int linux_stopped_by_watchpoint (void);
> static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
> +static int linux_core_of_thread (ptid_t ptid);
>
> struct pending_signals
> {
> @@ -2789,6 +2790,111 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
> #endif
>
> static int
> +compare_ints (const void *xa, const void *xb)
> +{
> + int a = *(const int *)xa;
> + int b = *(const int *)xb;
> +
> + return a - b;
> +}
> +
> +/* Given PID, print information about every thread in that process to BUFFER.
> + Set *CORES to a string with a comma-separated list of cores that threads
> + of PID execute on. The string must be freed by the caller. BUFFER must
> + be initialized. If no cores are found, *CORES will be set to NULL. */
> +static void
> +list_threads (int pid, struct buffer *buffer, char **cores)
> +{
> + int count = 0;
> + int allocated = 10;
> + int *core_numbers = xmalloc (sizeof (int) * allocated);
> + char pathname[128];
> + DIR *dir;
> + struct dirent *dp;
> + struct stat statbuf;
> +
> + sprintf (pathname, "/proc/%d/task", pid);
> + if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
> + {
> + dir = opendir (pathname);
> + if (!dir)
> + return;
> +
> + buffer_xml_printf (buffer, "<threads>");
> + while ((dp = readdir (dir)) != NULL)
> + {
> + unsigned long lwp = strtoul (dp->d_name, NULL, 10);
> +
> + if (lwp != 0)
> + {
> + unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
> +
> + if (core != -1)
> + {
> + char s[11];
> + sprintf (s, "%u", core);
> +
> + if (count == allocated)
> + core_numbers = realloc (core_numbers,
> + sizeof (int ) * (allocated *= 2));
No space after int. Please move the 'allocated *= 2' in its own
statement. My GM hat tells me that I should point out that there are a
bunch of hardcoded buffer sizes in the patch, that the GNU conventions
tells us we should avoid.
> + core_numbers[count++] = core;
> + buffer_xml_printf (buffer,
> + "<item>"
> + "<column name=\"tid\">%s</column>"
> + "<column name=\"core\">%s</column>"
> + "</item>", dp->d_name, s);
> + }
> + else
> + {
> + buffer_xml_printf (buffer,
> + "<item>"
> + "<column name=\"tid\">%s</column>"
> + "</item>", dp->d_name);
> + }
> + }
> + }
> + buffer_xml_printf (buffer, "</threads>");
> + }
> +
> + *cores = NULL;
> + if (count > 0)
> + {
> + struct buffer buffer2;
> + int *b;
> + int *e;
> + qsort (core_numbers, count, sizeof (int), compare_ints);
> +
> + /* Remove duplicates. */
> + b = core_numbers;
> + e = core_numbers + count;
> +
> + while (b != e) {
> + if ((b + 1 < e) && (*b == *(b+1)))
> + {
> + *(b + 1) = *(e - 1);
> + --e;
> + }
> + else
> + {
> + ++b;
> + }
> + }
> +
> + buffer_init (&buffer2);
> +
> + for (b = core_numbers; b != e; ++b) {
'{' goes on its own line.
> + char number[11];
> + sprintf (number, "%u", *b);
> + buffer_xml_printf (&buffer2, "%s%s",
> + (b == core_numbers) ? "" : ",", number);
> + }
> + buffer_grow_str0 (&buffer2, "");
There's a bug here somewhere in the duplicates detection that
we talked about off-list, that I know you've fixed
already. :-)
> +
> + *cores = buffer_finish (&buffer2);
> + }
> +}
> +
> +static int
> linux_qxfer_osdata (const char *annex,
> unsigned char *readbuf, unsigned const char *writebuf,
> CORE_ADDR offset, int len)
> @@ -2837,15 +2943,20 @@ linux_qxfer_osdata (const char *annex,
> FILE *f;
> char cmd[MAXPATHLEN + 1];
> struct passwd *entry;
> + int pid;
>
> sprintf (pathname, "/proc/%s/cmdline", dp->d_name);
> entry = getpwuid (statbuf.st_uid);
>
> + pid = (int)strtoul (dp->d_name, NULL, 10);
Space after (int).
> +
> if ((f = fopen (pathname, "r")) != NULL)
> {
> size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
> if (len > 0)
> {
> + struct buffer thread_buffer;
> + char *cores = 0;
> int i;
> for (i = 0; i < len; i++)
> if (cmd[i] == '\0')
> @@ -2857,11 +2968,29 @@ linux_qxfer_osdata (const char *annex,
> "<item>"
> "<column name=\"pid\">%s</column>"
> "<column name=\"user\">%s</column>"
> - "<column name=\"command\">%s</column>"
> - "</item>",
> + "<column name=\"command\">%s</column>",
> dp->d_name,
> entry ? entry->pw_name : "?",
> cmd);
> +
> + buffer_init (&thread_buffer);
> + list_threads (pid, &thread_buffer, &cores);
> +
> + if (cores)
> + {
> + buffer_xml_printf (
> + &buffer,
> + "<column name=\"cores\">%s</column>", cores);
> + free (cores);
> + }
> +
> + buffer_grow (&buffer,
> + thread_buffer.buffer,
> + thread_buffer.used_size);
> + free (buffer_finish (&thread_buffer));
> +
> +
> + buffer_xml_printf (&buffer, "</item>");
Any chance linux-nat.c also gets support for this (however it
ends up looking like)?
> }
> fclose (f);
> }
> @@ -3139,6 +3268,53 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
> return ret;
> }
>
> +static int
> +linux_core_of_thread (ptid_t ptid)
> +{
> + char filename[sizeof ("/proc//task//stat")
s,//,/
> + + 2*20 /* decimal digits for 2 numbers, max 2^64 bit each */
Spaces around '*'.
> + + 1];
> + FILE *f;
> + char *content = NULL;
> + char *p;
> + char *ts = 0;
> + int content_read = 0;
> + int i;
> + int core;
> +
> + sprintf (filename, "/proc/%d/task/%ld/stat",
> + ptid_get_pid (ptid), ptid_get_lwp (ptid));
> + f = fopen (filename, "r");
> +
Should check if file opened successfully. Older
kernels don't have /proc/<pid>/task available --- see "man proc".
> + for (;;)
> + {
> + int n;
> + content = realloc (content, content_read + 1024);
> + n = fread (content + content_read, 1, 1024, f);
> + content_read += n;
> + if (n < 1024)
> + {
> + content[content_read] = '\0';
> + break;
> + }
> + }
> +
> + p = strchr (content, '(');
> + p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
> +
> + p = strtok_r (p, " ", &ts);
> + for (i = 0; i != 36; ++i)
> + p = strtok_r (NULL, " ", &ts);
> +
> + if (sscanf (p, "%d", &core) == 0)
> + core = -1;
> +
> + free (content);
> + fclose (f);
> +
> + return core;
> +}
> +
> static struct target_ops linux_target_ops = {
> linux_create_inferior,
> linux_attach,
> @@ -3178,10 +3354,11 @@ static struct target_ops linux_target_ops = {
> linux_start_non_stop,
> linux_supports_multi_process,
> #ifdef USE_THREAD_DB
> - thread_db_handle_monitor_command
> + thread_db_handle_monitor_command,
> #else
> - NULL
> + NULL,
> #endif
> + linux_core_of_thread
> };
>
> static void
> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
> index 158b653..97c076d 100644
> --- a/gdb/gdbserver/remote-utils.c
> +++ b/gdb/gdbserver/remote-utils.c
> @@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
> gdbserver to know what inferior_ptid is. */
> if (1 || !ptid_equal (general_thread, ptid))
> {
> + int core = -1;
> /* In non-stop, don't change the general thread behind
> GDB's back. */
> if (!non_stop)
> @@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
> buf = write_ptid (buf, ptid);
> strcat (buf, ";");
> buf += strlen (buf);
> +
> + if (the_target->core_for_thread)
> + core = (*the_target->core_for_thread) (ptid);
> + if (core != -1)
> + {
> + sprintf (buf, "core:");
> + buf += strlen (buf);
> + sprintf (buf, "%x", core);
> + strcat (buf, ";");
> + buf += strlen (buf);
> + }
> }
> }
>
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index 14bc7e7..62e3e3d 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
> }
> }
>
> +static void
> +handle_threads_qxfer_proper (struct buffer *buffer)
> +{
> + struct inferior_list_entry *thread;
> +
> + buffer_grow_str (buffer, "<threads>\n");
> +
> + for (thread = all_threads.head; thread; thread = thread->next)
> + {
> + ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
> + char ptid_s[100];
> + int core = -1;
> + char core_s[21];
> +
> + write_ptid (ptid_s, ptid);
> +
> + if (the_target->core_for_thread)
> + core = (*the_target->core_for_thread) (ptid);
> +
> + if (core != -1)
> + {
> + sprintf (core_s, "%d", core);
> + buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
> + ptid_s, core_s);
> + }
> + else
> + {
> + buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
> + ptid_s);
> + }
> + }
> +
> + buffer_grow_str0 (buffer, "</threads>\n");
> +}
> +
> +static int
> +handle_threads_qxfer (const char *annex,
> + unsigned char *readbuf,
> + CORE_ADDR offset, int length)
> +{
> + static char *result = 0;
> + static unsigned int result_length = 0;
> +
> + if (annex && strcmp (annex, "") != 0)
> + return 0;
> +
> + if (offset == 0)
> + {
> + struct buffer buffer;
> + /* When asked for data at offset 0, generate everything and store into
> + 'result'. Successive reads will be served off 'result'. */
> + if (result)
> + free (result);
> +
> + buffer_init (&buffer);
> +
> + handle_threads_qxfer_proper (&buffer);
> +
> + result = buffer_finish (&buffer);
> + result_length = strlen (result);
> + buffer_free (&buffer);
> + }
> +
> + if (offset >= result_length)
> + {
> + /* We're out of data. */
> + free (result);
> + result = NULL;
> + result_length = 0;
> + return 0;
> + }
> +
> + if (length > result_length - offset)
> + length = result_length - offset;
> +
> + memcpy (readbuf, result + offset, length);
> +
> + return length;
> +
> +}
> +
> /* Handle all of the extended 'q' packets. */
> void
> handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> @@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> return;
> }
>
> + if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
> + {
> + unsigned char *data;
> + int n;
> + CORE_ADDR ofs;
> + unsigned int len;
> + char *annex;
> +
> + require_running (own_buf);
> +
> + /* Reject any annex; grab the offset and length. */
> + if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
> + || annex[0] != '\0')
> + {
> + strcpy (own_buf, "E00");
> + return;
> + }
> +
> + /* Read one extra byte, as an indicator of whether there is
> + more. */
> + if (len > PBUFSIZ - 2)
> + len = PBUFSIZ - 2;
> + data = malloc (len + 1);
> + if (!data)
> + return;
> + n = handle_threads_qxfer (annex, data, ofs, len + 1);
> + if (n < 0)
> + write_enn (own_buf);
> + else if (n > len)
> + *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
> + else
> + *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
> +
> + free (data);
> + return;
> + }
> +
> /* Protocol features query. */
> if (strncmp ("qSupported", own_buf, 10) == 0
> && (own_buf[10] == ':' || own_buf[10] == '\0'))
> @@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> if (target_supports_non_stop ())
> strcat (own_buf, ";QNonStop+");
>
> + strcat (own_buf, ";qXfer:threads:read+");
> +
> return;
> }
>
> diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
> index 0e4b127..8e8778a 100644
> --- a/gdb/gdbserver/target.h
> +++ b/gdb/gdbserver/target.h
> @@ -283,6 +283,9 @@ struct target_ops
> /* If not NULL, target-specific routine to process monitor command.
> Returns 1 if handled, or 0 to perform default processing. */
> int (*handle_monitor_command) (char *);
> +
> + /* Returns the core given a thread, or -1 if not known. */
> + int (*core_for_thread) (ptid_t);
> };
>
> extern struct target_ops *the_target;
> diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
> index 2f388fb..bd0044b 100644
> --- a/gdb/gdbthread.h
> +++ b/gdb/gdbthread.h
> @@ -346,4 +346,6 @@ extern struct cleanup *make_cleanup_restore_current_thread (void);
> INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
> extern struct thread_info* inferior_thread (void);
>
> +extern void update_thread_list (void);
> +
> #endif /* GDBTHREAD_H */
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index c0afecd..b232caf 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -5421,6 +5421,59 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
> return inf->aspace;
> }
>
> +/* Return the core for a thread. */
> +static int
> +linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
> +{
> + struct cleanup *back_to;
> + char *filename;
> + FILE *f;
> + char *content = NULL;
> + char *p;
> + char *ts = 0;
> + int content_read = 0;
> + int i;
> + int core;
> +
> + filename = xstrprintf ("/proc/%d/task/%ld/stat",
> + GET_PID (ptid), GET_LWP (ptid));
> + back_to = make_cleanup (xfree, filename);
> +
> + f = fopen (filename, "r");
> + make_cleanup_fclose (f);
Should check if file opened successfully. Older
kernels don't have /proc/<pid>/task available --- see "man proc".
> +
> + for (;;)
> + {
> + int n;
> + content = xrealloc (content, content_read + 1024);
> + n = fread (content + content_read, 1, 1024, f);
> + content_read += n;
> + if (n < 1024)
> + {
> + content[content_read] = '\0';
> + break;
> + }
> + }
> +
> + make_cleanup (xfree, content);
> +
> + p = strchr (content, '(');
> + p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
> +
> + /* If the first field after program name has index 0, then core number is
> + the field with index 36. There's no constant for that anywhere. */
> + p = strtok_r (p, " ", &ts);
> + for (i = 0; i != 36; ++i)
> + p = strtok_r (NULL, " ", &ts);
> +
> + if (sscanf (p, "%d", &core) == 0)
> + core = -1;
> +
> + do_cleanups (back_to);
> +
> + return core;
> +}
> +
> void
> linux_nat_add_target (struct target_ops *t)
> {
> @@ -5461,6 +5514,8 @@ linux_nat_add_target (struct target_ops *t)
>
> t->to_supports_multi_process = linux_nat_supports_multi_process;
>
> + t->to_core_of_thread = linux_nat_core_of_thread;
> +
> /* We don't change the stratum; this target will sit at
> process_stratum and thread_db will set at thread_stratum. This
> is a little strange, since this is a multi-threaded-capable
> diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
> index 248cd66..af51bde 100644
> --- a/gdb/mi/mi-interp.c
> +++ b/gdb/mi/mi-interp.c
> @@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
>
> if (print_frame)
> {
> + int core;
> if (uiout != mi_uiout)
> {
> /* The normal_stop function has printed frame information into
> @@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
> }
> else
> ui_out_field_string (mi_uiout, "stopped-threads", "all");
> +
> + core = target_core_of_thread (inferior_ptid);
> + if (core != -1)
> + ui_out_field_int (mi_uiout, "core", core);
> }
>
> fputs_unfiltered ("*stopped", raw_stdout);
> diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
> index 2332752..b152d98 100644
> --- a/gdb/mi/mi-main.c
> +++ b/gdb/mi/mi-main.c
> @@ -359,11 +359,44 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
> print_thread_info (uiout, thread, -1);
> }
>
> +struct collect_cores_data
> +{
> + int pid;
> +
> + VEC (int) *cores;
> +};
> +
> +static int collect_cores (struct thread_info *ti, void *xdata)
Line break before function name, please.
> +{
> + struct collect_cores_data *data = xdata;
> +
> + if (ptid_get_pid (ti->ptid) == data->pid)
> + {
> + int core = target_core_of_thread (ti->ptid);
> + if (core != -1)
> + VEC_safe_push (int, data->cores, core);
> + }
> +
> + return 0;
> +}
> +
> +struct print_one_inferior_data
> +{
> + int recurse;
> + VEC (int) *inferiors;
> +};
> +
> static int
> -print_one_inferior (struct inferior *inferior, void *arg)
> +print_one_inferior (struct inferior *inferior, void *xdata)
> {
> - if (inferior->pid != 0)
> + struct print_one_inferior_data *top_data = xdata;
> +
> + if (VEC_length (int, top_data->inferiors) == 0
> + || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
> + VEC_length (int, top_data->inferiors), sizeof (int),
> + compare_positive_ints))
> {
> + struct collect_cores_data data;
> struct cleanup *back_to
> = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
>
> @@ -371,36 +404,128 @@ print_one_inferior (struct inferior *inferior, void *arg)
> ui_out_field_string (uiout, "type", "process");
> ui_out_field_int (uiout, "pid", inferior->pid);
>
> + data.pid = inferior->pid;
> + data.cores = 0;
> + iterate_over_threads (collect_cores, &data);
> +
> + if (VEC_length (int, data.cores))
Use instead:
if (!VEC_empty (int, data.cores))
> + {
> + int elt;
> + int i;
> + int *b, *e;
> + struct cleanup *back_to_2 =
> + make_cleanup_ui_out_list_begin_end (uiout, "cores");
> +
> + qsort (VEC_address (int, data.cores),
> + VEC_length (int, data.cores), sizeof (int),
> + compare_positive_ints);
> +
> + b = VEC_address (int, data.cores);
> + e = b + VEC_length (int, data.cores);
> +
> + /* Remove duplicates. */
> + while (b != e) {
> + if ((b + 1 < e) && (*b == *(b+1)))
Spaces around '+'.
> + {
> + *(b + 1) = *(e - 1);
> + --e;
> + }
> + else
> + {
> + ++b;
> + }
> + }
> +
> + for (i = 0; VEC_iterate (int, data.cores, i, elt); ++i)
> + ui_out_field_int (uiout, NULL, elt);
> +
> + do_cleanups (back_to_2);
> + }
> +
> + if (top_data->recurse)
> + print_thread_info (uiout, -1, inferior->pid);
> +
> do_cleanups (back_to);
> }
>
> return 0;
> }
>
> +/* Output a field named 'cores' with a list as the value. The elements of
> + the list are obtained by splitting 'cores' on comma. */
> +static void
Double space after period. Please add one empty line between describing
comment and the function.
> +output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
> +{
> + struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
> + field_name);
> + char *cores = xstrdup (xcores);
> + char *p = cores;
> + char *ts = 0;
> +
> + make_cleanup (xfree, cores);
> +
> + for (p = strtok_r (p, ",", &ts); p; p = strtok_r (NULL, ",", &ts))
> + ui_out_field_string (uiout, NULL, p);
strtok_r isn't portable (not available on mingw, for example).
You could pull it from gnulib, but I doubt it's worth the trouble.
> +
> + do_cleanups (back_to);
> +}
> +
> void
> mi_cmd_list_thread_groups (char *command, char **argv, int argc)
> {
> struct cleanup *back_to;
> int available = 0;
> - char *id = NULL;
> + int recurse = 0;
> + VEC (int) *ids = 0;
>
> - if (argc > 0 && strcmp (argv[0], "--available") == 0)
> + enum opt
> {
> - ++argv;
> - --argc;
> - available = 1;
> - }
> + AVAILABLE_OPT, RECURSE_OPT
> + };
> + static struct mi_opt opts[] =
> + {
> + {"-available", AVAILABLE_OPT, 0},
> + {"-recurse", RECURSE_OPT, 1},
> + { 0, 0, 0 }
> + };
>
> - if (argc > 0)
> - id = argv[0];
> + int optind = 0;
> + char *optarg;
>
> - back_to = make_cleanup (null_cleanup, NULL);
> + while (1)
> + {
> + int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
> + &optind, &optarg);
> + if (opt < 0)
> + break;
> + switch ((enum opt) opt)
> + {
> + case AVAILABLE_OPT:
> + available = 1;
> + break;
> + case RECURSE_OPT:
> + if (strcmp (optarg, "1") != 0)
> + error ("only '1' is permitted value for the '--recurse' option");
Seems strange not to allow '--recurse 0'. What future values
are you thinking of? Wouldn't it make more sense to call this
"--threads"? We may add other level-1 sublists to thread-groups
in the future, and _not_ want --recurse 1 to always show
them, no?
> + recurse = 1;
> + break;
> + }
> + }
>
> - if (available && id)
> + for (; optind < argc; ++optind)
> {
> - error (_("Can only report top-level available thread groups"));
> + char *end;
> + int inf = strtoul (argv[optind], &end, 0);
> + if (*end != '\0')
> + error ("invalid group id '%s'", argv[optind]);
Something fishy with the tabs vs spaces here.
> + VEC_safe_push (int, ids, inf);
> }
> - else if (available)
> + qsort (VEC_address (int, ids),
> + VEC_length (int, ids),
> + sizeof (int), compare_positive_ints);
> +
> + back_to = make_cleanup ((void (*)(void *))VEC_OP (int, free), &ids);
Please don't cast the function's type: instead write a
small function that has the proper cleanup prototype, and
call free there.
> +
> + if (available)
> {
> struct osdata *data;
> struct osdata_item *item;
> @@ -416,12 +541,25 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
> ix_items, item);
> ix_items++)
> {
> - struct cleanup *back_to =
> - make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
> + struct cleanup *back_to;
>
> const char *pid = get_osdata_column (item, "pid");
> const char *cmd = get_osdata_column (item, "command");
> const char *user = get_osdata_column (item, "user");
> + const char *cores = get_osdata_column (item, "cores");
> +
> + int pid_i = strtoul (pid, NULL, 0);
> +
> + /* At present, the target will return all available processes
> + and if information about specific ones was required, we filter
> + undesired processes here. */
> + if (ids && bsearch (&pid_i, VEC_address (int, ids),
> + VEC_length (int, ids),
> + sizeof (int), compare_positive_ints) == NULL)
> + continue;
> +
> +
> + back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
>
> ui_out_field_fmt (uiout, "id", "%s", pid);
> ui_out_field_string (uiout, "type", "process");
> @@ -429,21 +567,57 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
> ui_out_field_string (uiout, "description", cmd);
> if (user)
> ui_out_field_string (uiout, "user", user);
> + if (cores)
> + output_cores (uiout, "cores", cores);
> +
> + if (item->children && recurse)
> + {
> + struct osdata_item *child;
> + int ix_child;
> +
> + make_cleanup_ui_out_list_begin_end (uiout, "threads");
> +
> + for (ix_child = 0; VEC_iterate (osdata_item_s,
> + item->children->items,
> + ix_child, child); ++ix_child)
Please format as:
for (ix_child = 0;
VEC_iterate (osdata_item_s, item->children->items,
ix_child, child);
++ix_child)
(it took me a bit to locate the ++ix_child statement in your formatting)
> + {
> + struct cleanup *back_to_2 =
> + make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
> +
> + const char *tid = get_osdata_column (child, "tid");
> + const char *tcore = get_osdata_column (child, "core");
> + ui_out_field_string (uiout, "id", tid);
> + if (tcore)
> + ui_out_field_string (uiout, "core", tcore);
> +
> + do_cleanups (back_to_2);
> + }
> + }
>
> do_cleanups (back_to);
> }
> }
> - else if (id)
> + else if (VEC_length (int, ids) == 1)
> {
> - int pid = atoi (id);
> + /* Local thread groups, single id. */
> + int pid = *VEC_address (int, ids);
> if (!in_inferior_list (pid))
> - error ("Invalid thread group id '%s'", id);
> + error ("Invalid thread group id '%d'", pid);
> print_thread_info (uiout, -1, pid);
> }
> else
> {
> + struct print_one_inferior_data data;
> + data.recurse = recurse;
> + data.inferiors = ids;
> +
> + /* Local thread groups. Either no explicit ids -- and we
> + print everything, or several explicit ids. In both cases,
> + we print more than one group, and have to use 'groups'
> + as the top-level element. */
> make_cleanup_ui_out_list_begin_end (uiout, "groups");
> - iterate_over_inferiors (print_one_inferior, NULL);
> + update_thread_list ();
> + iterate_over_inferiors (print_one_inferior, &data);
> }
>
> do_cleanups (back_to);
> diff --git a/gdb/osdata.c b/gdb/osdata.c
> index 3d2c23c..394fc4d 100644
> --- a/gdb/osdata.c
> +++ b/gdb/osdata.c
> @@ -50,8 +50,10 @@ osdata_parse (const char *xml)
> /* Internal parsing data passed to all XML callbacks. */
> struct osdata_parsing_data
> {
> + struct osdata *top_osdata;
> struct osdata *osdata;
> char *property_name;
> + struct osdata_item *current_item;
> };
>
> /* Handle the start of a <osdata> element. */
> @@ -71,9 +73,37 @@ osdata_start_osdata (struct gdb_xml_parser *parser,
> type = VEC_index (gdb_xml_value_s, attributes, 0)->value;
> osdata = XZALLOC (struct osdata);
> osdata->type = xstrdup (type);
> + data->top_osdata = data->osdata = osdata;
> +}
> +
> +/* Handle the start of a <threads> element. */
> +
> +static void
> +osdata_start_threads (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;
> +
> + osdata = XZALLOC (struct osdata);
> + osdata->type = xstrdup ("threads");
> data->osdata = osdata;
> }
>
> +static void
> +osdata_end_threads (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;
> +
> + data->current_item->children = data->osdata;
> + data->osdata = data->top_osdata;
> +}
> +
> +
> /* Handle the start of a <item> element. */
>
> static void
> @@ -84,6 +114,8 @@ osdata_start_item (struct gdb_xml_parser *parser,
> struct osdata_parsing_data *data = user_data;
> struct osdata_item item = { NULL };
> VEC_safe_push (osdata_item_s, data->osdata->items, &item);
> + if (data->osdata == data->top_osdata)
> + data->current_item = VEC_last (osdata_item_s, data->osdata->items);
> }
>
> /* Handle the start of a <column> element. */
> @@ -137,10 +169,15 @@ const struct gdb_xml_attribute column_attributes[] = {
> { NULL, GDB_XML_AF_NONE, NULL, NULL }
> };
>
> +extern const struct gdb_xml_element osdata_children[];
> +
> 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 },
> + { "threads", NULL, osdata_children,
> + GDB_XML_EF_OPTIONAL, osdata_start_threads,
> + osdata_end_threads },
> { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
> };
>
> diff --git a/gdb/osdata.h b/gdb/osdata.h
> index 4ada454..485789e 100644
> --- a/gdb/osdata.h
> +++ b/gdb/osdata.h
> @@ -22,6 +22,9 @@
>
> #include "vec.h"
>
> +typedef struct osdata *osdata_p;
> +DEF_VEC_P(osdata_p);
> +
> typedef struct osdata_column
> {
> char *name;
> @@ -32,6 +35,13 @@ DEF_VEC_O(osdata_column_s);
> typedef struct osdata_item
> {
> VEC(osdata_column_s) *columns;
> +
> + /* Nested items. Presently, a given item may only contain
> + children of specific kind, and children->type gives
> + the kind of children. In fact, only 'threads' may be
> + the kind of children right now. */
> + osdata_p children;
> +
> } osdata_item_s;
> DEF_VEC_O(osdata_item_s);
>
> @@ -41,8 +51,7 @@ struct osdata
>
> 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 *);
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 9fa92fb..eb47713 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -60,6 +60,7 @@
> #include "remote-fileio.h"
> #include "gdb/fileio.h"
> #include "gdb_stat.h"
> +#include "xml-support.h"
>
> #include "memory-map.h"
>
> @@ -303,6 +304,13 @@ struct remote_state
> int ctrlc_pending_p;
> };
>
> +/* Private data that we'll store in (struct thread_info)->private. */
> +struct private_thread_info
> +{
> + char *extra;
> + int core;
> +};
> +
> /* Returns true if the multi-process extensions are in effect. */
> static int
> remote_multi_process_p (struct remote_state *rs)
> @@ -1054,6 +1062,7 @@ enum {
> PACKET_qXfer_spu_read,
> PACKET_qXfer_spu_write,
> PACKET_qXfer_osdata,
> + PACKET_qXfer_threads,
> PACKET_qGetTLSAddr,
> PACKET_qSupported,
> PACKET_QPassSignals,
> @@ -2303,6 +2312,76 @@ remote_find_new_threads (void)
> CRAZY_MAX_THREADS);
> }
>
> +typedef struct thread_item
> +{
> + ptid_t ptid;
> + char *extra;
> + int core;
> +} thread_item_t;
> +DEF_VEC_O(thread_item_t);
> +
> +struct threads_parsing_context
> +{
> + VEC (thread_item_t) *items;
> +};
> +
> +static void
> +start_thread (struct gdb_xml_parser *parser,
> + const struct gdb_xml_element *element,
> + void *user_data, VEC(gdb_xml_value_s) *attributes)
libexpat is an optional dependency. This code should be
conditionalized on HAVE_LIBEXPAT. After fixing, please confirm
GDB still builds and works against gdbserver when configured
with "--with-expat=no".
> +{
> + struct threads_parsing_context *data = user_data;
> +
> + struct thread_item item;
> + char *id;
> +
> + id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
> + item.ptid = read_ptid (id, NULL);
> +
> + if (VEC_length (gdb_xml_value_s, attributes) > 1)
> + item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
> + else
> + item.core = -1;
> +
> + item.extra = 0;
> +
> + VEC_safe_push (thread_item_t, data->items, &item);
> +}
> +
> +static void
> +end_thread (struct gdb_xml_parser *parser,
> + const struct gdb_xml_element *element,
> + void *user_data, const char *body_text)
> +{
> + struct threads_parsing_context *data = user_data;
> +
> + if (body_text && *body_text)
> + VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
> +}
> +
> +const struct gdb_xml_attribute thread_attributes[] = {
> + { "id", GDB_XML_AF_NONE, NULL, NULL },
> + { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
> + { NULL, GDB_XML_AF_NONE, NULL, NULL }
> +};
> +
> +const struct gdb_xml_element thread_children[] = {
> + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
> +};
> +
> +const struct gdb_xml_element threads_children[] = {
> + { "thread", thread_attributes, thread_children,
> + GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
> + start_thread, end_thread },
> + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
> +};
> +
> +const struct gdb_xml_element threads_elements[] = {
> + { "threads", NULL, threads_children,
> + GDB_XML_EF_NONE, NULL, NULL },
> + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
> +};
> +
> /*
> * Find all threads for info threads command.
> * Uses new thread protocol contributed by Cisco.
> @@ -2320,6 +2399,66 @@ remote_threads_info (struct target_ops *ops)
> if (remote_desc == 0) /* paranoia */
> error (_("Command can only be used when connected to the remote target."));
>
> + if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
> + {
> + char *xml = target_read_stralloc (¤t_target,
> + TARGET_OBJECT_THREADS, NULL);
> +
> + struct cleanup *back_to = make_cleanup (xfree, xml);
> + if (xml && *xml)
> + {
> + struct gdb_xml_parser *parser;
> + struct threads_parsing_context context;
> + struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
> +
> + context.items = 0;
> + parser = gdb_xml_create_parser_and_cleanup (_("threads"),
> + threads_elements,
> + &context);
> +
> + gdb_xml_use_dtd (parser, "threads.dtd");
> +
> + if (gdb_xml_parse (parser, xml) == 0)
> + {
> + int i;
> + struct thread_item *item;
> +
> + for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
> + {
> + if (!ptid_equal (item->ptid, null_ptid))
> + {
> + struct thread_info *info;
> + /* In non-stop mode, we assume new found threads
> + are running until proven otherwise with a
> + stop reply. In all-stop, we can only get
> + here if all threads are stopped. */
> + int running = non_stop ? 1 : 0;
> +
> + remote_notice_new_inferior (item->ptid, running);
> +
> + info = find_thread_ptid (item->ptid);
> + if (info)
> + {
> + if (!info->private)
> + info->private = (struct private_thread_info *)
> + xmalloc (sizeof (struct private_thread_info));
> +
> + info->private->extra = item->extra;
> + item->extra = 0;
> + info->private->core = item->core;
> + }
> + }
> + xfree (item->extra);
> + }
> + }
> +
> + VEC_free (thread_item_t, context.items);
> + }
> +
> + do_cleanups (back_to);
> + return;
> + }
> +
> if (use_threadinfo_query)
> {
> putpkt ("qfThreadInfo");
> @@ -2392,6 +2531,15 @@ remote_threads_extra_info (struct thread_info *tp)
> server doesn't know about it. */
> return NULL;
>
> + if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
> + {
> + struct thread_info *info = find_thread_ptid (tp->ptid);
> + if (info && info->private)
> + return info->private->extra;
> + else
> + return NULL;
> + }
> +
> if (use_threadextra_query)
> {
> char *b = rs->buf;
> @@ -3152,6 +3300,8 @@ static struct protocol_feature remote_protocol_features[] = {
> PACKET_qXfer_spu_write },
> { "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
> PACKET_qXfer_osdata },
> + { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
> + PACKET_qXfer_threads },
> { "QPassSignals", PACKET_DISABLE, remote_supported_packet,
> PACKET_QPassSignals },
> { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
> @@ -4262,6 +4412,8 @@ struct stop_reply
>
> int solibs_changed;
> int replay_event;
> +
> + int core;
> };
>
> /* The list of already fetched and acknowledged stop events. */
> @@ -4425,6 +4577,7 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
> event->replay_event = 0;
> event->stopped_by_watchpoint_p = 0;
> event->regcache = NULL;
> + event->core = -1;
>
> switch (buf[0])
> {
> @@ -4451,7 +4604,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
> /* If this packet is an awatch packet, don't parse the 'a'
> as a register number. */
>
> - if (strncmp (p, "awatch", strlen("awatch")) != 0)
> + if (strncmp (p, "awatch", strlen("awatch")) != 0
> + && strncmp (p, "core", strlen ("core") != 0))
> {
> /* Read the ``P'' register number. */
> pnum = strtol (p, &p_temp, 16);
> @@ -4497,6 +4651,12 @@ Packet: '%s'\n"),
> if (p_temp)
> p = p_temp;
> }
> + else if (strncmp (p, "core", p1 - p) == 0)
> + {
> + ULONGEST c;
> + p = unpack_varlen_hex (++p1, &c);
> + event->core = c;
> + }
> else
> {
> /* Silently skip unknown optional info. */
> @@ -4706,6 +4866,7 @@ process_stop_reply (struct stop_reply *stop_reply,
> struct target_waitstatus *status)
> {
> ptid_t ptid;
> + struct thread_info *info;
>
> *status = stop_reply->ws;
> ptid = stop_reply->ptid;
> @@ -4736,6 +4897,12 @@ process_stop_reply (struct stop_reply *stop_reply,
> remote_stopped_by_watchpoint_p = stop_reply->stopped_by_watchpoint_p;
> remote_watch_data_address = stop_reply->watch_data_address;
>
> + /* Update the core associated with a thread when we process stop
> + event in that thread. */
> + info = find_thread_ptid (ptid);
> + if (info && info->private)
> + info->private->core = stop_reply->core;
Consider all-stop, multi-threaded applications:
What about the core of the other threads?
Shouldn't they all be invalidated?
Won't then be stale?
(see comment below)
> +
> remote_notice_new_inferior (ptid, 0);
> }
>
> @@ -7579,6 +7746,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
> (ops, "osdata", annex, readbuf, offset, len,
> &remote_protocol_packets[PACKET_qXfer_osdata]);
>
> + case TARGET_OBJECT_THREADS:
> + gdb_assert (annex == NULL);
> + return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
> + &remote_protocol_packets[PACKET_qXfer_threads]);
> +
> default:
> return -1;
> }
> @@ -8895,6 +9067,15 @@ remote_supports_cond_tracepoints (void)
> return rs->cond_tracepoints;
> }
>
> +static int
> +remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
> +{
> + struct thread_info *info = find_thread_ptid (ptid);
> + if (info && info->private)
> + return info->private->core;
> + return -1;
> +}
(see comment above) I think this returns stale core data
for all other threads other than the one that reported
the stop, no? How is that handled?
> +
> static void
> init_remote_ops (void)
> {
> @@ -8958,6 +9139,7 @@ Specify the serial device it is connected to\n\
> remote_ops.to_terminal_ours = remote_terminal_ours;
> remote_ops.to_supports_non_stop = remote_supports_non_stop;
> remote_ops.to_supports_multi_process = remote_supports_multi_process;
> + remote_ops.to_core_of_thread = remote_core_of_thread;
> }
>
> /* Set up the extended remote vector by making a copy of the standard
> @@ -9317,6 +9499,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
> add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
> "qXfer:osdata:read", "osdata", 0);
>
> + add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
> + "qXfer:threads:read", "threads", 0);
> +
> add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
> "qXfer:siginfo:read", "read-siginfo-object", 0);
>
> diff --git a/gdb/target.c b/gdb/target.c
> index cd1614b..4fd82b9 100644
> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -3024,6 +3024,26 @@ target_store_registers (struct regcache *regcache, int regno)
> noprocess ();
> }
>
> +int
> +target_core_of_thread (ptid_t ptid)
> +{
> + struct target_ops *t;
> +
> + for (t = current_target.beneath; t != NULL; t = t->beneath)
> + {
> + if (t->to_core_of_thread != NULL)
> + {
> + int retval = t->to_core_of_thread (t, ptid);
> + if (targetdebug)
> + fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
> + PIDGET (ptid), retval);
> + return retval;
> + }
> + }
> +
> + return -1;
> +}
> +
> static void
> debug_to_prepare_to_store (struct regcache *regcache)
> {
> diff --git a/gdb/target.h b/gdb/target.h
> index ebe6056..7435433 100644
> --- a/gdb/target.h
> +++ b/gdb/target.h
> @@ -255,7 +255,9 @@ enum target_object
> /* Extra signal info. Usually the contents of `siginfo_t' on unix
> platforms. */
> TARGET_OBJECT_SIGNAL_INFO,
> - /* Possible future objects: TARGET_OBJECT_FILE, ... */
> + /* The list of threads that are being debugged. */
> + TARGET_OBJECT_THREADS,
> + /* Possible future objects: TARGET_OBJECT_FILE, ... */
> };
>
> /* Request that OPS transfer up to LEN 8-bit bytes of the target's
> @@ -597,6 +599,14 @@ struct target_ops
> struct address_space *(*to_thread_address_space) (struct target_ops *,
> ptid_t);
>
> + /* Return the core that thread PTID is on, or -1 if such information
> + is not available. For a stopped thread, this is supposed to return
> + the core the thread was last running on. For running threads, it
> + should return one of the cores that the thread was running between
> + the call to this function and return -- and if it was running on
> + several cores, any other may be returned. */
> + int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
I guess that on some targets, it will be impossible to know which core
a running thread was running on. Should it be simply documented as
undefined instead?
> +
> int to_magic;
> /* Need sub-structure for target machine related rather than comm related?
> */
> @@ -1246,6 +1256,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
> (*current_target.to_log_command) (p); \
> while (0)
>
> +
> +extern int target_core_of_thread (ptid_t ptid);
> +
> /* Routines for maintenance of the target structures...
>
> add_target: Add a target to the list of all possible targets.
> diff --git a/gdb/thread.c b/gdb/thread.c
> index 689e9d5..fc2ee2a 100644
> --- a/gdb/thread.c
> +++ b/gdb/thread.c
> @@ -468,8 +468,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
> struct cleanup *cleanup_chain;
> int current_thread = -1;
>
> - prune_threads ();
> - target_find_new_threads ();
> + update_thread_list ();
>
> cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
>
> @@ -748,8 +747,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
> char *extra_info;
> int current_thread = -1;
>
> - prune_threads ();
> - target_find_new_threads ();
> + update_thread_list ();
> current_ptid = inferior_ptid;
>
> /* We'll be switching threads temporarily. */
> @@ -759,6 +757,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
> for (tp = thread_list; tp; tp = tp->next)
> {
> struct cleanup *chain2;
> + int core;
>
> if (requested_thread != -1 && tp->num != requested_thread)
> continue;
> @@ -817,6 +816,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
> ui_out_field_string (uiout, "state", state);
> }
>
> + core = target_core_of_thread (tp->ptid);
> + if (ui_out_is_mi_like_p (uiout) && core != -1)
> + ui_out_field_int (uiout, "core", core);
> +
> do_cleanups (chain2);
> }
>
> @@ -1058,8 +1061,7 @@ thread_apply_all_command (char *cmd, int from_tty)
> if (cmd == NULL || *cmd == '\000')
> error (_("Please specify a command following the thread ID list"));
>
> - prune_threads ();
> - target_find_new_threads ();
> + update_thread_list ();
>
> old_chain = make_cleanup_restore_current_thread ();
>
> @@ -1245,6 +1247,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
> return GDB_RC_OK;
> }
>
> +void
> +update_thread_list (void)
> +{
> + prune_threads ();
> + target_find_new_threads ();
> +}
> +
> /* Commands with a prefix of `thread'. */
> struct cmd_list_element *thread_cmd_list = NULL;
>
> diff --git a/gdb/ui-out.c b/gdb/ui-out.c
> index 19a4644..6ecdd1f 100644
> --- a/gdb/ui-out.c
> +++ b/gdb/ui-out.c
> @@ -44,7 +44,7 @@ struct ui_out_hdr
> is always available. Stack/nested level 0 is reserved for the
> top-level result. */
>
> -enum { MAX_UI_OUT_LEVELS = 6 };
> +enum { MAX_UI_OUT_LEVELS = 8 };
>
> struct ui_out_level
> {
> diff --git a/gdb/utils.c b/gdb/utils.c
> index 16ad084..c9aa612 100644
> --- a/gdb/utils.c
> +++ b/gdb/utils.c
> @@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
> return argv;
> }
>
> +int
> +compare_positive_ints (const void *ap, const void *bp)
> +{
> + /* Because we know we're comparing two ints which are positive,
> + there's no danger of overflow here. */
> + return * (int *) ap - * (int *) bp;
> +}
> +
> /* Provide a prototype to silence -Wmissing-prototypes. */
> extern initialize_file_ftype _initialize_utils;
>
Other:
- did you consider being able to list the available
cores? A candidate for "info os cores", I guess.
- your patch adds a bunch of trailing whitespace, would it
be possible to get rid of those please?
>quilt refresh
Warning: trailing whitespace in lines 26273,26309,31859 of gdb/doc/gdb.texinfo
Warning: trailing whitespace in line 260 of gdb/target.h
Warning: trailing whitespace in lines 2351,2352,2404,2408,2420,2452,2454,2455 of gdb/remote.c
Warning: trailing whitespace in line 101 of gdb/osdata.c
Warning: trailing whitespace in line 44 of gdb/osdata.h
Warning: trailing whitespace in lines 729,732,737,763,767,770,772,781,784,1230 of gdb/gdbserver/server.c
Warning: trailing whitespace in lines 2827,2837,2838,2854,2875,2880,2888,2892,2894,2959,2982,2986,2987,2988,2992,3274,3284,3285,3299,3304,3311 of gdb/gdbserver/linux-low.c
Warning: trailing whitespace in lines 393,411,438,446,459,469,497,503,504,522,556,557,576,580,581,584,594 of gdb/mi/mi-main.c
Refreshed patch core-awareness.diff
(quilt refresh --strip-trailing-whitespace does it automaticaly, and I
guess git has something similar)
- Don't forget to go over the patch and add
double-space-after-period everywhere. Sorry for the pickyness.
--
Pedro Alves
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2009-12-16 20:53 [MI] core awareness Vladimir Prus
2009-12-17 15:31 ` Pedro Alves
@ 2009-12-18 11:41 ` Eli Zaretskii
1 sibling, 0 replies; 17+ messages in thread
From: Eli Zaretskii @ 2009-12-18 11:41 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
> From: Vladimir Prus <vladimir@codesourcery.com>
> Date: Wed, 16 Dec 2009 23:52:38 +0300
>
> This is mostly MI change, but includes some remote protocol tweaks,
> and documentation. I'd appreciate review of those parts.
Thanks, see below for the documentation review.
> -several threads in the list.
> +several threads in the list. The @var{core} field reports on which
> +processor core the stop event has happened.
Suggest to rephrase slightly:
The @var{core} field reports the processor core on which the stop
event has happened.
> +@item id
> +The numeric id assigned to the thread by @value{GDBN}. This field is
> +always present. ^^
Two spaces, please.
> +Lists thread groups (@pxref{Thread groups}). When a single thread
> +group is passed as the argument, lists the children specified one.
Something is missing in the last sentence ("children of the specified
one"?).
> +are passed, the output always have a @samp{groups} result. The format
^^^^
"has"
> +thread. It may be present
Pedro already noticed this incomplete sentence.
> +@value{GDBN} gdb requests, using the @samp{qXfer:threads:read}
^^^
Remove the "gdb" part.
> +package (@pxref{qXfer threads read}) and XML document with the
> +format described below.
Sounds like an incomplete sentence (GDB requests what?).
Thanks.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-12 22:56 ` Doug Evans
2010-01-12 23:08 ` Doug Evans
@ 2010-01-13 5:21 ` Vladimir Prus
1 sibling, 0 replies; 17+ messages in thread
From: Vladimir Prus @ 2010-01-13 5:21 UTC (permalink / raw)
To: Doug Evans; +Cc: Pedro Alves, gdb-patches
On Wednesday 13 January 2010 01:56:32 Doug Evans wrote:
> On Tue, Jan 12, 2010 at 1:43 PM, Vladimir Prus
> <vladimir@codesourcery.com> wrote:
> > Thanks. Here's the version I've just checked in. Differs from above in:
> >
> > - mi-support.exp fix to make it not crash on the 'core' field
> > - demand_private_info now initialized 'extra' to 0, and does not rely
> > on luck.
>
> Hi.
>
> Nit: features/threads.dtd didn't get checked in.
Doh. Fixed.
Thanks,
Volodya
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-12 22:56 ` Doug Evans
@ 2010-01-12 23:08 ` Doug Evans
2010-01-13 5:21 ` Vladimir Prus
1 sibling, 0 replies; 17+ messages in thread
From: Doug Evans @ 2010-01-12 23:08 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
On Tue, Jan 12, 2010 at 2:56 PM, Doug Evans <dje@google.com> wrote:
> On Tue, Jan 12, 2010 at 1:43 PM, Vladimir Prus
> <vladimir@codesourcery.com> wrote:
>> Thanks. Here's the version I've just checked in. Differs from above in:
>>
>> - mi-support.exp fix to make it not crash on the 'core' field
>> - demand_private_info now initialized 'extra' to 0, and does not rely
>> on luck.
>
> Hi.
>
> Nit: features/threads.dtd didn't get checked in.
>
Sorry, one more nit.
I'm getting a build failure:
cc1: warnings being treated as errors
../../../src/gdb/mi/mi-main.c: In function 'mi_cmd_list_thread_groups':
../../../src/gdb/mi/mi-main.c:518: warning: 'tree' may be used
uninitialized in this function
../../../src/gdb/mi/mi-main.c:518: note: 'tree' was declared here
make: *** [mi-main.o] Error 1
Checked in as obvious.
2010-01-12 Doug Evans <dje@google.com>
* mi/mi-main.c (list_available_thread_groups): Avoid "may be used
uninitialized" warning from gcc on local `tree'.
Index: mi/mi-main.c
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-main.c,v
retrieving revision 1.162
diff -u -p -r1.162 mi-main.c
--- mi/mi-main.c 12 Jan 2010 21:40:24 -0000 1.162
+++ mi/mi-main.c 12 Jan 2010 23:00:51 -0000
@@ -513,9 +513,10 @@ list_available_thread_groups (VEC (int)
struct osdata_item *item;
int ix_items;
/* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
- The vector contains information about all threads for the given
- pid. */
- splay_tree tree;
+ The vector contains information about all threads for the given pid.
+ This is assigned an initial value to avoid "may be used uninitialized"
+ warning from gcc. */
+ splay_tree tree = NULL;
/* get_osdata will throw if it cannot return data. */
data = get_osdata ("processes");
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-12 21:44 ` Vladimir Prus
@ 2010-01-12 22:56 ` Doug Evans
2010-01-12 23:08 ` Doug Evans
2010-01-13 5:21 ` Vladimir Prus
0 siblings, 2 replies; 17+ messages in thread
From: Doug Evans @ 2010-01-12 22:56 UTC (permalink / raw)
To: Vladimir Prus; +Cc: Pedro Alves, gdb-patches
On Tue, Jan 12, 2010 at 1:43 PM, Vladimir Prus
<vladimir@codesourcery.com> wrote:
> Thanks. Here's the version I've just checked in. Differs from above in:
>
> - mi-support.exp fix to make it not crash on the 'core' field
> - demand_private_info now initialized 'extra' to 0, and does not rely
> on luck.
Hi.
Nit: features/threads.dtd didn't get checked in.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-11 21:22 ` Pedro Alves
@ 2010-01-12 21:44 ` Vladimir Prus
2010-01-12 22:56 ` Doug Evans
0 siblings, 1 reply; 17+ messages in thread
From: Vladimir Prus @ 2010-01-12 21:44 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: Text/Plain, Size: 3807 bytes --]
On Tuesday 12 January 2010 00:22:07 Pedro Alves wrote:
> On Monday 11 January 2010 21:01:08, Vladimir Prus wrote:
> > Implement core awareness.
> >
> > gdb/
> > * bcache.c (compare_ints): Remove
> > (print_percentage): Use compare_positive_ints.
> >
> > * defs.h (compare_positive_ints): Declare.
> > * linux-nat.h (struct lin_lwp): New field core.
> > (linux_nat_core_of_thread_1): Declare.
> > * linux-nat.c (add_lwp): Init the 'core' field.
> > (linux_nat_wait_1): Record the core.
> > (linux_nat_core_of_thread_1, linux_nat_core_of_thread): New.
> > (linux_nat_add_target): Register the above.
> > * linux-thread-db.c (update_thread_core): New.
> > (thread_db_find_new_threads): Update core information for
> > every thread.
> > * remote.c (struct private_thread_info): New.
> > (free_private_thread_info, demand_private_info): New.
> > (PACKET_qXfer_threads, use_osdata_threads): New.
> > (struct thread_item, threads_parsing_context
> > (start_thread, end_thread, thread_attributes)
> > (thread_children, threads_children, threads_elements): New.
> > (remote_threads_info): Try qXfer:threads before anything
> > else.
> > (remote_protocol_packets): Register qXfer:threads.
> > (remote_open_1): Init use_osdata_threads.
> > (struct stop_reply): New field 'core'.
> > (remote_parse_stop_reply): Parse core number.
> > (process_stop_reply): Record core number.
> > (remote_xfer_partial): Handle qXfer:threads.
> > (remote_core_of_thread): New.
> > (init_remote_ops): Register remote_core_of_thread.
> > (_initialize_remote): Register qXfer:read.
> > * target.c (target_core_of_thread): New
> > * target.h (enum target_object): New value TARGET_OBJECT_THREADS.
> > (struct target_ops): New field to_core_of_threads.
> > (target_core_of_thread): Declare.
> > * gdbthread.h (struct thread_info): New field private_dtor.
> > * thread.c (print_thread_info): Report the core.
> > * ui-out.c (MAX_UI_OUT_LEVELS): Increase.
> > * utils.c (compare_positive_ints): New.
> > * features/threads.dtd: New.
> > * mi/mi-interp.c (mi_on_normal_stop): Report the core.
> > * mi/mi-main.c (struct collect_cores_data, collect_cores)
> > (do_nothing, free_vector_of_osdata_items)
> > (splay_tree_int_comparator, free_splay_tree): New.
> > (print_one_inferior_data): Implemented printing of selected
> > inferiors. Collect and print cores.
> > (output_cores): New.
> > (mi_cmd_list_thread_groups): Support --recurse. Permit specifying
> > thread groups together with --available.
> >
> > gdbserver/
> > * linux-low.c (linux_core_of_thread): New.
> > (compare_ints, show_process, list_threads): New.
> > (linux_qxfer_osdata): Report threads and cores.
> > (linux_target_op): Register linux_core_of_thread.
> > * remote-utils.c (prepare_resume_reply): Report the core.
> > (buffer_xml_printf): Support %d specifier.
> > * server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
> > New.
> > (handle_query): Handle qXfer:threads. Announce availability
> > thereof.
> > * target.h (struct target_ops): New field core_of_thread.
>
> This version is OK. Thanks.
Thanks. Here's the version I've just checked in. Differs from above in:
- mi-support.exp fix to make it not crash on the 'core' field
- demand_private_info now initialized 'extra' to 0, and does not rely
on luck.
- Volodya
>
>
- Volodya
[-- Attachment #2: final.diff --]
[-- Type: text/x-patch, Size: 67747 bytes --]
Index: gdb/ChangeLog
===================================================================
RCS file: /cvs/src/src/gdb/ChangeLog,v
retrieving revision 1.11239
diff -u -p -r1.11239 ChangeLog
--- gdb/ChangeLog 12 Jan 2010 12:22:49 -0000 1.11239
+++ gdb/ChangeLog 12 Jan 2010 21:37:14 -0000
@@ -1,3 +1,55 @@
+2010-01-13 Vladimir Prus <vladimir@codesourcery.com>
+
+ Implement core awareness.
+
+ * bcache.c (compare_ints): Remove
+ (print_percentage): Use compare_positive_ints.
+ * defs.h (compare_positive_ints): Declare.
+ * linux-nat.h (struct lin_lwp): New field core.
+ (linux_nat_core_of_thread_1): Declare.
+ * linux-nat.c (add_lwp): Init the 'core' field.
+ (linux_nat_wait_1): Record the core.
+ (linux_nat_core_of_thread_1, linux_nat_core_of_thread): New.
+ (linux_nat_add_target): Register the above.
+ * linux-thread-db.c (update_thread_core): New.
+ (thread_db_find_new_threads): Update core information for
+ every thread.
+ * remote.c (struct private_thread_info): New.
+ (free_private_thread_info, demand_private_info): New.
+ (PACKET_qXfer_threads, use_osdata_threads): New.
+ (struct thread_item, threads_parsing_context
+ (start_thread, end_thread, thread_attributes)
+ (thread_children, threads_children, threads_elements): New.
+ (remote_threads_info): Try qXfer:threads before anything
+ else.
+ (remote_protocol_packets): Register qXfer:threads.
+ (remote_open_1): Init use_osdata_threads.
+ (struct stop_reply): New field 'core'.
+ (remote_parse_stop_reply): Parse core number.
+ (process_stop_reply): Record core number.
+ (remote_xfer_partial): Handle qXfer:threads.
+ (remote_core_of_thread): New.
+ (init_remote_ops): Register remote_core_of_thread.
+ (_initialize_remote): Register qXfer:read.
+ * target.c (target_core_of_thread): New
+ * target.h (enum target_object): New value TARGET_OBJECT_THREADS.
+ (struct target_ops): New field to_core_of_threads.
+ (target_core_of_thread): Declare.
+ * gdbthread.h (struct thread_info): New field private_dtor.
+ * thread.c (print_thread_info): Report the core.
+ * ui-out.c (MAX_UI_OUT_LEVELS): Increase.
+ * utils.c (compare_positive_ints): New.
+ * features/threads.dtd: New.
+ * mi/mi-interp.c (mi_on_normal_stop): Report the core.
+ * mi/mi-main.c (struct collect_cores_data, collect_cores)
+ (do_nothing, free_vector_of_osdata_items)
+ (splay_tree_int_comparator, free_splay_tree): New.
+ (print_one_inferior_data): Implemented printing of selected
+ inferiors. Collect and print cores.
+ (output_cores): New.
+ (mi_cmd_list_thread_groups): Support --recurse. Permit specifying
+ thread groups together with --available.
+
2010-01-12 Jan Kratochvil <jan.kratochvil@redhat.com>
* configure: Regenerate (for _STRUCTURED_PROC).
Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1107
diff -u -p -r1.1107 Makefile.in
--- gdb/Makefile.in 1 Jan 2010 07:31:28 -0000 1.1107
+++ gdb/Makefile.in 12 Jan 2010 21:37:14 -0000
@@ -444,7 +444,8 @@ 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/osdata.dtd
+ $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+ $(srcdir)/features/threads.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,
Index: gdb/bcache.c
===================================================================
RCS file: /cvs/src/src/gdb/bcache.c,v
retrieving revision 1.25
diff -u -p -r1.25 bcache.c
--- gdb/bcache.c 1 Jan 2010 07:31:30 -0000 1.25
+++ gdb/bcache.c 12 Jan 2010 21:37:14 -0000
@@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
\f
/* Printing statistics. */
-static int
-compare_ints (const void *ap, const void *bp)
-{
- /* Because we know we're comparing two ints which are positive,
- there's no danger of overflow here. */
- return * (int *) ap - * (int *) bp;
-}
-
-
static void
print_percentage (int portion, int total)
{
@@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *
/* To compute the median, we need the set of chain lengths sorted. */
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
- compare_ints);
+ compare_positive_ints);
qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
- compare_ints);
+ compare_positive_ints);
if (c->num_buckets > 0)
{
Index: gdb/defs.h
===================================================================
RCS file: /cvs/src/src/gdb/defs.h,v
retrieving revision 1.260
diff -u -p -r1.260 defs.h
--- gdb/defs.h 1 Jan 2010 07:31:30 -0000 1.260
+++ gdb/defs.h 12 Jan 2010 21:37:14 -0000
@@ -417,6 +417,8 @@ char *ldirname (const char *filename);
char **gdb_buildargv (const char *);
+int compare_positive_ints (const void *ap, const void *bp);
+
/* From demangle.c */
extern void set_demangling_style (char *);
Index: gdb/gdbthread.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbthread.h,v
retrieving revision 1.53
diff -u -p -r1.53 gdbthread.h
--- gdb/gdbthread.h 1 Jan 2010 07:31:32 -0000 1.53
+++ gdb/gdbthread.h 12 Jan 2010 21:37:14 -0000
@@ -187,6 +187,10 @@ struct thread_info
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
+
+ /* Function that is called to free PRIVATE. If this is NULL, then
+ xfree will be called on PRIVATE. */
+ void (*private_dtor) (struct private_thread_info *);
};
/* Create an empty thread list, or empty the existing one. */
@@ -346,4 +350,6 @@ extern struct cleanup *make_cleanup_rest
INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
extern struct thread_info* inferior_thread (void);
+extern void update_thread_list (void);
+
#endif /* GDBTHREAD_H */
Index: gdb/linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/linux-nat.c,v
retrieving revision 1.159
diff -u -p -r1.159 linux-nat.c
--- gdb/linux-nat.c 8 Jan 2010 22:52:03 -0000 1.159
+++ gdb/linux-nat.c 12 Jan 2010 21:37:14 -0000
@@ -1184,6 +1184,7 @@ add_lwp (ptid_t ptid)
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
lp->ptid = ptid;
+ lp->core = -1;
lp->next = lwp_list;
lwp_list = lp;
@@ -3642,6 +3643,7 @@ retry:
fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
restore_child_signals_mask (&prev_mask);
+ lp->core = linux_nat_core_of_thread_1 (lp->ptid);
return lp->ptid;
}
@@ -5423,6 +5425,75 @@ linux_nat_thread_address_space (struct t
return inf->aspace;
}
+int
+linux_nat_core_of_thread_1 (ptid_t ptid)
+{
+ struct cleanup *back_to;
+ char *filename;
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ filename = xstrprintf ("/proc/%d/task/%ld/stat",
+ GET_PID (ptid), GET_LWP (ptid));
+ back_to = make_cleanup (xfree, filename);
+
+ f = fopen (filename, "r");
+ if (!f)
+ {
+ do_cleanups (back_to);
+ return -1;
+ }
+
+ make_cleanup_fclose (f);
+
+ for (;;)
+ {
+ int n;
+ content = xrealloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ make_cleanup (xfree, content);
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ /* If the first field after program name has index 0, then core number is
+ the field with index 36. There's no constant for that anywhere. */
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ do_cleanups (back_to);
+
+ return core;
+}
+
+/* Return the cached value of the processor core for thread PTID. */
+
+int
+linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct lwp_info *info = find_lwp_pid (ptid);
+ if (info)
+ return info->core;
+ return -1;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -5463,6 +5534,8 @@ linux_nat_add_target (struct target_ops
t->to_supports_multi_process = linux_nat_supports_multi_process;
+ t->to_core_of_thread = linux_nat_core_of_thread;
+
/* We don't change the stratum; this target will sit at
process_stratum and thread_db will set at thread_stratum. This
is a little strange, since this is a multi-threaded-capable
Index: gdb/linux-nat.h
===================================================================
RCS file: /cvs/src/src/gdb/linux-nat.h,v
retrieving revision 1.33
diff -u -p -r1.33 linux-nat.h
--- gdb/linux-nat.h 1 Jan 2010 07:31:37 -0000 1.33
+++ gdb/linux-nat.h 12 Jan 2010 21:37:14 -0000
@@ -89,6 +89,9 @@ struct lwp_info
- TARGET_WAITKIND_SYSCALL_RETURN */
int syscall_state;
+ /* The processor core this LWP was last seen on. */
+ int core;
+
/* Next LWP in list. */
struct lwp_info *next;
};
@@ -163,3 +166,6 @@ void linux_nat_switch_fork (ptid_t new_p
/* Return the saved siginfo associated with PTID. */
struct siginfo *linux_nat_get_siginfo (ptid_t ptid);
+
+/* Compute and return the processor core of a given thread. */
+int linux_nat_core_of_thread_1 (ptid_t ptid);
Index: gdb/linux-thread-db.c
===================================================================
RCS file: /cvs/src/src/gdb/linux-thread-db.c,v
retrieving revision 1.71
diff -u -p -r1.71 linux-thread-db.c
--- gdb/linux-thread-db.c 1 Jan 2010 07:31:37 -0000 1.71
+++ gdb/linux-thread-db.c 12 Jan 2010 21:37:14 -0000
@@ -1454,6 +1454,12 @@ thread_db_find_new_threads_1 (ptid_t pti
thread_db_find_new_threads_2 (ptid, 0);
}
+static int
+update_thread_core (struct lwp_info *info, void *closure)
+{
+ info->core = linux_nat_core_of_thread_1 (info->ptid);
+ return 0;
+}
static void
thread_db_find_new_threads (struct target_ops *ops)
@@ -1466,6 +1472,9 @@ thread_db_find_new_threads (struct targe
return;
thread_db_find_new_threads_1 (inferior_ptid);
+
+ iterate_over_lwps (minus_one_ptid /* iterate over all */,
+ update_thread_core, NULL);
}
static char *
Index: gdb/remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.379
diff -u -p -r1.379 remote.c
--- gdb/remote.c 7 Jan 2010 19:17:46 -0000 1.379
+++ gdb/remote.c 12 Jan 2010 21:37:15 -0000
@@ -60,6 +60,7 @@
#include "remote-fileio.h"
#include "gdb/fileio.h"
#include "gdb_stat.h"
+#include "xml-support.h"
#include "memory-map.h"
@@ -324,6 +325,20 @@ struct remote_state
int ctrlc_pending_p;
};
+/* Private data that we'll store in (struct thread_info)->private. */
+struct private_thread_info
+{
+ char *extra;
+ int core;
+};
+
+static void
+free_private_thread_info (struct private_thread_info *info)
+{
+ xfree (info->extra);
+ xfree (info);
+}
+
/* Returns true if the multi-process extensions are in effect. */
static int
remote_multi_process_p (struct remote_state *rs)
@@ -1121,6 +1136,7 @@ enum {
PACKET_qXfer_spu_read,
PACKET_qXfer_spu_write,
PACKET_qXfer_osdata,
+ PACKET_qXfer_threads,
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
@@ -1395,7 +1411,7 @@ remote_notice_new_inferior (ptid_t currt
remote_add_thread (currthread, running);
inferior_ptid = currthread;
}
- return;
+ return;
}
if (ptid_equal (magic_null_ptid, inferior_ptid))
@@ -1405,7 +1421,7 @@ remote_notice_new_inferior (ptid_t currt
doesn't support qC. This is the first stop reported
after an attach, so this is the main thread. Update the
ptid in the thread list. */
- thread_change_ptid (inferior_ptid, currthread);
+ thread_change_ptid (inferior_ptid, currthread);
return;
}
@@ -1427,6 +1443,26 @@ remote_notice_new_inferior (ptid_t currt
}
}
+/* Return the private thread data, creating it if necessary. */
+
+struct private_thread_info *
+demand_private_info (ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+
+ gdb_assert (info);
+
+ if (!info->private)
+ {
+ info->private = xmalloc (sizeof (*(info->private)));
+ info->private_dtor = free_private_thread_info;
+ info->private->core = -1;
+ info->private->extra = 0;
+ }
+
+ return info->private;
+}
+
/* Call this function as a result of
1) A halt indication (T packet) containing a thread id
2) A direct query of currthread
@@ -1437,12 +1473,6 @@ static void
record_currthread (ptid_t currthread)
{
general_thread = currthread;
-
- if (ptid_equal (currthread, minus_one_ptid))
- /* We're just invalidating the local thread mirror. */
- return;
-
- remote_notice_new_inferior (currthread, 0);
}
static char *last_pass_packet;
@@ -2371,6 +2401,80 @@ remote_find_new_threads (void)
CRAZY_MAX_THREADS);
}
+#if defined(HAVE_LIBEXPAT)
+
+typedef struct thread_item
+{
+ ptid_t ptid;
+ char *extra;
+ int core;
+} thread_item_t;
+DEF_VEC_O(thread_item_t);
+
+struct threads_parsing_context
+{
+ VEC (thread_item_t) *items;
+};
+
+static void
+start_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct threads_parsing_context *data = user_data;
+
+ struct thread_item item;
+ char *id;
+
+ id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ item.ptid = read_ptid (id, NULL);
+
+ if (VEC_length (gdb_xml_value_s, attributes) > 1)
+ item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
+ else
+ item.core = -1;
+
+ item.extra = 0;
+
+ VEC_safe_push (thread_item_t, data->items, &item);
+}
+
+static void
+end_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, const char *body_text)
+{
+ struct threads_parsing_context *data = user_data;
+
+ if (body_text && *body_text)
+ VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
+}
+
+const struct gdb_xml_attribute thread_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element thread_children[] = {
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_children[] = {
+ { "thread", thread_attributes, thread_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ start_thread, end_thread },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_elements[] = {
+ { "threads", NULL, threads_children,
+ GDB_XML_EF_NONE, NULL, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+#endif
+
/*
* Find all threads for info threads command.
* Uses new thread protocol contributed by Cisco.
@@ -2388,6 +2492,61 @@ remote_threads_info (struct target_ops *
if (remote_desc == 0) /* paranoia */
error (_("Command can only be used when connected to the remote target."));
+#if defined(HAVE_LIBEXPAT)
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ char *xml = target_read_stralloc (¤t_target,
+ TARGET_OBJECT_THREADS, NULL);
+
+ struct cleanup *back_to = make_cleanup (xfree, xml);
+ if (xml && *xml)
+ {
+ struct gdb_xml_parser *parser;
+ struct threads_parsing_context context;
+ struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+ context.items = 0;
+ parser = gdb_xml_create_parser_and_cleanup (_("threads"),
+ threads_elements,
+ &context);
+
+ gdb_xml_use_dtd (parser, "threads.dtd");
+
+ if (gdb_xml_parse (parser, xml) == 0)
+ {
+ int i;
+ struct thread_item *item;
+
+ for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
+ {
+ if (!ptid_equal (item->ptid, null_ptid))
+ {
+ struct private_thread_info *info;
+ /* In non-stop mode, we assume new found threads
+ are running until proven otherwise with a
+ stop reply. In all-stop, we can only get
+ here if all threads are stopped. */
+ int running = non_stop ? 1 : 0;
+
+ remote_notice_new_inferior (item->ptid, running);
+
+ info = demand_private_info (item->ptid);
+ info->core = item->core;
+ info->extra = item->extra;
+ item->extra = 0;
+ }
+ xfree (item->extra);
+ }
+ }
+
+ VEC_free (thread_item_t, context.items);
+ }
+
+ do_cleanups (back_to);
+ return;
+ }
+#endif
+
if (use_threadinfo_query)
{
putpkt ("qfThreadInfo");
@@ -2460,6 +2619,15 @@ remote_threads_extra_info (struct thread
server doesn't know about it. */
return NULL;
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ struct thread_info *info = find_thread_ptid (tp->ptid);
+ if (info && info->private)
+ return info->private->extra;
+ else
+ return NULL;
+ }
+
if (use_threadextra_query)
{
char *b = rs->buf;
@@ -3245,6 +3413,8 @@ static struct protocol_feature remote_pr
PACKET_qXfer_spu_write },
{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
PACKET_qXfer_osdata },
+ { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
+ PACKET_qXfer_threads },
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
PACKET_QPassSignals },
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -4359,6 +4529,8 @@ struct stop_reply
int solibs_changed;
int replay_event;
+
+ int core;
};
/* The list of already fetched and acknowledged stop events. */
@@ -4522,6 +4694,7 @@ remote_parse_stop_reply (char *buf, stru
event->replay_event = 0;
event->stopped_by_watchpoint_p = 0;
event->regcache = NULL;
+ event->core = -1;
switch (buf[0])
{
@@ -4548,7 +4721,8 @@ remote_parse_stop_reply (char *buf, stru
/* If this packet is an awatch packet, don't parse the 'a'
as a register number. */
- if (strncmp (p, "awatch", strlen("awatch")) != 0)
+ if (strncmp (p, "awatch", strlen("awatch")) != 0
+ && strncmp (p, "core", strlen ("core") != 0))
{
/* Read the ``P'' register number. */
pnum = strtol (p, &p_temp, 16);
@@ -4594,6 +4768,12 @@ Packet: '%s'\n"),
if (p_temp)
p = p_temp;
}
+ else if (strncmp (p, "core", p1 - p) == 0)
+ {
+ ULONGEST c;
+ p = unpack_varlen_hex (++p1, &c);
+ event->core = c;
+ }
else
{
/* Silently skip unknown optional info. */
@@ -4803,6 +4983,7 @@ process_stop_reply (struct stop_reply *s
struct target_waitstatus *status)
{
ptid_t ptid;
+ struct thread_info *info;
*status = stop_reply->ws;
ptid = stop_reply->ptid;
@@ -4834,6 +5015,7 @@ process_stop_reply (struct stop_reply *s
remote_watch_data_address = stop_reply->watch_data_address;
remote_notice_new_inferior (ptid, 0);
+ demand_private_info (ptid)->core = stop_reply->core;
}
stop_reply_xfree (stop_reply);
@@ -7676,6 +7858,11 @@ remote_xfer_partial (struct target_ops *
(ops, "osdata", annex, readbuf, offset, len,
&remote_protocol_packets[PACKET_qXfer_osdata]);
+ case TARGET_OBJECT_THREADS:
+ gdb_assert (annex == NULL);
+ return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
+ &remote_protocol_packets[PACKET_qXfer_threads]);
+
default:
return -1;
}
@@ -9324,6 +9511,15 @@ remote_set_disconnected_tracing (int val
error (_("Target does not support this command."));
}
+static int
+remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+ if (info && info->private)
+ return info->private->core;
+ return -1;
+}
+
static void
init_remote_ops (void)
{
@@ -9397,6 +9593,7 @@ Specify the serial device it is connecte
remote_ops.to_trace_find = remote_trace_find;
remote_ops.to_get_trace_state_variable_value = remote_get_trace_state_variable_value;
remote_ops.to_set_disconnected_tracing = remote_set_disconnected_tracing;
+ remote_ops.to_core_of_thread = remote_core_of_thread;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -9933,6 +10130,9 @@ Show the maximum size of the address (in
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
"qXfer:osdata:read", "osdata", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
+ "qXfer:threads:read", "threads", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
"qXfer:siginfo:read", "read-siginfo-object", 0);
Index: gdb/target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.236
diff -u -p -r1.236 target.c
--- gdb/target.c 7 Jan 2010 19:17:46 -0000 1.236
+++ gdb/target.c 12 Jan 2010 21:37:15 -0000
@@ -3064,6 +3064,26 @@ target_store_registers (struct regcache
noprocess ();
}
+int
+target_core_of_thread (ptid_t ptid)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_core_of_thread != NULL)
+ {
+ int retval = t->to_core_of_thread (t, ptid);
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
+ PIDGET (ptid), retval);
+ return retval;
+ }
+ }
+
+ return -1;
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
Index: gdb/target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.170
diff -u -p -r1.170 target.h
--- gdb/target.h 7 Jan 2010 19:17:46 -0000 1.170
+++ gdb/target.h 12 Jan 2010 21:37:15 -0000
@@ -256,6 +256,8 @@ enum target_object
/* Extra signal info. Usually the contents of `siginfo_t' on unix
platforms. */
TARGET_OBJECT_SIGNAL_INFO,
+ /* The list of threads that are being debugged. */
+ TARGET_OBJECT_THREADS,
/* Possible future objects: TARGET_OBJECT_FILE, ... */
};
@@ -651,6 +653,14 @@ struct target_ops
disconnection - set VAL to 1 to keep tracing, 0 to stop. */
void (*to_set_disconnected_tracing) (int val);
+ /* Return the processor core that thread PTID was last seen on.
+ This information is updated only when:
+ - update_thread_list is called
+ - thread stops
+ If the core cannot be determined -- either for the specified thread, or
+ right now, or in this debug session, or for this target -- return -1. */
+ int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
+
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@@ -1332,6 +1342,9 @@ extern int target_search_memory (CORE_AD
(*current_target.to_log_command) (p); \
while (0)
+
+extern int target_core_of_thread (ptid_t ptid);
+
/* Routines for maintenance of the target structures...
add_target: Add a target to the list of all possible targets.
Index: gdb/thread.c
===================================================================
RCS file: /cvs/src/src/gdb/thread.c,v
retrieving revision 1.118
diff -u -p -r1.118 thread.c
--- gdb/thread.c 1 Jan 2010 07:31:42 -0000 1.118
+++ gdb/thread.c 12 Jan 2010 21:37:15 -0000
@@ -114,10 +114,13 @@ free_thread (struct thread_info *tp)
{
clear_thread_inferior_resources (tp);
- /* FIXME: do I ever need to call the back-end to give it a
- chance at this private data before deleting the thread? */
if (tp->private)
- xfree (tp->private);
+ {
+ if (tp->private_dtor)
+ tp->private_dtor (tp->private);
+ else
+ xfree (tp->private);
+ }
xfree (tp);
}
@@ -468,8 +471,7 @@ do_captured_list_thread_ids (struct ui_o
struct cleanup *cleanup_chain;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
@@ -748,8 +750,7 @@ print_thread_info (struct ui_out *uiout,
char *extra_info;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
current_ptid = inferior_ptid;
/* We'll be switching threads temporarily. */
@@ -759,6 +760,7 @@ print_thread_info (struct ui_out *uiout,
for (tp = thread_list; tp; tp = tp->next)
{
struct cleanup *chain2;
+ int core;
if (requested_thread != -1 && tp->num != requested_thread)
continue;
@@ -817,6 +819,10 @@ print_thread_info (struct ui_out *uiout,
ui_out_field_string (uiout, "state", state);
}
+ core = target_core_of_thread (tp->ptid);
+ if (ui_out_is_mi_like_p (uiout) && core != -1)
+ ui_out_field_int (uiout, "core", core);
+
do_cleanups (chain2);
}
@@ -1058,8 +1064,7 @@ thread_apply_all_command (char *cmd, int
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
old_chain = make_cleanup_restore_current_thread ();
@@ -1245,6 +1250,13 @@ gdb_thread_select (struct ui_out *uiout,
return GDB_RC_OK;
}
+void
+update_thread_list (void)
+{
+ prune_threads ();
+ target_find_new_threads ();
+}
+
/* Commands with a prefix of `thread'. */
struct cmd_list_element *thread_cmd_list = NULL;
Index: gdb/ui-out.c
===================================================================
RCS file: /cvs/src/src/gdb/ui-out.c,v
retrieving revision 1.44
diff -u -p -r1.44 ui-out.c
--- gdb/ui-out.c 1 Jan 2010 07:31:43 -0000 1.44
+++ gdb/ui-out.c 12 Jan 2010 21:37:15 -0000
@@ -44,7 +44,7 @@ struct ui_out_hdr
is always available. Stack/nested level 0 is reserved for the
top-level result. */
-enum { MAX_UI_OUT_LEVELS = 6 };
+enum { MAX_UI_OUT_LEVELS = 8 };
struct ui_out_level
{
Index: gdb/utils.c
===================================================================
RCS file: /cvs/src/src/gdb/utils.c,v
retrieving revision 1.220
diff -u -p -r1.220 utils.c
--- gdb/utils.c 1 Jan 2010 07:31:43 -0000 1.220
+++ gdb/utils.c 12 Jan 2010 21:37:15 -0000
@@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
return argv;
}
+int
+compare_positive_ints (const void *ap, const void *bp)
+{
+ /* Because we know we're comparing two ints which are positive,
+ there's no danger of overflow here. */
+ return * (int *) ap - * (int *) bp;
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_utils;
Index: gdb/doc/ChangeLog
===================================================================
RCS file: /cvs/src/src/gdb/doc/ChangeLog,v
retrieving revision 1.992
diff -u -p -r1.992 ChangeLog
--- gdb/doc/ChangeLog 6 Jan 2010 20:31:28 -0000 1.992
+++ gdb/doc/ChangeLog 12 Jan 2010 21:37:16 -0000
@@ -1,3 +1,13 @@
+2010-01-13 Vladimir Prus <vladimir@codesourcery.com>
+
+ * gdb.texinfo (GDB/MI Thread Information): New.
+ (GDB/MI Async Records): Document the core field in *stopped.
+ (GDB/MI Miscellaneous Commands): Expand -list-thread-groups
+ documentation
+ (Process list): Document that osdata document may contain
+ threads.
+ (Remote Serial Protocol): Document qXfer:threads.
+
2010-01-06 Stan Shebs <stan@codesourcery.com>
* gdb.texinfo (Starting and Stopping Trace Experiments): Document
Index: gdb/doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.656
diff -u -p -r1.656 gdb.texinfo
--- gdb/doc/gdb.texinfo 6 Jan 2010 20:31:28 -0000 1.656
+++ gdb/doc/gdb.texinfo 12 Jan 2010 21:37:17 -0000
@@ -15542,6 +15542,10 @@ are:
@tab @code{qXfer:siginfo:write}
@tab @code{set $_siginfo}
+@item @code{threads}
+@tab @code{qXfer:threads:read}
+@tab @code{info threads}
+
@item @code{get-thread-local-@*storage-address}
@tab @code{qGetTLSAddr}
@tab Displaying @code{__thread} variables
@@ -21828,6 +21832,7 @@ follow development on @email{gdb@@source
* GDB/MI Stream Records::
* GDB/MI Async Records::
* GDB/MI Frame Information::
+* GDB/MI Thread Information::
@end menu
@node GDB/MI Result Records
@@ -21920,7 +21925,7 @@ several times, either for different thre
all threads together, or even for a single thread, if the thread must
be stepped though some code before letting it run freely.
-@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
+@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
The target has stopped. The @var{reason} field can have one of the
following values:
@@ -21960,7 +21965,9 @@ If all threads are stopped, the @var{sto
value of @code{"all"}. Otherwise, the value of the @var{stopped}
field will be a list of thread identifiers. Presently, this list will
always include a single thread, but frontend should be prepared to see
-several threads in the list.
+several threads in the list. The @var{core} field reports the
+processor core on which the stop event has happened. This field may be absent
+if such information is not available.
@item =thread-group-created,id="@var{id}"
@itemx =thread-group-exited,id="@var{id}"
@@ -22037,6 +22044,34 @@ corresponds to the frame's code address.
@end table
+@node GDB/MI Thread Information
+@subsection @sc{gdb/mi} Thread Information
+
+Whenever @value{GDBN} has to report an information about a thread, it
+uses a tuple with the following fields:
+
+@table @code
+@item id
+The numeric id assigned to the thread by @value{GDBN}. This field is
+always present.
+
+@item target-id
+Target-specific string identifying the thread. This field is always present.
+
+@item details
+Additional information about the thread provided by the target.
+It is supposed to be human-readable and not interpreted by the
+frontend. This field is optional.
+
+@item state
+Either @samp{stopped} or @samp{running}, depending on whether the
+thread is presently running. This field is always present.
+
+@item core
+The value of this field is an integer number of the processor core the
+thread was last seen on. This field is optional.
+@end table
+
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Simple Examples
@@ -26349,20 +26384,84 @@ while the target is running.
@subheading Synopsis
@smallexample
--list-thread-groups [ --available ] [ @var{group} ]
+-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
@end smallexample
-When used without the @var{group} parameter, lists top-level thread
-groups that are being debugged. When used with the @var{group}
-parameter, the children of the specified group are listed. The
-children can be either threads, or other groups. At present,
-@value{GDBN} will not report both threads and groups as children at
-the same time, but it may change in future.
-
-With the @samp{--available} option, instead of reporting groups that
-are been debugged, GDB will report all thread groups available on the
-target. Using the @samp{--available} option together with @var{group}
-is not allowed.
+Lists thread groups (@pxref{Thread groups}). When a single thread
+group is passed as the argument, lists the children of that group.
+When several thread group are passed, lists information about those
+thread groups. Without any parameters, lists information about all
+top-level thread groups.
+
+Normally, thread groups that are being debugged are reported.
+With the @samp{--available} option, @value{GDBN} reports thread groups
+available on the target.
+
+The output of this command may have either a @samp{threads} result or
+a @samp{groups} result. The @samp{thread} result has a list of tuples
+as value, with each tuple describing a thread (@pxref{GDB/MI Thread
+Information}). The @samp{groups} result has a list of tuples as value,
+each tuple describing a thread group. If top-level groups are
+requested (that is, no parameter is passed), or when several groups
+are passed, the output always has a @samp{groups} result. The format
+of the @samp{group} result is described below.
+
+To reduce the number of roundtrips it's possible to list thread groups
+together with their children, by passing the @samp{--recurse} option
+and the recursion depth. Presently, only recursion depth of 1 is
+permitted. If this option is present, then every reported thread group
+will also include its children, either as @samp{group} or
+@samp{threads} field.
+
+In general, any combination of option and parameters is permitted, with
+the following caveats:
+
+@itemize @bullet
+@item
+When a single thread group is passed, the output will typically
+be the @samp{threads} result. Because threads may not contain
+anything, the @samp{recurse} option will be ignored.
+
+@item
+When the @samp{--available} option is passed, limited information may
+be available. In particular, the list of threads of a process might
+be inaccessible. Further, specifying specific thread groups might
+not give any performance advantage over listing all thread groups.
+The frontend should assume that @samp{-list-thread-groups --available}
+is always an expensive operation and cache the results.
+
+@end itemize
+
+The @samp{groups} result is a list of tuples, where each tuple may
+have the following fields:
+
+@table @code
+@item id
+Identifier of the thread group. This field is always present.
+
+@item type
+The type of the thread group. At present, only @samp{process} is a
+valid type.
+
+@item pid
+The target-specific process identifier. This field is only present
+for thread groups of type @samp{process}.
+
+@item num_children
+The number of children this thread group has. This field may be
+absent for an available thread group.
+
+@item threads
+This field has a list of tuples as value, each tuple describing a
+thread. It may be present if the @samp{--recurse} option is
+specified, and it's actually possible to obtain the threads.
+
+@item cores
+This field is a list of integers, each identifying a core that one
+thread of the group is running on. This field may be absent if
+such information is not available.
+
+@end table
@subheading Example
@@ -26376,6 +26475,16 @@ is not allowed.
@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
+-list-thread-groups --available
+^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
+-list-thread-groups --available --recurse 1
+ ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
+-list-thread-groups --available --recurse 1 17 18
+^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
@end smallexample
@subheading The @code{-interpreter-exec} Command
@@ -28125,6 +28234,7 @@ Show the current setting of the target w
* File-I/O Remote Protocol Extension::
* Library List Format::
* Memory Map Format::
+* Thread List Format::
@end menu
@node Overview
@@ -29025,6 +29135,10 @@ If @var{n} is @samp{thread}, then @var{r
the stopped thread, as specified in @ref{thread-id syntax}.
@item
+If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
+the core on which the stop event was detected.
+
+@item
If @var{n} is a recognized @dfn{stop reason}, it describes a more
specific event that stopped the target. The currently defined stop
reasons are listed below. @var{aa} should be @samp{05}, the trap
@@ -29058,8 +29172,6 @@ logged execution events, because it has
beginning when executing backward) of the log. The value of @var{r}
will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
for more information.
-
-
@end table
@item W @var{AA}
@@ -29599,6 +29711,12 @@ These are the currently defined stub fea
@tab @samp{-}
@tab Yes
+@item @samp{qXfer:threads:read}
+@tab No
+@tab @samp{-}
+@tab Yes
+
+
@item @samp{QNonStop}
@tab No
@tab @samp{-}
@@ -29682,6 +29800,10 @@ The remote stub understands the @samp{qX
The remote stub understands the @samp{qXfer:siginfo:write} packet
(@pxref{qXfer siginfo write}).
+@item qXfer:threads:read
+The remote stub understands the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}).
+
@item QNonStop
The remote stub understands the @samp{QNonStop} packet
(@pxref{QNonStop}).
@@ -29879,6 +30001,15 @@ This packet is not probed by default; th
by supplying an appropriate @samp{qSupported} response
(@pxref{qSupported}).
+@item qXfer:threads:read::@var{offset},@var{length}
+@anchor{qXfer threads read}
+Access the list of threads on target. @xref{Thread List Format}. The
+annex part of the generic @samp{qXfer} packet must be empty
+(@pxref{qXfer read}).
+
+This packet is not probed by default; the remote stub must request it,
+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}.
@@ -31909,6 +32040,30 @@ The formal DTD for memory map format is
<!ATTLIST property name CDATA #REQUIRED>
@end smallexample
+@node Thread List Format
+@section Thread List Format
+@cindex thread list format
+
+To efficiently update the list of threads and their attributes,
+@value{GDBN} issues the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}) and obtains the XML document with
+the following structure:
+
+@smallexample
+<?xml version="1.0"?>
+<threads>
+ <thread id="id" core="0">
+ ... description ...
+ </thread>
+</threads>
+@end smallexample
+
+Each @samp{thread} element must have the @samp{id} attribute that
+identifies the thread (@pxref{thread-id syntax}). The
+@samp{core} attribute, if present, specifies which processor core
+the thread was last executing on. The content of the of @samp{thread}
+element is interpreted as human-readable auxilliary information.
+
@include agentexpr.texi
@node Target Descriptions
@@ -32468,6 +32623,7 @@ An example document is:
<column name="pid">1</column>
<column name="user">root</column>
<column name="command">/sbin/init</column>
+ <column name="cores">1,2,3</column>
</item>
</osdata>
@end smallexample
@@ -32475,7 +32631,9 @@ An example document is:
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,
+displayed by @value{GDBN}. The @samp{cores} column, if present,
+should contain a comma-separated list of cores that this process
+is running on. Target may provide additional columns,
which @value{GDBN} currently ignores.
@include gpl.texi
Index: gdb/gdbserver/ChangeLog
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/ChangeLog,v
retrieving revision 1.316
diff -u -p -r1.316 ChangeLog
--- gdb/gdbserver/ChangeLog 4 Jan 2010 15:03:00 -0000 1.316
+++ gdb/gdbserver/ChangeLog 12 Jan 2010 21:37:17 -0000
@@ -1,3 +1,17 @@
+2010-01-13 Vladimir Prus <vladimir@codesourcery.com>
+
+ * linux-low.c (linux_core_of_thread): New.
+ (compare_ints, show_process, list_threads): New.
+ (linux_qxfer_osdata): Report threads and cores.
+ (linux_target_op): Register linux_core_of_thread.
+ * remote-utils.c (prepare_resume_reply): Report the core.
+ (buffer_xml_printf): Support %d specifier.
+ * server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
+ New.
+ (handle_query): Handle qXfer:threads. Announce availability
+ thereof.
+ * target.h (struct target_ops): New field core_of_thread.
+
2010-01-04 Ulrich Weigand <uweigand@de.ibm.com>
* Makefile.in (clean): Remove new generated files.
Index: gdb/gdbserver/linux-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v
retrieving revision 1.121
diff -u -p -r1.121 linux-low.c
--- gdb/gdbserver/linux-low.c 1 Jan 2010 07:31:49 -0000 1.121
+++ gdb/gdbserver/linux-low.c 12 Jan 2010 21:37:17 -0000
@@ -140,6 +140,7 @@ static int check_removed_breakpoint (str
static void *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int linux_core_of_thread (ptid_t ptid);
struct pending_signals
{
@@ -2802,6 +2803,175 @@ linux_read_offsets (CORE_ADDR *text_p, C
#endif
static int
+compare_ints (const void *xa, const void *xb)
+{
+ int a = *(const int *)xa;
+ int b = *(const int *)xb;
+
+ return a - b;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+/* Given PID, iterates over all threads in that process.
+
+ Information about each thread, in a format suitable for qXfer:osdata:thread
+ is printed to BUFFER, if it's not NULL. BUFFER is assumed to be already
+ initialized, and the caller is responsible for finishing and appending '\0'
+ to it.
+
+ The list of cores that threads are running on is assigned to *CORES, if it
+ is not NULL. If no cores are found, *CORES will be set to NULL. Caller
+ should free *CORES. */
+
+static void
+list_threads (int pid, struct buffer *buffer, char **cores)
+{
+ int count = 0;
+ int allocated = 10;
+ int *core_numbers = xmalloc (sizeof (int) * allocated);
+ char pathname[128];
+ DIR *dir;
+ struct dirent *dp;
+ struct stat statbuf;
+
+ sprintf (pathname, "/proc/%d/task", pid);
+ if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+ {
+ dir = opendir (pathname);
+ if (!dir)
+ {
+ free (core_numbers);
+ return;
+ }
+
+ while ((dp = readdir (dir)) != NULL)
+ {
+ unsigned long lwp = strtoul (dp->d_name, NULL, 10);
+
+ if (lwp != 0)
+ {
+ unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
+
+ if (core != -1)
+ {
+ char s[sizeof ("4294967295")];
+ sprintf (s, "%u", core);
+
+ if (count == allocated)
+ {
+ allocated *= 2;
+ core_numbers = realloc (core_numbers,
+ sizeof (int) * allocated);
+ }
+ core_numbers[count++] = core;
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "<column name=\"core\">%s</column>"
+ "</item>", pid, dp->d_name, s);
+ }
+ else
+ {
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "</item>", pid, dp->d_name);
+ }
+ }
+ }
+ }
+
+ if (cores)
+ {
+ *cores = NULL;
+ if (count > 0)
+ {
+ struct buffer buffer2;
+ int *b;
+ int *e;
+ qsort (core_numbers, count, sizeof (int), compare_ints);
+
+ /* Remove duplicates. */
+ b = core_numbers;
+ e = unique (b, core_numbers + count);
+
+ buffer_init (&buffer2);
+
+ for (b = core_numbers; b != e; ++b)
+ {
+ char number[sizeof ("4294967295")];
+ sprintf (number, "%u", *b);
+ buffer_xml_printf (&buffer2, "%s%s",
+ (b == core_numbers) ? "" : ",", number);
+ }
+ buffer_grow_str0 (&buffer2, "");
+
+ *cores = buffer_finish (&buffer2);
+ }
+ }
+ free (core_numbers);
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+ char pathname[128];
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+
+ sprintf (pathname, "/proc/%d/cmdline", pid);
+
+ if ((f = fopen (pathname, "r")) != NULL)
+ {
+ size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+ if (len > 0)
+ {
+ char *cores = 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\">%d</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>",
+ pid,
+ username,
+ cmd);
+
+ /* This only collects core numbers, and does not print threads. */
+ list_threads (pid, NULL, &cores);
+
+ if (cores)
+ {
+ buffer_xml_printf (buffer,
+ "<column name=\"cores\">%s</column>", cores);
+ free (cores);
+ }
+
+ buffer_xml_printf (buffer, "</item>");
+ }
+ fclose (f);
+ }
+}
+
+static int
linux_qxfer_osdata (const char *annex,
unsigned char *readbuf, unsigned const char *writebuf,
CORE_ADDR offset, int len)
@@ -2811,10 +2981,16 @@ linux_qxfer_osdata (const char *annex,
static const char *buf;
static long len_avail = -1;
static struct buffer buffer;
+ int processes = 0;
+ int threads = 0;
DIR *dirp;
- if (strcmp (annex, "processes") != 0)
+ if (strcmp (annex, "processes") == 0)
+ processes = 1;
+ else if (strcmp (annex, "threads") == 0)
+ threads = 1;
+ else
return 0;
if (!readbuf || writebuf)
@@ -2827,7 +3003,10 @@ linux_qxfer_osdata (const char *annex,
len_avail = 0;
buf = NULL;
buffer_init (&buffer);
- buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ if (processes)
+ buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ else if (threads)
+ buffer_grow_str (&buffer, "<osdata type=\"threads\">");
dirp = opendir ("/proc");
if (dirp)
@@ -2846,37 +3025,16 @@ linux_qxfer_osdata (const char *annex,
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);
+ int pid = (int) strtoul (dp->d_name, NULL, 10);
- if ((f = fopen (pathname, "r")) != NULL)
+ if (processes)
{
- 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);
+ struct passwd *entry = getpwuid (statbuf.st_uid);
+ show_process (pid, entry ? entry->pw_name : "?", &buffer);
+ }
+ else if (threads)
+ {
+ list_threads (pid, &buffer, NULL);
}
}
}
@@ -3152,6 +3310,55 @@ linux_qxfer_spu (const char *annex, unsi
return ret;
}
+static int
+linux_core_of_thread (ptid_t ptid)
+{
+ char filename[sizeof ("/proc//task//stat")
+ + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
+ + 1];
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ sprintf (filename, "/proc/%d/task/%ld/stat",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid));
+ f = fopen (filename, "r");
+ if (!f)
+ return -1;
+
+ for (;;)
+ {
+ int n;
+ content = realloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ free (content);
+ fclose (f);
+
+ return core;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -3191,10 +3398,11 @@ static struct target_ops linux_target_op
linux_start_non_stop,
linux_supports_multi_process,
#ifdef USE_THREAD_DB
- thread_db_handle_monitor_command
+ thread_db_handle_monitor_command,
#else
- NULL
+ NULL,
#endif
+ linux_core_of_thread
};
static void
Index: gdb/gdbserver/remote-utils.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/remote-utils.c,v
retrieving revision 1.69
diff -u -p -r1.69 remote-utils.c
--- gdb/gdbserver/remote-utils.c 1 Jan 2010 07:31:49 -0000 1.69
+++ gdb/gdbserver/remote-utils.c 12 Jan 2010 21:37:17 -0000
@@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t
gdbserver to know what inferior_ptid is. */
if (1 || !ptid_equal (general_thread, ptid))
{
+ int core = -1;
/* In non-stop, don't change the general thread behind
GDB's back. */
if (!non_stop)
@@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t
buf = write_ptid (buf, ptid);
strcat (buf, ";");
buf += strlen (buf);
+
+ if (the_target->core_of_thread)
+ core = (*the_target->core_of_thread) (ptid);
+ if (core != -1)
+ {
+ sprintf (buf, "core:");
+ buf += strlen (buf);
+ sprintf (buf, "%x", core);
+ strcat (buf, ";");
+ buf += strlen (buf);
+ }
}
}
@@ -1604,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer
prev = f + 1;
}
break;
+ case 'd':
+ {
+ int i = va_arg (ap, int);
+ char b[sizeof ("4294967295")];
+
+ buffer_grow (buffer, prev, f - prev - 1);
+ sprintf (b, "%d", i);
+ buffer_grow_str (buffer, b);
+ prev = f + 1;
+ }
}
percent = 0;
}
Index: gdb/gdbserver/server.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.c,v
retrieving revision 1.106
diff -u -p -r1.106 server.c
--- gdb/gdbserver/server.c 1 Jan 2010 07:31:49 -0000 1.106
+++ gdb/gdbserver/server.c 12 Jan 2010 21:37:17 -0000
@@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
}
}
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+ struct inferior_list_entry *thread;
+
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for (thread = all_threads.head; thread; thread = thread->next)
+ {
+ ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+ char ptid_s[100];
+ int core = -1;
+ char core_s[21];
+
+ write_ptid (ptid_s, ptid);
+
+ if (the_target->core_of_thread)
+ core = (*the_target->core_of_thread) (ptid);
+
+ if (core != -1)
+ {
+ sprintf (core_s, "%d", core);
+ buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+ ptid_s, core_s);
+ }
+ else
+ {
+ buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+ ptid_s);
+ }
+ }
+
+ buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+ unsigned char *readbuf,
+ CORE_ADDR offset, int length)
+{
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (annex && strcmp (annex, "") != 0)
+ return 0;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+ /* When asked for data at offset 0, generate everything and store into
+ 'result'. Successive reads will be served off 'result'. */
+ if (result)
+ free (result);
+
+ buffer_init (&buffer);
+
+ handle_threads_qxfer_proper (&buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (length > result_length - offset)
+ length = result_length - offset;
+
+ memcpy (readbuf, result + offset, length);
+
+ return length;
+
+}
+
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_
return;
}
+ if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+ {
+ unsigned char *data;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+ char *annex;
+
+ require_running (own_buf);
+
+ /* Reject any annex; grab the offset and length. */
+ if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+ || annex[0] != '\0')
+ {
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ /* Read one extra byte, as an indicator of whether there is
+ more. */
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ data = malloc (len + 1);
+ if (!data)
+ return;
+ n = handle_threads_qxfer (annex, data, ofs, len + 1);
+ if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
+ strcat (own_buf, ";qXfer:threads:read+");
+
return;
}
Index: gdb/gdbserver/target.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/target.h,v
retrieving revision 1.41
diff -u -p -r1.41 target.h
--- gdb/gdbserver/target.h 1 Jan 2010 07:31:49 -0000 1.41
+++ gdb/gdbserver/target.h 12 Jan 2010 21:37:17 -0000
@@ -283,6 +283,9 @@ struct target_ops
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
+
+ /* Returns the core given a thread, or -1 if not known. */
+ int (*core_of_thread) (ptid_t);
};
extern struct target_ops *the_target;
Index: gdb/mi/mi-interp.c
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-interp.c,v
retrieving revision 1.53
diff -u -p -r1.53 mi-interp.c
--- gdb/mi/mi-interp.c 1 Jan 2010 07:31:50 -0000 1.53
+++ gdb/mi/mi-interp.c 12 Jan 2010 21:37:17 -0000
@@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, i
if (print_frame)
{
+ int core;
if (uiout != mi_uiout)
{
/* The normal_stop function has printed frame information into
@@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, i
}
else
ui_out_field_string (mi_uiout, "stopped-threads", "all");
+
+ core = target_core_of_thread (inferior_ptid);
+ if (core != -1)
+ ui_out_field_int (mi_uiout, "core", core);
}
fputs_unfiltered ("*stopped", raw_stdout);
Index: gdb/mi/mi-main.c
===================================================================
RCS file: /cvs/src/src/gdb/mi/mi-main.c,v
retrieving revision 1.161
diff -u -p -r1.161 mi-main.c
--- gdb/mi/mi-main.c 1 Jan 2010 07:31:50 -0000 1.161
+++ gdb/mi/mi-main.c 12 Jan 2010 21:37:17 -0000
@@ -50,6 +50,7 @@
#include "valprint.h"
#include "inferior.h"
#include "osdata.h"
+#include "splay-tree.h"
#include <ctype.h>
#include <sys/time.h>
@@ -360,11 +361,55 @@ mi_cmd_thread_info (char *command, char
print_thread_info (uiout, thread, -1);
}
+struct collect_cores_data
+{
+ int pid;
+
+ VEC (int) *cores;
+};
+
static int
-print_one_inferior (struct inferior *inferior, void *arg)
+collect_cores (struct thread_info *ti, void *xdata)
{
- if (inferior->pid != 0)
+ struct collect_cores_data *data = xdata;
+
+ if (ptid_get_pid (ti->ptid) == data->pid)
{
+ int core = target_core_of_thread (ti->ptid);
+ if (core != -1)
+ VEC_safe_push (int, data->cores, core);
+ }
+
+ return 0;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+struct print_one_inferior_data
+{
+ int recurse;
+ VEC (int) *inferiors;
+};
+
+static int
+print_one_inferior (struct inferior *inferior, void *xdata)
+{
+ struct print_one_inferior_data *top_data = xdata;
+
+ if (VEC_empty (int, top_data->inferiors)
+ || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
+ VEC_length (int, top_data->inferiors), sizeof (int),
+ compare_positive_ints))
+ {
+ struct collect_cores_data data;
struct cleanup *back_to
= make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -372,81 +417,299 @@ print_one_inferior (struct inferior *inf
ui_out_field_string (uiout, "type", "process");
ui_out_field_int (uiout, "pid", inferior->pid);
+ data.pid = inferior->pid;
+ data.cores = 0;
+ iterate_over_threads (collect_cores, &data);
+
+ if (!VEC_empty (int, data.cores))
+ {
+ int elt;
+ int i;
+ int *b, *e;
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_list_begin_end (uiout, "cores");
+
+ qsort (VEC_address (int, data.cores),
+ VEC_length (int, data.cores), sizeof (int),
+ compare_positive_ints);
+
+ b = VEC_address (int, data.cores);
+ e = b + VEC_length (int, data.cores);
+ e = unique (b, e);
+
+ for (; b != e; ++b)
+ ui_out_field_int (uiout, NULL, *b);
+
+ do_cleanups (back_to_2);
+ }
+
+ if (top_data->recurse)
+ print_thread_info (uiout, -1, inferior->pid);
+
do_cleanups (back_to);
}
return 0;
}
-void
-mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+/* Output a field named 'cores' with a list as the value. The elements of
+ the list are obtained by splitting 'cores' on comma. */
+
+static void
+output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
{
- struct cleanup *back_to;
- int available = 0;
- char *id = NULL;
+ struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+ field_name);
+ char *cores = xstrdup (xcores);
+ char *p = cores;
- if (argc > 0 && strcmp (argv[0], "--available") == 0)
- {
- ++argv;
- --argc;
- available = 1;
- }
+ make_cleanup (xfree, cores);
- if (argc > 0)
- id = argv[0];
+ for (p = strtok (p, ","); p; p = strtok (NULL, ","))
+ ui_out_field_string (uiout, NULL, p);
- back_to = make_cleanup (null_cleanup, NULL);
+ do_cleanups (back_to);
+}
- if (available && id)
- {
- error (_("Can only report top-level available thread groups"));
- }
- else if (available)
- {
- struct osdata *data;
- struct osdata_item *item;
- int ix_items;
+static void
+free_vector_of_ints (void *xvector)
+{
+ VEC (int) **vector = xvector;
+ VEC_free (int, *vector);
+}
- data = get_osdata ("processes");
- make_cleanup_osdata_free (data);
+static void
+do_nothing (splay_tree_key k)
+{
+}
- make_cleanup_ui_out_list_begin_end (uiout, "groups");
+static void
+free_vector_of_osdata_items (splay_tree_value xvalue)
+{
+ VEC (osdata_item_s) *value = (VEC (osdata_item_s) *) xvalue;
+ /* We don't free the items itself, it will be done separately. */
+ VEC_free (osdata_item_s, value);
+}
+
+static int
+splay_tree_int_comparator (splay_tree_key xa, splay_tree_key xb)
+{
+ int a = xa;
+ int b = xb;
+ return a - b;
+}
+
+static void
+free_splay_tree (void *xt)
+{
+ splay_tree t = xt;
+ splay_tree_delete (t);
+}
+
+static void
+list_available_thread_groups (VEC (int) *ids, int recurse)
+{
+ struct osdata *data;
+ struct osdata_item *item;
+ int ix_items;
+ /* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
+ The vector contains information about all threads for the given
+ pid. */
+ splay_tree tree;
+
+ /* get_osdata will throw if it cannot return data. */
+ data = get_osdata ("processes");
+ make_cleanup_osdata_free (data);
+
+ if (recurse)
+ {
+ struct osdata *threads = get_osdata ("threads");
+ make_cleanup_osdata_free (threads);
+
+ tree = splay_tree_new (splay_tree_int_comparator,
+ do_nothing,
+ free_vector_of_osdata_items);
+ make_cleanup (free_splay_tree, tree);
for (ix_items = 0;
- VEC_iterate (osdata_item_s, data->items,
+ VEC_iterate (osdata_item_s, threads->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");
+ int pid_i = strtoul (pid, NULL, 0);
+ VEC (osdata_item_s) *vec = 0;
+
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (!n)
+ {
+ VEC_safe_push (osdata_item_s, vec, item);
+ splay_tree_insert (tree, pid_i, (splay_tree_value)vec);
+ }
+ else
+ {
+ vec = (VEC (osdata_item_s) *) n->value;
+ VEC_safe_push (osdata_item_s, vec, item);
+ n->value = (splay_tree_value) vec;
+ }
+ }
+ }
+
+ 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;
+
+ const char *pid = get_osdata_column (item, "pid");
+ const char *cmd = get_osdata_column (item, "command");
+ const char *user = get_osdata_column (item, "user");
+ const char *cores = get_osdata_column (item, "cores");
+
+ int pid_i = strtoul (pid, NULL, 0);
+
+ /* At present, the target will return all available processes
+ and if information about specific ones was required, we filter
+ undesired processes here. */
+ if (ids && bsearch (&pid_i, VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints) == NULL)
+ continue;
+
+
+ back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ 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);
+ if (cores)
+ output_cores (uiout, "cores", cores);
+
+ if (recurse)
+ {
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (n)
+ {
+ VEC (osdata_item_s) *children = (VEC (osdata_item_s) *) n->value;
+ struct osdata_item *child;
+ int ix_child;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+ for (ix_child = 0;
+ VEC_iterate (osdata_item_s, children, ix_child, child);
+ ++ix_child)
+ {
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *tid = get_osdata_column (child, "tid");
+ const char *tcore = get_osdata_column (child, "core");
+ ui_out_field_string (uiout, "id", tid);
+ if (tcore)
+ ui_out_field_string (uiout, "core", tcore);
+
+ do_cleanups (back_to_2);
+ }
+ }
+ }
+
+ do_cleanups (back_to);
+ }
+}
+
+void
+mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+{
+ struct cleanup *back_to;
+ int available = 0;
+ int recurse = 0;
+ VEC (int) *ids = 0;
+
+ enum opt
+ {
+ AVAILABLE_OPT, RECURSE_OPT
+ };
+ static struct mi_opt opts[] =
+ {
+ {"-available", AVAILABLE_OPT, 0},
+ {"-recurse", RECURSE_OPT, 1},
+ { 0, 0, 0 }
+ };
- 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);
+ int optind = 0;
+ char *optarg;
- do_cleanups (back_to);
+ while (1)
+ {
+ int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+ &optind, &optarg);
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case AVAILABLE_OPT:
+ available = 1;
+ break;
+ case RECURSE_OPT:
+ if (strcmp (optarg, "0") == 0)
+ ;
+ else if (strcmp (optarg, "1") == 0)
+ recurse = 1;
+ else
+ error ("only '0' and '1' are valid values for the '--recurse' option");
+ break;
}
}
- else if (id)
+
+ for (; optind < argc; ++optind)
{
- int pid = atoi (id);
+ char *end;
+ int inf = strtoul (argv[optind], &end, 0);
+ if (*end != '\0')
+ error ("invalid group id '%s'", argv[optind]);
+ VEC_safe_push (int, ids, inf);
+ }
+ if (VEC_length (int, ids) > 1)
+ qsort (VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints);
+
+ back_to = make_cleanup (free_vector_of_ints, &ids);
+
+ if (available)
+ {
+ list_available_thread_groups (ids, recurse);
+ }
+ else if (VEC_length (int, ids) == 1)
+ {
+ /* Local thread groups, single id. */
+ int pid = *VEC_address (int, ids);
if (!in_inferior_list (pid))
- error ("Invalid thread group id '%s'", id);
- print_thread_info (uiout, -1, pid);
+ error ("Invalid thread group id '%d'", pid);
+ print_thread_info (uiout, -1, pid);
}
else
{
+ struct print_one_inferior_data data;
+ data.recurse = recurse;
+ data.inferiors = ids;
+
+ /* Local thread groups. Either no explicit ids -- and we
+ print everything, or several explicit ids. In both cases,
+ we print more than one group, and have to use 'groups'
+ as the top-level element. */
make_cleanup_ui_out_list_begin_end (uiout, "groups");
- iterate_over_inferiors (print_one_inferior, NULL);
+ update_thread_list ();
+ iterate_over_inferiors (print_one_inferior, &data);
}
-
+
do_cleanups (back_to);
}
Index: gdb/testsuite/ChangeLog
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/ChangeLog,v
retrieving revision 1.2080
diff -u -p -r1.2080 ChangeLog
--- gdb/testsuite/ChangeLog 12 Jan 2010 05:49:00 -0000 1.2080
+++ gdb/testsuite/ChangeLog 12 Jan 2010 21:37:19 -0000
@@ -1,3 +1,8 @@
+2010-01-13 Vladimir Prus <vladimir@codesourcery.com>
+
+ * lib/mi-support.exp (mi_check_thread_states): Handle
+ core number in thread listing.
+
2010-01-12 Joel Brobecker <brobecker@adacore.com>
* gdb.base/maint.exp: Adjust the expected output for the
Index: gdb/testsuite/lib/mi-support.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/lib/mi-support.exp,v
retrieving revision 1.89
diff -u -p -r1.89 mi-support.exp
--- gdb/testsuite/lib/mi-support.exp 1 Jan 2010 07:57:56 -0000 1.89
+++ gdb/testsuite/lib/mi-support.exp 12 Jan 2010 21:37:19 -0000
@@ -1877,7 +1877,7 @@ proc mi_check_thread_states { xstates te
foreach s $states {
set pattern "${pattern}(.*)state=\"$s\""
}
- set pattern "$pattern\\\}\\\].*"
+ set pattern "${pattern}(,core=\"\[0-9\]*\")?\\\}\\\].*"
verbose -log "expecting: $pattern"
mi_gdb_test "-thread-info" $pattern $test
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-11 21:01 ` Vladimir Prus
@ 2010-01-11 21:22 ` Pedro Alves
2010-01-12 21:44 ` Vladimir Prus
0 siblings, 1 reply; 17+ messages in thread
From: Pedro Alves @ 2010-01-11 21:22 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
On Monday 11 January 2010 21:01:08, Vladimir Prus wrote:
> Implement core awareness.
>
> gdb/
> * bcache.c (compare_ints): Remove
> (print_percentage): Use compare_positive_ints.
>
> * defs.h (compare_positive_ints): Declare.
> * linux-nat.h (struct lin_lwp): New field core.
> (linux_nat_core_of_thread_1): Declare.
> * linux-nat.c (add_lwp): Init the 'core' field.
> (linux_nat_wait_1): Record the core.
> (linux_nat_core_of_thread_1, linux_nat_core_of_thread): New.
> (linux_nat_add_target): Register the above.
> * linux-thread-db.c (update_thread_core): New.
> (thread_db_find_new_threads): Update core information for
> every thread.
> * remote.c (struct private_thread_info): New.
> (free_private_thread_info, demand_private_info): New.
> (PACKET_qXfer_threads, use_osdata_threads): New.
> (struct thread_item, threads_parsing_context
> (start_thread, end_thread, thread_attributes)
> (thread_children, threads_children, threads_elements): New.
> (remote_threads_info): Try qXfer:threads before anything
> else.
> (remote_protocol_packets): Register qXfer:threads.
> (remote_open_1): Init use_osdata_threads.
> (struct stop_reply): New field 'core'.
> (remote_parse_stop_reply): Parse core number.
> (process_stop_reply): Record core number.
> (remote_xfer_partial): Handle qXfer:threads.
> (remote_core_of_thread): New.
> (init_remote_ops): Register remote_core_of_thread.
> (_initialize_remote): Register qXfer:read.
> * target.c (target_core_of_thread): New
> * target.h (enum target_object): New value TARGET_OBJECT_THREADS.
> (struct target_ops): New field to_core_of_threads.
> (target_core_of_thread): Declare.
> * gdbthread.h (struct thread_info): New field private_dtor.
> * thread.c (print_thread_info): Report the core.
> * ui-out.c (MAX_UI_OUT_LEVELS): Increase.
> * utils.c (compare_positive_ints): New.
> * features/threads.dtd: New.
> * mi/mi-interp.c (mi_on_normal_stop): Report the core.
> * mi/mi-main.c (struct collect_cores_data, collect_cores)
> (do_nothing, free_vector_of_osdata_items)
> (splay_tree_int_comparator, free_splay_tree): New.
> (print_one_inferior_data): Implemented printing of selected
> inferiors. Collect and print cores.
> (output_cores): New.
> (mi_cmd_list_thread_groups): Support --recurse. Permit specifying
> thread groups together with --available.
>
> gdbserver/
> * linux-low.c (linux_core_of_thread): New.
> (compare_ints, show_process, list_threads): New.
> (linux_qxfer_osdata): Report threads and cores.
> (linux_target_op): Register linux_core_of_thread.
> * remote-utils.c (prepare_resume_reply): Report the core.
> (buffer_xml_printf): Support %d specifier.
> * server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
> New.
> (handle_query): Handle qXfer:threads. Announce availability
> thereof.
> * target.h (struct target_ops): New field core_of_thread.
This version is OK. Thanks.
--
Pedro Alves
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-11 14:17 ` Pedro Alves
@ 2010-01-11 21:01 ` Vladimir Prus
2010-01-11 21:22 ` Pedro Alves
0 siblings, 1 reply; 17+ messages in thread
From: Vladimir Prus @ 2010-01-11 21:01 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: Text/Plain, Size: 1695 bytes --]
On Monday 11 January 2010 17:17:16 Pedro Alves wrote:
> > - Introduce thread_get_core function in thread.c. That thread will
> > be documented as returning the core a given thread was last noticed
> > running on by GDB, with that information updated either by
> > update_thread_list or when a thread stops. We'd need a new field
> > inside thread_info to keep this information.
> > - Make linux native update core information for all threads when
> > listing them.
>
> Sounds good to me. Note that linux-nat.c doesn't list threads,
> linux-thread-db.c does.
>
> > What I am primarily trying to avoid is introducing a packet to get
> > core information for a single running thread. That will be slow,
> > and such functionality is not required by anything at present.
>
> That's fine. My gripe is in the disconnect between the
> documentation of what the method should do, and what it
> actually does.
Here's the patch revised per above, and offlist discussion. For the benefit
of list, the problem with the above scheme is that linux_nat_wait seems
like the best place to record core, however, a new thread is only added to
the thread list by linux_thread_db_wait, and while in practice, a thread
will always stop on thread breakpoint and before stopping for real, and
therefore will be present in the thread table when linux_nat_wait wishes
to record core number, such design is brittle. So, this revision makes
linux native behave similarly to remote, and adjust documentation on
the to_core_of_thread method to match that behaviour.
This patch also fixes a few formatting issues that whitespace-mode found,
and adjust naming of some functions to be consistent.
- Volodya
[-- Attachment #2: delta.diff --]
[-- Type: text/x-patch, Size: 6072 bytes --]
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index d638a3f..76953ae 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1181,8 +1181,8 @@ prepare_resume_reply (char *buf, ptid_t ptid,
strcat (buf, ";");
buf += strlen (buf);
- if (the_target->core_for_thread)
- core = (*the_target->core_for_thread) (ptid);
+ if (the_target->core_of_thread)
+ core = (*the_target->core_of_thread) (ptid);
if (core != -1)
{
sprintf (buf, "core:");
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index dfc92a8..c781552 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -723,8 +723,8 @@ handle_threads_qxfer_proper (struct buffer *buffer)
write_ptid (ptid_s, ptid);
- if (the_target->core_for_thread)
- core = (*the_target->core_for_thread) (ptid);
+ if (the_target->core_of_thread)
+ core = (*the_target->core_of_thread) (ptid);
if (core != -1)
{
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 0ee0d41..0c761e9 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -285,7 +285,7 @@ struct target_ops
int (*handle_monitor_command) (char *);
/* Returns the core given a thread, or -1 if not known. */
- int (*core_for_thread) (ptid_t);
+ int (*core_of_thread) (ptid_t);
};
extern struct target_ops *the_target;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 717f78c..bf0a5f1 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1184,6 +1184,7 @@ add_lwp (ptid_t ptid)
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
lp->ptid = ptid;
+ lp->core = -1;
lp->next = lwp_list;
lwp_list = lp;
@@ -3642,6 +3643,7 @@ retry:
fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
restore_child_signals_mask (&prev_mask);
+ lp->core = linux_nat_core_of_thread_1 (lp->ptid);
return lp->ptid;
}
@@ -5423,10 +5425,8 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
return inf->aspace;
}
-/* Return the core for a thread. */
-
-static int
-linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+int
+linux_nat_core_of_thread_1 (ptid_t ptid)
{
struct cleanup *back_to;
char *filename;
@@ -5483,6 +5483,17 @@ linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
return core;
}
+/* Return the cached value of the processor core for thread PTID. */
+
+int
+linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct lwp_info *info = find_lwp_pid (ptid);
+ if (info)
+ return info->core;
+ return -1;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index 9537740..5aba089 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -89,6 +89,9 @@ struct lwp_info
- TARGET_WAITKIND_SYSCALL_RETURN */
int syscall_state;
+ /* The processor core this LWP was last seen on. */
+ int core;
+
/* Next LWP in list. */
struct lwp_info *next;
};
@@ -163,3 +166,6 @@ void linux_nat_switch_fork (ptid_t new_ptid);
/* Return the saved siginfo associated with PTID. */
struct siginfo *linux_nat_get_siginfo (ptid_t ptid);
+
+/* Compute and return the processor core of a given thread. */
+int linux_nat_core_of_thread_1 (ptid_t ptid);
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index 659d99d..2c66da7 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -1454,6 +1454,12 @@ thread_db_find_new_threads_1 (ptid_t ptid)
thread_db_find_new_threads_2 (ptid, 0);
}
+static int
+update_thread_core (struct lwp_info *info, void *closure)
+{
+ info->core = linux_nat_core_of_thread_1 (info->ptid);
+ return 0;
+}
static void
thread_db_find_new_threads (struct target_ops *ops)
@@ -1466,6 +1472,9 @@ thread_db_find_new_threads (struct target_ops *ops)
return;
thread_db_find_new_threads_1 (inferior_ptid);
+
+ iterate_over_lwps (minus_one_ptid /* iterate over all */,
+ update_thread_core, NULL);
}
static char *
diff --git a/gdb/remote.c b/gdb/remote.c
index 992ef9a..beb585e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1411,7 +1411,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
remote_add_thread (currthread, running);
inferior_ptid = currthread;
}
- return;
+ return;
}
if (ptid_equal (magic_null_ptid, inferior_ptid))
@@ -1421,7 +1421,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
doesn't support qC. This is the first stop reported
after an attach, so this is the main thread. Update the
ptid in the thread list. */
- thread_change_ptid (inferior_ptid, currthread);
+ thread_change_ptid (inferior_ptid, currthread);
return;
}
@@ -1452,12 +1452,12 @@ demand_private_info (ptid_t ptid)
gdb_assert (info);
- if (!info->private)
+ if (!info->private)
{
info->private = xmalloc (sizeof (*(info->private)));
info->private_dtor = free_private_thread_info;
}
-
+
return info->private;
}
diff --git a/gdb/target.h b/gdb/target.h
index 9725375..a020bf7 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -653,11 +653,10 @@ struct target_ops
disconnection - set VAL to 1 to keep tracing, 0 to stop. */
void (*to_set_disconnected_tracing) (int val);
- /* Return the core that thread PTID is on. For a stopped thread, should
- return the core the thread was last running on. For a running thread,
- should return one of the cores that the thread was running between
- the call to this function and return -- and if it was running on
- several cores, any other may be returned.
+ /* Return the processor core that thread PTID was last seen on.
+ This information is updated only when:
+ - update_thread_list is called
+ - thread stops
If the core cannot be determined -- either for the specified thread, or
right now, or in this debug session, or for this target -- return -1. */
int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
[-- Attachment #3: core4.diff --]
[-- Type: text/x-patch, Size: 63637 bytes --]
commit ea1be42644d10bb9197c6cd67927aa760906646e
Author: Vladimir Prus <vladimir@codesourcery.com>
Date: Wed Dec 16 23:39:19 2009 +0300
Implement core awareness.
gdb/
* bcache.c (compare_ints): Remove
(print_percentage): Use compare_positive_ints.
* defs.h (compare_positive_ints): Declare.
* linux-nat.h (struct lin_lwp): New field core.
(linux_nat_core_of_thread_1): Declare.
* linux-nat.c (add_lwp): Init the 'core' field.
(linux_nat_wait_1): Record the core.
(linux_nat_core_of_thread_1, linux_nat_core_of_thread): New.
(linux_nat_add_target): Register the above.
* linux-thread-db.c (update_thread_core): New.
(thread_db_find_new_threads): Update core information for
every thread.
* remote.c (struct private_thread_info): New.
(free_private_thread_info, demand_private_info): New.
(PACKET_qXfer_threads, use_osdata_threads): New.
(struct thread_item, threads_parsing_context
(start_thread, end_thread, thread_attributes)
(thread_children, threads_children, threads_elements): New.
(remote_threads_info): Try qXfer:threads before anything
else.
(remote_protocol_packets): Register qXfer:threads.
(remote_open_1): Init use_osdata_threads.
(struct stop_reply): New field 'core'.
(remote_parse_stop_reply): Parse core number.
(process_stop_reply): Record core number.
(remote_xfer_partial): Handle qXfer:threads.
(remote_core_of_thread): New.
(init_remote_ops): Register remote_core_of_thread.
(_initialize_remote): Register qXfer:read.
* target.c (target_core_of_thread): New
* target.h (enum target_object): New value TARGET_OBJECT_THREADS.
(struct target_ops): New field to_core_of_threads.
(target_core_of_thread): Declare.
* gdbthread.h (struct thread_info): New field private_dtor.
* thread.c (print_thread_info): Report the core.
* ui-out.c (MAX_UI_OUT_LEVELS): Increase.
* utils.c (compare_positive_ints): New.
* features/threads.dtd: New.
* mi/mi-interp.c (mi_on_normal_stop): Report the core.
* mi/mi-main.c (struct collect_cores_data, collect_cores)
(do_nothing, free_vector_of_osdata_items)
(splay_tree_int_comparator, free_splay_tree): New.
(print_one_inferior_data): Implemented printing of selected
inferiors. Collect and print cores.
(output_cores): New.
(mi_cmd_list_thread_groups): Support --recurse. Permit specifying
thread groups together with --available.
gdbserver/
* linux-low.c (linux_core_of_thread): New.
(compare_ints, show_process, list_threads): New.
(linux_qxfer_osdata): Report threads and cores.
(linux_target_op): Register linux_core_of_thread.
* remote-utils.c (prepare_resume_reply): Report the core.
(buffer_xml_printf): Support %d specifier.
* server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
New.
(handle_query): Handle qXfer:threads. Announce availability
thereof.
* target.h (struct target_ops): New field core_of_thread.
gdb/doc
* gdb.texinfo (GDB/MI Thread Information): New.
(GDB/MI Async Records): Document the core field in *stopped.
(GDB/MI Miscellaneous Commands): Expand -list-thread-groups
documentation
(Process list): Document that osdata document may contain
threads.
(Remote Serial Protocol): Document qXfer:threads.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4d3e02a..31a0f76 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -444,7 +444,8 @@ 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/osdata.dtd
+ $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+ $(srcdir)/features/threads.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,
diff --git a/gdb/bcache.c b/gdb/bcache.c
index 1bc2eba..4badf6e 100644
--- a/gdb/bcache.c
+++ b/gdb/bcache.c
@@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
\f
/* Printing statistics. */
-static int
-compare_ints (const void *ap, const void *bp)
-{
- /* Because we know we're comparing two ints which are positive,
- there's no danger of overflow here. */
- return * (int *) ap - * (int *) bp;
-}
-
-
static void
print_percentage (int portion, int total)
{
@@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
/* To compute the median, we need the set of chain lengths sorted. */
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
- compare_ints);
+ compare_positive_ints);
qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
- compare_ints);
+ compare_positive_ints);
if (c->num_buckets > 0)
{
diff --git a/gdb/defs.h b/gdb/defs.h
index 5d251b5..b0a212d 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -417,6 +417,8 @@ char *ldirname (const char *filename);
char **gdb_buildargv (const char *);
+int compare_positive_ints (const void *ap, const void *bp);
+
/* From demangle.c */
extern void set_demangling_style (char *);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5b78f50..02e2bbd 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -15542,6 +15542,10 @@ are:
@tab @code{qXfer:siginfo:write}
@tab @code{set $_siginfo}
+@item @code{threads}
+@tab @code{qXfer:threads:read}
+@tab @code{info threads}
+
@item @code{get-thread-local-@*storage-address}
@tab @code{qGetTLSAddr}
@tab Displaying @code{__thread} variables
@@ -21828,6 +21832,7 @@ follow development on @email{gdb@@sourceware.org} and
* GDB/MI Stream Records::
* GDB/MI Async Records::
* GDB/MI Frame Information::
+* GDB/MI Thread Information::
@end menu
@node GDB/MI Result Records
@@ -21920,7 +21925,7 @@ several times, either for different threads, because it cannot resume
all threads together, or even for a single thread, if the thread must
be stepped though some code before letting it run freely.
-@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
+@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
The target has stopped. The @var{reason} field can have one of the
following values:
@@ -21960,7 +21965,9 @@ If all threads are stopped, the @var{stopped} field will have the
value of @code{"all"}. Otherwise, the value of the @var{stopped}
field will be a list of thread identifiers. Presently, this list will
always include a single thread, but frontend should be prepared to see
-several threads in the list.
+several threads in the list. The @var{core} field reports the
+processor core on which the stop event has happened. This field may be absent
+if such information is not available.
@item =thread-group-created,id="@var{id}"
@itemx =thread-group-exited,id="@var{id}"
@@ -22037,6 +22044,34 @@ corresponds to the frame's code address. This field may be absent.
@end table
+@node GDB/MI Thread Information
+@subsection @sc{gdb/mi} Thread Information
+
+Whenever @value{GDBN} has to report an information about a thread, it
+uses a tuple with the following fields:
+
+@table @code
+@item id
+The numeric id assigned to the thread by @value{GDBN}. This field is
+always present.
+
+@item target-id
+Target-specific string identifying the thread. This field is always present.
+
+@item details
+Additional information about the thread provided by the target.
+It is supposed to be human-readable and not interpreted by the
+frontend. This field is optional.
+
+@item state
+Either @samp{stopped} or @samp{running}, depending on whether the
+thread is presently running. This field is always present.
+
+@item core
+The value of this field is an integer number of the processor core the
+thread was last seen on. This field is optional.
+@end table
+
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Simple Examples
@@ -26349,20 +26384,84 @@ while the target is running.
@subheading Synopsis
@smallexample
--list-thread-groups [ --available ] [ @var{group} ]
+-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
@end smallexample
-When used without the @var{group} parameter, lists top-level thread
-groups that are being debugged. When used with the @var{group}
-parameter, the children of the specified group are listed. The
-children can be either threads, or other groups. At present,
-@value{GDBN} will not report both threads and groups as children at
-the same time, but it may change in future.
+Lists thread groups (@pxref{Thread groups}). When a single thread
+group is passed as the argument, lists the children of that group.
+When several thread group are passed, lists information about those
+thread groups. Without any parameters, lists information about all
+top-level thread groups.
+
+Normally, thread groups that are being debugged are reported.
+With the @samp{--available} option, @value{GDBN} reports thread groups
+available on the target.
+
+The output of this command may have either a @samp{threads} result or
+a @samp{groups} result. The @samp{thread} result has a list of tuples
+as value, with each tuple describing a thread (@pxref{GDB/MI Thread
+Information}). The @samp{groups} result has a list of tuples as value,
+each tuple describing a thread group. If top-level groups are
+requested (that is, no parameter is passed), or when several groups
+are passed, the output always has a @samp{groups} result. The format
+of the @samp{group} result is described below.
+
+To reduce the number of roundtrips it's possible to list thread groups
+together with their children, by passing the @samp{--recurse} option
+and the recursion depth. Presently, only recursion depth of 1 is
+permitted. If this option is present, then every reported thread group
+will also include its children, either as @samp{group} or
+@samp{threads} field.
+
+In general, any combination of option and parameters is permitted, with
+the following caveats:
+
+@itemize @bullet
+@item
+When a single thread group is passed, the output will typically
+be the @samp{threads} result. Because threads may not contain
+anything, the @samp{recurse} option will be ignored.
+
+@item
+When the @samp{--available} option is passed, limited information may
+be available. In particular, the list of threads of a process might
+be inaccessible. Further, specifying specific thread groups might
+not give any performance advantage over listing all thread groups.
+The frontend should assume that @samp{-list-thread-groups --available}
+is always an expensive operation and cache the results.
+
+@end itemize
+
+The @samp{groups} result is a list of tuples, where each tuple may
+have the following fields:
+
+@table @code
+@item id
+Identifier of the thread group. This field is always present.
+
+@item type
+The type of the thread group. At present, only @samp{process} is a
+valid type.
+
+@item pid
+The target-specific process identifier. This field is only present
+for thread groups of type @samp{process}.
-With the @samp{--available} option, instead of reporting groups that
-are been debugged, GDB will report all thread groups available on the
-target. Using the @samp{--available} option together with @var{group}
-is not allowed.
+@item num_children
+The number of children this thread group has. This field may be
+absent for an available thread group.
+
+@item threads
+This field has a list of tuples as value, each tuple describing a
+thread. It may be present if the @samp{--recurse} option is
+specified, and it's actually possible to obtain the threads.
+
+@item cores
+This field is a list of integers, each identifying a core that one
+thread of the group is running on. This field may be absent if
+such information is not available.
+
+@end table
@subheading Example
@@ -26376,6 +26475,16 @@ is not allowed.
@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
+-list-thread-groups --available
+^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
+-list-thread-groups --available --recurse 1
+ ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
+-list-thread-groups --available --recurse 1 17 18
+^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
@end smallexample
@subheading The @code{-interpreter-exec} Command
@@ -28125,6 +28234,7 @@ Show the current setting of the target wait timeout.
* File-I/O Remote Protocol Extension::
* Library List Format::
* Memory Map Format::
+* Thread List Format::
@end menu
@node Overview
@@ -29025,6 +29135,10 @@ If @var{n} is @samp{thread}, then @var{r} is the @var{thread-id} of
the stopped thread, as specified in @ref{thread-id syntax}.
@item
+If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
+the core on which the stop event was detected.
+
+@item
If @var{n} is a recognized @dfn{stop reason}, it describes a more
specific event that stopped the target. The currently defined stop
reasons are listed below. @var{aa} should be @samp{05}, the trap
@@ -29058,8 +29172,6 @@ logged execution events, because it has reached the end (or the
beginning when executing backward) of the log. The value of @var{r}
will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
for more information.
-
-
@end table
@item W @var{AA}
@@ -29599,6 +29711,12 @@ These are the currently defined stub features and their properties:
@tab @samp{-}
@tab Yes
+@item @samp{qXfer:threads:read}
+@tab No
+@tab @samp{-}
+@tab Yes
+
+
@item @samp{QNonStop}
@tab No
@tab @samp{-}
@@ -29682,6 +29800,10 @@ The remote stub understands the @samp{qXfer:siginfo:read} packet
The remote stub understands the @samp{qXfer:siginfo:write} packet
(@pxref{qXfer siginfo write}).
+@item qXfer:threads:read
+The remote stub understands the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}).
+
@item QNonStop
The remote stub understands the @samp{QNonStop} packet
(@pxref{QNonStop}).
@@ -29879,6 +30001,15 @@ This packet is not probed by default; the remote stub must request it,
by supplying an appropriate @samp{qSupported} response
(@pxref{qSupported}).
+@item qXfer:threads:read::@var{offset},@var{length}
+@anchor{qXfer threads read}
+Access the list of threads on target. @xref{Thread List Format}. The
+annex part of the generic @samp{qXfer} packet must be empty
+(@pxref{qXfer read}).
+
+This packet is not probed by default; the remote stub must request it,
+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}.
@@ -31909,6 +32040,30 @@ The formal DTD for memory map format is given below:
<!ATTLIST property name CDATA #REQUIRED>
@end smallexample
+@node Thread List Format
+@section Thread List Format
+@cindex thread list format
+
+To efficiently update the list of threads and their attributes,
+@value{GDBN} issues the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}) and obtains the XML document with
+the following structure:
+
+@smallexample
+<?xml version="1.0"?>
+<threads>
+ <thread id="id" core="0">
+ ... description ...
+ </thread>
+</threads>
+@end smallexample
+
+Each @samp{thread} element must have the @samp{id} attribute that
+identifies the thread (@pxref{thread-id syntax}). The
+@samp{core} attribute, if present, specifies which processor core
+the thread was last executing on. The content of the of @samp{thread}
+element is interpreted as human-readable auxilliary information.
+
@include agentexpr.texi
@node Target Descriptions
@@ -32468,6 +32623,7 @@ An example document is:
<column name="pid">1</column>
<column name="user">root</column>
<column name="command">/sbin/init</column>
+ <column name="cores">1,2,3</column>
</item>
</osdata>
@end smallexample
@@ -32475,7 +32631,9 @@ An example document is:
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,
+displayed by @value{GDBN}. The @samp{cores} column, if present,
+should contain a comma-separated list of cores that this process
+is running on. Target may provide additional columns,
which @value{GDBN} currently ignores.
@include gpl.texi
diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
new file mode 100644
index 0000000..20308ee
--- /dev/null
+++ b/gdb/features/threads.dtd
@@ -0,0 +1,13 @@
+<!-- Copyright (C) 2008, 2009 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. -->
+
+<!ELEMENT threads (thread*)>
+<!ATTLIST threads version CDATA #FIXED "1.0">
+
+<!ELEMENT thread (#PCDATA)>
+
+<!ATTLIST thread id CDATA #REQUIRED>
+<!ATTLIST thread core CDATA #IMPLIED>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index c20322e..5584691 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -140,6 +140,7 @@ static int check_removed_breakpoint (struct lwp_info *event_child);
static void *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int linux_core_of_thread (ptid_t ptid);
struct pending_signals
{
@@ -2802,6 +2803,175 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
#endif
static int
+compare_ints (const void *xa, const void *xb)
+{
+ int a = *(const int *)xa;
+ int b = *(const int *)xb;
+
+ return a - b;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+/* Given PID, iterates over all threads in that process.
+
+ Information about each thread, in a format suitable for qXfer:osdata:thread
+ is printed to BUFFER, if it's not NULL. BUFFER is assumed to be already
+ initialized, and the caller is responsible for finishing and appending '\0'
+ to it.
+
+ The list of cores that threads are running on is assigned to *CORES, if it
+ is not NULL. If no cores are found, *CORES will be set to NULL. Caller
+ should free *CORES. */
+
+static void
+list_threads (int pid, struct buffer *buffer, char **cores)
+{
+ int count = 0;
+ int allocated = 10;
+ int *core_numbers = xmalloc (sizeof (int) * allocated);
+ char pathname[128];
+ DIR *dir;
+ struct dirent *dp;
+ struct stat statbuf;
+
+ sprintf (pathname, "/proc/%d/task", pid);
+ if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+ {
+ dir = opendir (pathname);
+ if (!dir)
+ {
+ free (core_numbers);
+ return;
+ }
+
+ while ((dp = readdir (dir)) != NULL)
+ {
+ unsigned long lwp = strtoul (dp->d_name, NULL, 10);
+
+ if (lwp != 0)
+ {
+ unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
+
+ if (core != -1)
+ {
+ char s[sizeof ("4294967295")];
+ sprintf (s, "%u", core);
+
+ if (count == allocated)
+ {
+ allocated *= 2;
+ core_numbers = realloc (core_numbers,
+ sizeof (int) * allocated);
+ }
+ core_numbers[count++] = core;
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "<column name=\"core\">%s</column>"
+ "</item>", pid, dp->d_name, s);
+ }
+ else
+ {
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "</item>", pid, dp->d_name);
+ }
+ }
+ }
+ }
+
+ if (cores)
+ {
+ *cores = NULL;
+ if (count > 0)
+ {
+ struct buffer buffer2;
+ int *b;
+ int *e;
+ qsort (core_numbers, count, sizeof (int), compare_ints);
+
+ /* Remove duplicates. */
+ b = core_numbers;
+ e = unique (b, core_numbers + count);
+
+ buffer_init (&buffer2);
+
+ for (b = core_numbers; b != e; ++b)
+ {
+ char number[sizeof ("4294967295")];
+ sprintf (number, "%u", *b);
+ buffer_xml_printf (&buffer2, "%s%s",
+ (b == core_numbers) ? "" : ",", number);
+ }
+ buffer_grow_str0 (&buffer2, "");
+
+ *cores = buffer_finish (&buffer2);
+ }
+ }
+ free (core_numbers);
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+ char pathname[128];
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+
+ sprintf (pathname, "/proc/%d/cmdline", pid);
+
+ if ((f = fopen (pathname, "r")) != NULL)
+ {
+ size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+ if (len > 0)
+ {
+ char *cores = 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\">%d</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>",
+ pid,
+ username,
+ cmd);
+
+ /* This only collects core numbers, and does not print threads. */
+ list_threads (pid, NULL, &cores);
+
+ if (cores)
+ {
+ buffer_xml_printf (buffer,
+ "<column name=\"cores\">%s</column>", cores);
+ free (cores);
+ }
+
+ buffer_xml_printf (buffer, "</item>");
+ }
+ fclose (f);
+ }
+}
+
+static int
linux_qxfer_osdata (const char *annex,
unsigned char *readbuf, unsigned const char *writebuf,
CORE_ADDR offset, int len)
@@ -2811,10 +2981,16 @@ linux_qxfer_osdata (const char *annex,
static const char *buf;
static long len_avail = -1;
static struct buffer buffer;
+ int processes = 0;
+ int threads = 0;
DIR *dirp;
- if (strcmp (annex, "processes") != 0)
+ if (strcmp (annex, "processes") == 0)
+ processes = 1;
+ else if (strcmp (annex, "threads") == 0)
+ threads = 1;
+ else
return 0;
if (!readbuf || writebuf)
@@ -2827,7 +3003,10 @@ linux_qxfer_osdata (const char *annex,
len_avail = 0;
buf = NULL;
buffer_init (&buffer);
- buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ if (processes)
+ buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ else if (threads)
+ buffer_grow_str (&buffer, "<osdata type=\"threads\">");
dirp = opendir ("/proc");
if (dirp)
@@ -2846,37 +3025,16 @@ linux_qxfer_osdata (const char *annex,
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);
+ int pid = (int) strtoul (dp->d_name, NULL, 10);
- if ((f = fopen (pathname, "r")) != NULL)
+ if (processes)
{
- 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);
+ struct passwd *entry = getpwuid (statbuf.st_uid);
+ show_process (pid, entry ? entry->pw_name : "?", &buffer);
+ }
+ else if (threads)
+ {
+ list_threads (pid, &buffer, NULL);
}
}
}
@@ -3152,6 +3310,55 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
return ret;
}
+static int
+linux_core_of_thread (ptid_t ptid)
+{
+ char filename[sizeof ("/proc//task//stat")
+ + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
+ + 1];
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ sprintf (filename, "/proc/%d/task/%ld/stat",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid));
+ f = fopen (filename, "r");
+ if (!f)
+ return -1;
+
+ for (;;)
+ {
+ int n;
+ content = realloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ free (content);
+ fclose (f);
+
+ return core;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -3191,10 +3398,11 @@ static struct target_ops linux_target_ops = {
linux_start_non_stop,
linux_supports_multi_process,
#ifdef USE_THREAD_DB
- thread_db_handle_monitor_command
+ thread_db_handle_monitor_command,
#else
- NULL
+ NULL,
#endif
+ linux_core_of_thread
};
static void
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 9b5bad8..76953ae 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
gdbserver to know what inferior_ptid is. */
if (1 || !ptid_equal (general_thread, ptid))
{
+ int core = -1;
/* In non-stop, don't change the general thread behind
GDB's back. */
if (!non_stop)
@@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
buf = write_ptid (buf, ptid);
strcat (buf, ";");
buf += strlen (buf);
+
+ if (the_target->core_of_thread)
+ core = (*the_target->core_of_thread) (ptid);
+ if (core != -1)
+ {
+ sprintf (buf, "core:");
+ buf += strlen (buf);
+ sprintf (buf, "%x", core);
+ strcat (buf, ";");
+ buf += strlen (buf);
+ }
}
}
@@ -1604,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...)
prev = f + 1;
}
break;
+ case 'd':
+ {
+ int i = va_arg (ap, int);
+ char b[sizeof ("4294967295")];
+
+ buffer_grow (buffer, prev, f - prev - 1);
+ sprintf (b, "%d", i);
+ buffer_grow_str (buffer, b);
+ prev = f + 1;
+ }
}
percent = 0;
}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 9254121..c781552 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
}
}
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+ struct inferior_list_entry *thread;
+
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for (thread = all_threads.head; thread; thread = thread->next)
+ {
+ ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+ char ptid_s[100];
+ int core = -1;
+ char core_s[21];
+
+ write_ptid (ptid_s, ptid);
+
+ if (the_target->core_of_thread)
+ core = (*the_target->core_of_thread) (ptid);
+
+ if (core != -1)
+ {
+ sprintf (core_s, "%d", core);
+ buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+ ptid_s, core_s);
+ }
+ else
+ {
+ buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+ ptid_s);
+ }
+ }
+
+ buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+ unsigned char *readbuf,
+ CORE_ADDR offset, int length)
+{
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (annex && strcmp (annex, "") != 0)
+ return 0;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+ /* When asked for data at offset 0, generate everything and store into
+ 'result'. Successive reads will be served off 'result'. */
+ if (result)
+ free (result);
+
+ buffer_init (&buffer);
+
+ handle_threads_qxfer_proper (&buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (length > result_length - offset)
+ length = result_length - offset;
+
+ memcpy (readbuf, result + offset, length);
+
+ return length;
+
+}
+
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}
+ if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+ {
+ unsigned char *data;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+ char *annex;
+
+ require_running (own_buf);
+
+ /* Reject any annex; grab the offset and length. */
+ if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+ || annex[0] != '\0')
+ {
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ /* Read one extra byte, as an indicator of whether there is
+ more. */
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ data = malloc (len + 1);
+ if (!data)
+ return;
+ n = handle_threads_qxfer (annex, data, ofs, len + 1);
+ if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
+ strcat (own_buf, ";qXfer:threads:read+");
+
return;
}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index ad21eb7..0c761e9 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -283,6 +283,9 @@ struct target_ops
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
+
+ /* Returns the core given a thread, or -1 if not known. */
+ int (*core_of_thread) (ptid_t);
};
extern struct target_ops *the_target;
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 4ab2111..cd24eaf 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -187,6 +187,10 @@ struct thread_info
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
+
+ /* Function that is called to free PRIVATE. If this is NULL, then
+ xfree will be called on PRIVATE. */
+ void (*private_dtor) (struct private_thread_info *);
};
/* Create an empty thread list, or empty the existing one. */
@@ -346,4 +350,6 @@ extern struct cleanup *make_cleanup_restore_current_thread (void);
INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
extern struct thread_info* inferior_thread (void);
+extern void update_thread_list (void);
+
#endif /* GDBTHREAD_H */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 48ea1bc..bf0a5f1 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1184,6 +1184,7 @@ add_lwp (ptid_t ptid)
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
lp->ptid = ptid;
+ lp->core = -1;
lp->next = lwp_list;
lwp_list = lp;
@@ -3642,6 +3643,7 @@ retry:
fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
restore_child_signals_mask (&prev_mask);
+ lp->core = linux_nat_core_of_thread_1 (lp->ptid);
return lp->ptid;
}
@@ -5423,6 +5425,75 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
return inf->aspace;
}
+int
+linux_nat_core_of_thread_1 (ptid_t ptid)
+{
+ struct cleanup *back_to;
+ char *filename;
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ filename = xstrprintf ("/proc/%d/task/%ld/stat",
+ GET_PID (ptid), GET_LWP (ptid));
+ back_to = make_cleanup (xfree, filename);
+
+ f = fopen (filename, "r");
+ if (!f)
+ {
+ do_cleanups (back_to);
+ return -1;
+ }
+
+ make_cleanup_fclose (f);
+
+ for (;;)
+ {
+ int n;
+ content = xrealloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ make_cleanup (xfree, content);
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ /* If the first field after program name has index 0, then core number is
+ the field with index 36. There's no constant for that anywhere. */
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ do_cleanups (back_to);
+
+ return core;
+}
+
+/* Return the cached value of the processor core for thread PTID. */
+
+int
+linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct lwp_info *info = find_lwp_pid (ptid);
+ if (info)
+ return info->core;
+ return -1;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -5463,6 +5534,8 @@ linux_nat_add_target (struct target_ops *t)
t->to_supports_multi_process = linux_nat_supports_multi_process;
+ t->to_core_of_thread = linux_nat_core_of_thread;
+
/* We don't change the stratum; this target will sit at
process_stratum and thread_db will set at thread_stratum. This
is a little strange, since this is a multi-threaded-capable
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index 9537740..5aba089 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -89,6 +89,9 @@ struct lwp_info
- TARGET_WAITKIND_SYSCALL_RETURN */
int syscall_state;
+ /* The processor core this LWP was last seen on. */
+ int core;
+
/* Next LWP in list. */
struct lwp_info *next;
};
@@ -163,3 +166,6 @@ void linux_nat_switch_fork (ptid_t new_ptid);
/* Return the saved siginfo associated with PTID. */
struct siginfo *linux_nat_get_siginfo (ptid_t ptid);
+
+/* Compute and return the processor core of a given thread. */
+int linux_nat_core_of_thread_1 (ptid_t ptid);
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index 659d99d..2c66da7 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -1454,6 +1454,12 @@ thread_db_find_new_threads_1 (ptid_t ptid)
thread_db_find_new_threads_2 (ptid, 0);
}
+static int
+update_thread_core (struct lwp_info *info, void *closure)
+{
+ info->core = linux_nat_core_of_thread_1 (info->ptid);
+ return 0;
+}
static void
thread_db_find_new_threads (struct target_ops *ops)
@@ -1466,6 +1472,9 @@ thread_db_find_new_threads (struct target_ops *ops)
return;
thread_db_find_new_threads_1 (inferior_ptid);
+
+ iterate_over_lwps (minus_one_ptid /* iterate over all */,
+ update_thread_core, NULL);
}
static char *
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 96e458c..41388bb 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
if (print_frame)
{
+ int core;
if (uiout != mi_uiout)
{
/* The normal_stop function has printed frame information into
@@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
}
else
ui_out_field_string (mi_uiout, "stopped-threads", "all");
+
+ core = target_core_of_thread (inferior_ptid);
+ if (core != -1)
+ ui_out_field_int (mi_uiout, "core", core);
}
fputs_unfiltered ("*stopped", raw_stdout);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 16f6102..aee246c 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -50,6 +50,7 @@
#include "valprint.h"
#include "inferior.h"
#include "osdata.h"
+#include "splay-tree.h"
#include <ctype.h>
#include <sys/time.h>
@@ -360,11 +361,55 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
print_thread_info (uiout, thread, -1);
}
+struct collect_cores_data
+{
+ int pid;
+
+ VEC (int) *cores;
+};
+
+static int
+collect_cores (struct thread_info *ti, void *xdata)
+{
+ struct collect_cores_data *data = xdata;
+
+ if (ptid_get_pid (ti->ptid) == data->pid)
+ {
+ int core = target_core_of_thread (ti->ptid);
+ if (core != -1)
+ VEC_safe_push (int, data->cores, core);
+ }
+
+ return 0;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+struct print_one_inferior_data
+{
+ int recurse;
+ VEC (int) *inferiors;
+};
+
static int
-print_one_inferior (struct inferior *inferior, void *arg)
+print_one_inferior (struct inferior *inferior, void *xdata)
{
- if (inferior->pid != 0)
+ struct print_one_inferior_data *top_data = xdata;
+
+ if (VEC_empty (int, top_data->inferiors)
+ || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
+ VEC_length (int, top_data->inferiors), sizeof (int),
+ compare_positive_ints))
{
+ struct collect_cores_data data;
struct cleanup *back_to
= make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -372,81 +417,299 @@ print_one_inferior (struct inferior *inferior, void *arg)
ui_out_field_string (uiout, "type", "process");
ui_out_field_int (uiout, "pid", inferior->pid);
+ data.pid = inferior->pid;
+ data.cores = 0;
+ iterate_over_threads (collect_cores, &data);
+
+ if (!VEC_empty (int, data.cores))
+ {
+ int elt;
+ int i;
+ int *b, *e;
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_list_begin_end (uiout, "cores");
+
+ qsort (VEC_address (int, data.cores),
+ VEC_length (int, data.cores), sizeof (int),
+ compare_positive_ints);
+
+ b = VEC_address (int, data.cores);
+ e = b + VEC_length (int, data.cores);
+ e = unique (b, e);
+
+ for (; b != e; ++b)
+ ui_out_field_int (uiout, NULL, *b);
+
+ do_cleanups (back_to_2);
+ }
+
+ if (top_data->recurse)
+ print_thread_info (uiout, -1, inferior->pid);
+
do_cleanups (back_to);
}
return 0;
}
-void
-mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+/* Output a field named 'cores' with a list as the value. The elements of
+ the list are obtained by splitting 'cores' on comma. */
+
+static void
+output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
{
- struct cleanup *back_to;
- int available = 0;
- char *id = NULL;
+ struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+ field_name);
+ char *cores = xstrdup (xcores);
+ char *p = cores;
- if (argc > 0 && strcmp (argv[0], "--available") == 0)
- {
- ++argv;
- --argc;
- available = 1;
- }
+ make_cleanup (xfree, cores);
- if (argc > 0)
- id = argv[0];
+ for (p = strtok (p, ","); p; p = strtok (NULL, ","))
+ ui_out_field_string (uiout, NULL, p);
- back_to = make_cleanup (null_cleanup, NULL);
+ do_cleanups (back_to);
+}
- if (available && id)
- {
- error (_("Can only report top-level available thread groups"));
- }
- else if (available)
- {
- struct osdata *data;
- struct osdata_item *item;
- int ix_items;
+static void
+free_vector_of_ints (void *xvector)
+{
+ VEC (int) **vector = xvector;
+ VEC_free (int, *vector);
+}
- data = get_osdata ("processes");
- make_cleanup_osdata_free (data);
+static void
+do_nothing (splay_tree_key k)
+{
+}
- make_cleanup_ui_out_list_begin_end (uiout, "groups");
+static void
+free_vector_of_osdata_items (splay_tree_value xvalue)
+{
+ VEC (osdata_item_s) *value = (VEC (osdata_item_s) *) xvalue;
+ /* We don't free the items itself, it will be done separately. */
+ VEC_free (osdata_item_s, value);
+}
+
+static int
+splay_tree_int_comparator (splay_tree_key xa, splay_tree_key xb)
+{
+ int a = xa;
+ int b = xb;
+ return a - b;
+}
+
+static void
+free_splay_tree (void *xt)
+{
+ splay_tree t = xt;
+ splay_tree_delete (t);
+}
+
+static void
+list_available_thread_groups (VEC (int) *ids, int recurse)
+{
+ struct osdata *data;
+ struct osdata_item *item;
+ int ix_items;
+ /* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
+ The vector contains information about all threads for the given
+ pid. */
+ splay_tree tree;
+
+ /* get_osdata will throw if it cannot return data. */
+ data = get_osdata ("processes");
+ make_cleanup_osdata_free (data);
+
+ if (recurse)
+ {
+ struct osdata *threads = get_osdata ("threads");
+ make_cleanup_osdata_free (threads);
+
+ tree = splay_tree_new (splay_tree_int_comparator,
+ do_nothing,
+ free_vector_of_osdata_items);
+ make_cleanup (free_splay_tree, tree);
for (ix_items = 0;
- VEC_iterate (osdata_item_s, data->items,
+ VEC_iterate (osdata_item_s, threads->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");
+ int pid_i = strtoul (pid, NULL, 0);
+ VEC (osdata_item_s) *vec = 0;
+
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (!n)
+ {
+ VEC_safe_push (osdata_item_s, vec, item);
+ splay_tree_insert (tree, pid_i, (splay_tree_value)vec);
+ }
+ else
+ {
+ vec = (VEC (osdata_item_s) *) n->value;
+ VEC_safe_push (osdata_item_s, vec, item);
+ n->value = (splay_tree_value) vec;
+ }
+ }
+ }
+
+ 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;
+
+ const char *pid = get_osdata_column (item, "pid");
+ const char *cmd = get_osdata_column (item, "command");
+ const char *user = get_osdata_column (item, "user");
+ const char *cores = get_osdata_column (item, "cores");
+
+ int pid_i = strtoul (pid, NULL, 0);
+
+ /* At present, the target will return all available processes
+ and if information about specific ones was required, we filter
+ undesired processes here. */
+ if (ids && bsearch (&pid_i, VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints) == NULL)
+ continue;
+
- 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);
+ back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
- do_cleanups (back_to);
+ 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);
+ if (cores)
+ output_cores (uiout, "cores", cores);
+
+ if (recurse)
+ {
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (n)
+ {
+ VEC (osdata_item_s) *children = (VEC (osdata_item_s) *) n->value;
+ struct osdata_item *child;
+ int ix_child;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+ for (ix_child = 0;
+ VEC_iterate (osdata_item_s, children, ix_child, child);
+ ++ix_child)
+ {
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *tid = get_osdata_column (child, "tid");
+ const char *tcore = get_osdata_column (child, "core");
+ ui_out_field_string (uiout, "id", tid);
+ if (tcore)
+ ui_out_field_string (uiout, "core", tcore);
+
+ do_cleanups (back_to_2);
+ }
+ }
}
+
+ do_cleanups (back_to);
}
- else if (id)
+}
+
+void
+mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+{
+ struct cleanup *back_to;
+ int available = 0;
+ int recurse = 0;
+ VEC (int) *ids = 0;
+
+ enum opt
{
- int pid = atoi (id);
+ AVAILABLE_OPT, RECURSE_OPT
+ };
+ static struct mi_opt opts[] =
+ {
+ {"-available", AVAILABLE_OPT, 0},
+ {"-recurse", RECURSE_OPT, 1},
+ { 0, 0, 0 }
+ };
+
+ int optind = 0;
+ char *optarg;
+
+ while (1)
+ {
+ int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+ &optind, &optarg);
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case AVAILABLE_OPT:
+ available = 1;
+ break;
+ case RECURSE_OPT:
+ if (strcmp (optarg, "0") == 0)
+ ;
+ else if (strcmp (optarg, "1") == 0)
+ recurse = 1;
+ else
+ error ("only '0' and '1' are valid values for the '--recurse' option");
+ break;
+ }
+ }
+
+ for (; optind < argc; ++optind)
+ {
+ char *end;
+ int inf = strtoul (argv[optind], &end, 0);
+ if (*end != '\0')
+ error ("invalid group id '%s'", argv[optind]);
+ VEC_safe_push (int, ids, inf);
+ }
+ if (VEC_length (int, ids) > 1)
+ qsort (VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints);
+
+ back_to = make_cleanup (free_vector_of_ints, &ids);
+
+ if (available)
+ {
+ list_available_thread_groups (ids, recurse);
+ }
+ else if (VEC_length (int, ids) == 1)
+ {
+ /* Local thread groups, single id. */
+ int pid = *VEC_address (int, ids);
if (!in_inferior_list (pid))
- error ("Invalid thread group id '%s'", id);
- print_thread_info (uiout, -1, pid);
+ error ("Invalid thread group id '%d'", pid);
+ print_thread_info (uiout, -1, pid);
}
else
{
+ struct print_one_inferior_data data;
+ data.recurse = recurse;
+ data.inferiors = ids;
+
+ /* Local thread groups. Either no explicit ids -- and we
+ print everything, or several explicit ids. In both cases,
+ we print more than one group, and have to use 'groups'
+ as the top-level element. */
make_cleanup_ui_out_list_begin_end (uiout, "groups");
- iterate_over_inferiors (print_one_inferior, NULL);
+ update_thread_list ();
+ iterate_over_inferiors (print_one_inferior, &data);
}
-
+
do_cleanups (back_to);
}
diff --git a/gdb/remote.c b/gdb/remote.c
index 9c50f7e..beb585e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -60,6 +60,7 @@
#include "remote-fileio.h"
#include "gdb/fileio.h"
#include "gdb_stat.h"
+#include "xml-support.h"
#include "memory-map.h"
@@ -324,6 +325,20 @@ struct remote_state
int ctrlc_pending_p;
};
+/* Private data that we'll store in (struct thread_info)->private. */
+struct private_thread_info
+{
+ char *extra;
+ int core;
+};
+
+static void
+free_private_thread_info (struct private_thread_info *info)
+{
+ xfree (info->extra);
+ xfree (info);
+}
+
/* Returns true if the multi-process extensions are in effect. */
static int
remote_multi_process_p (struct remote_state *rs)
@@ -1121,6 +1136,7 @@ enum {
PACKET_qXfer_spu_read,
PACKET_qXfer_spu_write,
PACKET_qXfer_osdata,
+ PACKET_qXfer_threads,
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
@@ -1395,7 +1411,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
remote_add_thread (currthread, running);
inferior_ptid = currthread;
}
- return;
+ return;
}
if (ptid_equal (magic_null_ptid, inferior_ptid))
@@ -1405,7 +1421,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
doesn't support qC. This is the first stop reported
after an attach, so this is the main thread. Update the
ptid in the thread list. */
- thread_change_ptid (inferior_ptid, currthread);
+ thread_change_ptid (inferior_ptid, currthread);
return;
}
@@ -1427,6 +1443,24 @@ remote_notice_new_inferior (ptid_t currthread, int running)
}
}
+/* Return the private thread data, creating it if necessary. */
+
+struct private_thread_info *
+demand_private_info (ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+
+ gdb_assert (info);
+
+ if (!info->private)
+ {
+ info->private = xmalloc (sizeof (*(info->private)));
+ info->private_dtor = free_private_thread_info;
+ }
+
+ return info->private;
+}
+
/* Call this function as a result of
1) A halt indication (T packet) containing a thread id
2) A direct query of currthread
@@ -1437,12 +1471,6 @@ static void
record_currthread (ptid_t currthread)
{
general_thread = currthread;
-
- if (ptid_equal (currthread, minus_one_ptid))
- /* We're just invalidating the local thread mirror. */
- return;
-
- remote_notice_new_inferior (currthread, 0);
}
static char *last_pass_packet;
@@ -2371,6 +2399,80 @@ remote_find_new_threads (void)
CRAZY_MAX_THREADS);
}
+#if defined(HAVE_LIBEXPAT)
+
+typedef struct thread_item
+{
+ ptid_t ptid;
+ char *extra;
+ int core;
+} thread_item_t;
+DEF_VEC_O(thread_item_t);
+
+struct threads_parsing_context
+{
+ VEC (thread_item_t) *items;
+};
+
+static void
+start_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct threads_parsing_context *data = user_data;
+
+ struct thread_item item;
+ char *id;
+
+ id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ item.ptid = read_ptid (id, NULL);
+
+ if (VEC_length (gdb_xml_value_s, attributes) > 1)
+ item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
+ else
+ item.core = -1;
+
+ item.extra = 0;
+
+ VEC_safe_push (thread_item_t, data->items, &item);
+}
+
+static void
+end_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, const char *body_text)
+{
+ struct threads_parsing_context *data = user_data;
+
+ if (body_text && *body_text)
+ VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
+}
+
+const struct gdb_xml_attribute thread_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element thread_children[] = {
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_children[] = {
+ { "thread", thread_attributes, thread_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ start_thread, end_thread },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_elements[] = {
+ { "threads", NULL, threads_children,
+ GDB_XML_EF_NONE, NULL, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+#endif
+
/*
* Find all threads for info threads command.
* Uses new thread protocol contributed by Cisco.
@@ -2388,6 +2490,61 @@ remote_threads_info (struct target_ops *ops)
if (remote_desc == 0) /* paranoia */
error (_("Command can only be used when connected to the remote target."));
+#if defined(HAVE_LIBEXPAT)
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ char *xml = target_read_stralloc (¤t_target,
+ TARGET_OBJECT_THREADS, NULL);
+
+ struct cleanup *back_to = make_cleanup (xfree, xml);
+ if (xml && *xml)
+ {
+ struct gdb_xml_parser *parser;
+ struct threads_parsing_context context;
+ struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+ context.items = 0;
+ parser = gdb_xml_create_parser_and_cleanup (_("threads"),
+ threads_elements,
+ &context);
+
+ gdb_xml_use_dtd (parser, "threads.dtd");
+
+ if (gdb_xml_parse (parser, xml) == 0)
+ {
+ int i;
+ struct thread_item *item;
+
+ for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
+ {
+ if (!ptid_equal (item->ptid, null_ptid))
+ {
+ struct private_thread_info *info;
+ /* In non-stop mode, we assume new found threads
+ are running until proven otherwise with a
+ stop reply. In all-stop, we can only get
+ here if all threads are stopped. */
+ int running = non_stop ? 1 : 0;
+
+ remote_notice_new_inferior (item->ptid, running);
+
+ info = demand_private_info (item->ptid);
+ info->core = item->core;
+ info->extra = item->extra;
+ item->extra = 0;
+ }
+ xfree (item->extra);
+ }
+ }
+
+ VEC_free (thread_item_t, context.items);
+ }
+
+ do_cleanups (back_to);
+ return;
+ }
+#endif
+
if (use_threadinfo_query)
{
putpkt ("qfThreadInfo");
@@ -2460,6 +2617,15 @@ remote_threads_extra_info (struct thread_info *tp)
server doesn't know about it. */
return NULL;
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ struct thread_info *info = find_thread_ptid (tp->ptid);
+ if (info && info->private)
+ return info->private->extra;
+ else
+ return NULL;
+ }
+
if (use_threadextra_query)
{
char *b = rs->buf;
@@ -3245,6 +3411,8 @@ static struct protocol_feature remote_protocol_features[] = {
PACKET_qXfer_spu_write },
{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
PACKET_qXfer_osdata },
+ { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
+ PACKET_qXfer_threads },
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
PACKET_QPassSignals },
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -4359,6 +4527,8 @@ struct stop_reply
int solibs_changed;
int replay_event;
+
+ int core;
};
/* The list of already fetched and acknowledged stop events. */
@@ -4522,6 +4692,7 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
event->replay_event = 0;
event->stopped_by_watchpoint_p = 0;
event->regcache = NULL;
+ event->core = -1;
switch (buf[0])
{
@@ -4548,7 +4719,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
/* If this packet is an awatch packet, don't parse the 'a'
as a register number. */
- if (strncmp (p, "awatch", strlen("awatch")) != 0)
+ if (strncmp (p, "awatch", strlen("awatch")) != 0
+ && strncmp (p, "core", strlen ("core") != 0))
{
/* Read the ``P'' register number. */
pnum = strtol (p, &p_temp, 16);
@@ -4594,6 +4766,12 @@ Packet: '%s'\n"),
if (p_temp)
p = p_temp;
}
+ else if (strncmp (p, "core", p1 - p) == 0)
+ {
+ ULONGEST c;
+ p = unpack_varlen_hex (++p1, &c);
+ event->core = c;
+ }
else
{
/* Silently skip unknown optional info. */
@@ -4803,6 +4981,7 @@ process_stop_reply (struct stop_reply *stop_reply,
struct target_waitstatus *status)
{
ptid_t ptid;
+ struct thread_info *info;
*status = stop_reply->ws;
ptid = stop_reply->ptid;
@@ -4834,6 +5013,7 @@ process_stop_reply (struct stop_reply *stop_reply,
remote_watch_data_address = stop_reply->watch_data_address;
remote_notice_new_inferior (ptid, 0);
+ demand_private_info (ptid)->core = stop_reply->core;
}
stop_reply_xfree (stop_reply);
@@ -7676,6 +7856,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
(ops, "osdata", annex, readbuf, offset, len,
&remote_protocol_packets[PACKET_qXfer_osdata]);
+ case TARGET_OBJECT_THREADS:
+ gdb_assert (annex == NULL);
+ return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
+ &remote_protocol_packets[PACKET_qXfer_threads]);
+
default:
return -1;
}
@@ -9324,6 +9509,15 @@ remote_set_disconnected_tracing (int val)
error (_("Target does not support this command."));
}
+static int
+remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+ if (info && info->private)
+ return info->private->core;
+ return -1;
+}
+
static void
init_remote_ops (void)
{
@@ -9397,6 +9591,7 @@ Specify the serial device it is connected to\n\
remote_ops.to_trace_find = remote_trace_find;
remote_ops.to_get_trace_state_variable_value = remote_get_trace_state_variable_value;
remote_ops.to_set_disconnected_tracing = remote_set_disconnected_tracing;
+ remote_ops.to_core_of_thread = remote_core_of_thread;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -9933,6 +10128,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
"qXfer:osdata:read", "osdata", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
+ "qXfer:threads:read", "threads", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
"qXfer:siginfo:read", "read-siginfo-object", 0);
diff --git a/gdb/target.c b/gdb/target.c
index 25a2cd7..edf8697 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3064,6 +3064,26 @@ target_store_registers (struct regcache *regcache, int regno)
noprocess ();
}
+int
+target_core_of_thread (ptid_t ptid)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_core_of_thread != NULL)
+ {
+ int retval = t->to_core_of_thread (t, ptid);
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
+ PIDGET (ptid), retval);
+ return retval;
+ }
+ }
+
+ return -1;
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
diff --git a/gdb/target.h b/gdb/target.h
index 20cbe29..a020bf7 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -256,6 +256,8 @@ enum target_object
/* Extra signal info. Usually the contents of `siginfo_t' on unix
platforms. */
TARGET_OBJECT_SIGNAL_INFO,
+ /* The list of threads that are being debugged. */
+ TARGET_OBJECT_THREADS,
/* Possible future objects: TARGET_OBJECT_FILE, ... */
};
@@ -651,6 +653,14 @@ struct target_ops
disconnection - set VAL to 1 to keep tracing, 0 to stop. */
void (*to_set_disconnected_tracing) (int val);
+ /* Return the processor core that thread PTID was last seen on.
+ This information is updated only when:
+ - update_thread_list is called
+ - thread stops
+ If the core cannot be determined -- either for the specified thread, or
+ right now, or in this debug session, or for this target -- return -1. */
+ int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
+
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@@ -1332,6 +1342,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
(*current_target.to_log_command) (p); \
while (0)
+
+extern int target_core_of_thread (ptid_t ptid);
+
/* Routines for maintenance of the target structures...
add_target: Add a target to the list of all possible targets.
diff --git a/gdb/thread.c b/gdb/thread.c
index 80c3786..16a207c 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -114,10 +114,13 @@ free_thread (struct thread_info *tp)
{
clear_thread_inferior_resources (tp);
- /* FIXME: do I ever need to call the back-end to give it a
- chance at this private data before deleting the thread? */
if (tp->private)
- xfree (tp->private);
+ {
+ if (tp->private_dtor)
+ tp->private_dtor (tp->private);
+ else
+ xfree (tp->private);
+ }
xfree (tp);
}
@@ -468,8 +471,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
struct cleanup *cleanup_chain;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
@@ -748,8 +750,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
char *extra_info;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
current_ptid = inferior_ptid;
/* We'll be switching threads temporarily. */
@@ -759,6 +760,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
for (tp = thread_list; tp; tp = tp->next)
{
struct cleanup *chain2;
+ int core;
if (requested_thread != -1 && tp->num != requested_thread)
continue;
@@ -817,6 +819,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
ui_out_field_string (uiout, "state", state);
}
+ core = target_core_of_thread (tp->ptid);
+ if (ui_out_is_mi_like_p (uiout) && core != -1)
+ ui_out_field_int (uiout, "core", core);
+
do_cleanups (chain2);
}
@@ -1058,8 +1064,7 @@ thread_apply_all_command (char *cmd, int from_tty)
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
old_chain = make_cleanup_restore_current_thread ();
@@ -1245,6 +1250,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
return GDB_RC_OK;
}
+void
+update_thread_list (void)
+{
+ prune_threads ();
+ target_find_new_threads ();
+}
+
/* Commands with a prefix of `thread'. */
struct cmd_list_element *thread_cmd_list = NULL;
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index db8c894..e5fd474 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -44,7 +44,7 @@ struct ui_out_hdr
is always available. Stack/nested level 0 is reserved for the
top-level result. */
-enum { MAX_UI_OUT_LEVELS = 6 };
+enum { MAX_UI_OUT_LEVELS = 8 };
struct ui_out_level
{
diff --git a/gdb/utils.c b/gdb/utils.c
index 4c03655..f72a9e3 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
return argv;
}
+int
+compare_positive_ints (const void *ap, const void *bp)
+{
+ /* Because we know we're comparing two ints which are positive,
+ there's no danger of overflow here. */
+ return * (int *) ap - * (int *) bp;
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_utils;
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-09 12:41 ` Vladimir Prus
2010-01-09 13:48 ` Eli Zaretskii
@ 2010-01-11 14:17 ` Pedro Alves
2010-01-11 21:01 ` Vladimir Prus
1 sibling, 1 reply; 17+ messages in thread
From: Pedro Alves @ 2010-01-11 14:17 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
On Saturday 09 January 2010 12:40:39, Vladimir Prus wrote:
> On Friday 08 January 2010 23:30:20 Pedro Alves wrote:
>
> > > The primary problem is that when we process a stop reply
> > > for a new thread, the thread is not created and added to gdb
> > > thread table yet, so it's not possible to set its info->private.
> > > I don't see any way around this.
> >
> > The remote_notice_new_inferior calls always return
> > with the possibly new thread added to the list already. From
> > the patched code:
> >
> > remote_threads_info:
> > ...
> > remote_notice_new_inferior (item->ptid, running);
> >
> > info = find_thread_ptid (item->ptid);
>
> Oh, I did not realize this. I've simplified the code with this
> assumption in mind, and killed the two globals.
>
> > You can, for example, pass the core to remote_notice_new_inferior,
> > and have remote_notice_new_inferior itself update the core field.
>
> I took a slightly different approach, since passing core to a function
> whose purpose is to handle appearance of a new thread would be somewhat
> inconsistent.
>
> > > -several threads in the list.
> > > +several threads in the list. The @var{core} field reports the
> > > +processor core on which the stop event has happened. This field may be absent
> > > +if such information is not available.
> >
> > s/stop event/stop event/
> >
> >
> > Formatting here is wrong:
> >
> > > + if (!info->private) {
> > > + info->private = (struct private_thread_info *)
> > > + xmalloc (sizeof (struct private_thread_info));
> > > + info->private_dtor = free_private_thread_info;
> > > + }
> >
> >
> > ( If you write that as `info->private = xmalloc (sizeof (*info->private))'
> > it's a shorter line. )
> >
> >
> > Also:
> >
> > $ quilt refresh
> > Warning: trailing whitespace in line 118 of gdb/thread.c
> > Warning: trailing whitespace in lines 602,603,606 of gdb/target.h
> > Refreshed patch core3.diff
>
> I've fixed the above formatting issues.
And added others :-) :
$ quilt refresh
Warning: trailing whitespace in lines 1387,1392 of gdb/remote.c
Refreshed patch core4.diff
>
>
> > > For a start, I certainly find that a single packet that returns all information
> > > about threads -- which a frontend might be requesting on each step -- is a good
> > > idea regardless of "core awareness". As for invalidation, I think it's reasonable
> > > to fetch information about all threads, just because the time to get that information
> > > is probably less than communication latency for a single packet.
> >
> > Right, agreed on that then. Good that it's considered.
> >
> > >
> > > So, for avoidance of doubt, how about this scheme:
> > > - core information of each thread is invalidated when that thread is resumed
> > > (where resumption is reported by the 'target_resumed' observer)
> > > - remote_core_of_thread checks if it's asked for a core of the most recently
> > > stopped thread. If so, it reports it. Otherwise, it refreshes the thread list,
> > > and then reports core it has fetched
> > > - this all is for all-stop only. For non-stop, we get stop packet of each stopped
> > > thread and therefore remote_core_of_thread never needs to fetch information
> > > about all threads.
> > >
> > > If this sounds OK, I'll implement such a scheme.
> >
> > Sounds good, although I still don't see why you need to special
> > case the last-reported-thread. One concern about non-stop and
> > running threads though. Looking at the new documentation of
> > the new target interface makes it clearer what I'm talking about:
> >
> > /* Return the core that thread PTID is on. For a stopped thread, should
> > return the core the thread was last running on. For a running thread,
> > ^^^^^^^^^^^^^^^^^^^^^
> > should return one of the cores that the thread was running between
> > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > the call to this function and return -- and if it was running on
> > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > several cores, any other may be returned.
> > If the core cannot be determined -- either for the specified thread, or
> > right now, or in this debug session, or for this target -- return -1. */
> >
> > int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
> >
> >
> > Specifically, the underlined bit. If we look at the current
> > remote.c implementation of that method,
> >
> > static int
> > remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
> > {
> > struct thread_info *info = find_thread_ptid (ptid);
> > if (info && info->private)
> > return info->private->core;
> > else if (ptid_equal (ptid, thread_of_last_core))
> > return last_core;
> > return -1;
> > }
> >
> > we see that there's nothing here that considers the fact
> > that `info->private->core' gets stale real quick, and does
> > not actually represent "one of the cores that the thread
> > was running between the call to this function and return".
> > This is relying on the callers of target_core_of_thread
> > having refreshed the thread list, but there's
> > no reason a native target would need to update the
> > thread list to query the core of a single thread
> > when it has direct access to that info.
> >
> > So, we either update the documentation of
> > target_core_of_thread's assumptions to match reality,
> > which feels a bit hackish given that it is a remote.c
> > related assumption, or we change the remote
> > implementation to refetch the data if the thread is
> > running, which has the problem that with the
> > current API, for every running thread, you'd re-update
> > the whole thread list (and this is what made me
> > wonder before how would you consider the protocol
> > should handle updating the core data of a
> > single thread).
>
> I guess we should recall what uses core information has. First,
> it's necessary to inform the user what thread is running on what
> processor. This is a 'wholesale' operation. Second, it's necessary
> to report the core of the thread that has just stopped. I do not see
> a use case that requires to find the core of the individual running
> thread. Furthermore, iterating over all threads, calling
> target_core_of_thread, will always be inefficient.
>
> So, maybe we should not use target method at all. Instead:
>
> - Introduce thread_get_core function in thread.c. That thread will
> be documented as returning the core a given thread was last noticed
> running on by GDB, with that information updated either by
> update_thread_list or when a thread stops. We'd need a new field
> inside thread_info to keep this information.
> - Make linux native update core information for all threads when
> listing them.
Sounds good to me. Note that linux-nat.c doesn't list threads,
linux-thread-db.c does.
> What I am primarily trying to avoid is introducing a packet to get
> core information for a single running thread. That will be slow,
> and such functionality is not required by anything at present.
That's fine. My gripe is in the disconnect between the
documentation of what the method should do, and what it
actually does.
--
Pedro Alves
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-09 12:41 ` Vladimir Prus
@ 2010-01-09 13:48 ` Eli Zaretskii
2010-01-11 14:17 ` Pedro Alves
1 sibling, 0 replies; 17+ messages in thread
From: Eli Zaretskii @ 2010-01-09 13:48 UTC (permalink / raw)
To: Vladimir Prus; +Cc: pedro, gdb-patches
> From: Vladimir Prus <vladimir@codesourcery.com>
> Date: Sat, 9 Jan 2010 15:40:39 +0300
> Cc: gdb-patches@sourceware.org
>
> gdb/doc
> * gdb.texinfo (GDB/MI Thread Information): New.
> (GDB/MI Async Records): Document the core field in *stopped.
> (GDB/MI Miscellaneous Commands): Expand -list-thread-groups
> documentation
> (Process list): Document that osdata document may contain
> threads.
> (Remote Serial Protocol): Document qXfer:threads.
OK for this part. Thanks.
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-08 20:30 ` Pedro Alves
@ 2010-01-09 12:41 ` Vladimir Prus
2010-01-09 13:48 ` Eli Zaretskii
2010-01-11 14:17 ` Pedro Alves
0 siblings, 2 replies; 17+ messages in thread
From: Vladimir Prus @ 2010-01-09 12:41 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: Text/Plain, Size: 7607 bytes --]
On Friday 08 January 2010 23:30:20 Pedro Alves wrote:
> > The primary problem is that when we process a stop reply
> > for a new thread, the thread is not created and added to gdb
> > thread table yet, so it's not possible to set its info->private.
> > I don't see any way around this.
>
> The remote_notice_new_inferior calls always return
> with the possibly new thread added to the list already. From
> the patched code:
>
> remote_threads_info:
> ...
> remote_notice_new_inferior (item->ptid, running);
>
> info = find_thread_ptid (item->ptid);
Oh, I did not realize this. I've simplified the code with this
assumption in mind, and killed the two globals.
> You can, for example, pass the core to remote_notice_new_inferior,
> and have remote_notice_new_inferior itself update the core field.
I took a slightly different approach, since passing core to a function
whose purpose is to handle appearance of a new thread would be somewhat
inconsistent.
> > -several threads in the list.
> > +several threads in the list. The @var{core} field reports the
> > +processor core on which the stop event has happened. This field may be absent
> > +if such information is not available.
>
> s/stop event/stop event/
>
>
> Formatting here is wrong:
>
> > + if (!info->private) {
> > + info->private = (struct private_thread_info *)
> > + xmalloc (sizeof (struct private_thread_info));
> > + info->private_dtor = free_private_thread_info;
> > + }
>
>
> ( If you write that as `info->private = xmalloc (sizeof (*info->private))'
> it's a shorter line. )
>
>
> Also:
>
> $ quilt refresh
> Warning: trailing whitespace in line 118 of gdb/thread.c
> Warning: trailing whitespace in lines 602,603,606 of gdb/target.h
> Refreshed patch core3.diff
I've fixed the above formatting issues.
> > For a start, I certainly find that a single packet that returns all information
> > about threads -- which a frontend might be requesting on each step -- is a good
> > idea regardless of "core awareness". As for invalidation, I think it's reasonable
> > to fetch information about all threads, just because the time to get that information
> > is probably less than communication latency for a single packet.
>
> Right, agreed on that then. Good that it's considered.
>
> >
> > So, for avoidance of doubt, how about this scheme:
> > - core information of each thread is invalidated when that thread is resumed
> > (where resumption is reported by the 'target_resumed' observer)
> > - remote_core_of_thread checks if it's asked for a core of the most recently
> > stopped thread. If so, it reports it. Otherwise, it refreshes the thread list,
> > and then reports core it has fetched
> > - this all is for all-stop only. For non-stop, we get stop packet of each stopped
> > thread and therefore remote_core_of_thread never needs to fetch information
> > about all threads.
> >
> > If this sounds OK, I'll implement such a scheme.
>
> Sounds good, although I still don't see why you need to special
> case the last-reported-thread. One concern about non-stop and
> running threads though. Looking at the new documentation of
> the new target interface makes it clearer what I'm talking about:
>
> /* Return the core that thread PTID is on. For a stopped thread, should
> return the core the thread was last running on. For a running thread,
> ^^^^^^^^^^^^^^^^^^^^^
> should return one of the cores that the thread was running between
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> the call to this function and return -- and if it was running on
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> several cores, any other may be returned.
> If the core cannot be determined -- either for the specified thread, or
> right now, or in this debug session, or for this target -- return -1. */
>
> int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
>
>
> Specifically, the underlined bit. If we look at the current
> remote.c implementation of that method,
>
> static int
> remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
> {
> struct thread_info *info = find_thread_ptid (ptid);
> if (info && info->private)
> return info->private->core;
> else if (ptid_equal (ptid, thread_of_last_core))
> return last_core;
> return -1;
> }
>
> we see that there's nothing here that considers the fact
> that `info->private->core' gets stale real quick, and does
> not actually represent "one of the cores that the thread
> was running between the call to this function and return".
> This is relying on the callers of target_core_of_thread
> having refreshed the thread list, but there's
> no reason a native target would need to update the
> thread list to query the core of a single thread
> when it has direct access to that info.
>
> So, we either update the documentation of
> target_core_of_thread's assumptions to match reality,
> which feels a bit hackish given that it is a remote.c
> related assumption, or we change the remote
> implementation to refetch the data if the thread is
> running, which has the problem that with the
> current API, for every running thread, you'd re-update
> the whole thread list (and this is what made me
> wonder before how would you consider the protocol
> should handle updating the core data of a
> single thread).
I guess we should recall what uses core information has. First,
it's necessary to inform the user what thread is running on what
processor. This is a 'wholesale' operation. Second, it's necessary
to report the core of the thread that has just stopped. I do not see
a use case that requires to find the core of the individual running
thread. Furthermore, iterating over all threads, calling
target_core_of_thread, will always be inefficient.
So, maybe we should not use target method at all. Instead:
- Introduce thread_get_core function in thread.c. That thread will
be documented as returning the core a given thread was last noticed
running on by GDB, with that information updated either by
update_thread_list or when a thread stops. We'd need a new field
inside thread_info to keep this information.
- Make linux native update core information for all threads when
listing them.
What I am primarily trying to avoid is introducing a packet to get
core information for a single running thread. That will be slow,
and such functionality is not required by anything at present.
> > Well, while I presumably could have split /proc/cpuinfo on empty lines,
> > and somehow print that in 'info os cores' I am unsure if we can promise
> > any fields to be present in the output, given that man:proc does not
> > mention any field except of "processor" and "bogomips". So, all that
> > "info os cores" can meaningfully print is the numeric ids of cores,
> > and this does not add very much to the information already available
> > to the frontend.
>
> E.g., if all threads in a system are pinned to / running on only one
> of multiple cores, the frontend does not have have any way to know
> about all the cores on the target system. No way to show to the
> user 40 threads on core 1, and 0 on core 2. It simply has no
> info about core 2. That's fine, I was only wondering if you
> had considered it; we can add this at any time.
Yes, I think we can add it later, if needed.
- Volodya
[-- Attachment #2: delta.diff --]
[-- Type: text/x-patch, Size: 5640 bytes --]
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ca5e92c..5057ade 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21906,7 +21906,7 @@ value of @code{"all"}. Otherwise, the value of the @var{stopped}
field will be a list of thread identifiers. Presently, this list will
always include a single thread, but frontend should be prepared to see
several threads in the list. The @var{core} field reports the
-processor core on which the stop event has happened. This field may be absent
+processor core on which the stop event has happened. This field may be absent
if such information is not available.
@item =thread-group-created,id="@var{id}"
diff --git a/gdb/remote.c b/gdb/remote.c
index 9d343b2..ffd4ad3 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -318,12 +318,6 @@ free_private_thread_info (struct private_thread_info *info)
xfree (info);
}
-/* The core number that was last seen by process_stop_reply. */
-static int last_core = -1;
-
-/* The thread that corresponds to last_core. */
-static ptid_t thread_of_last_core;
-
/* Returns true if the multi-process extensions are in effect. */
static int
remote_multi_process_p (struct remote_state *rs)
@@ -1381,6 +1375,24 @@ remote_notice_new_inferior (ptid_t currthread, int running)
}
}
+/* Return the private thread data, creating it if necessary. */
+
+struct private_thread_info *
+demand_private_info (ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+
+ gdb_assert (info);
+
+ if (!info->private)
+ {
+ info->private = xmalloc (sizeof (*(info->private)));
+ info->private_dtor = free_private_thread_info;
+ }
+
+ return info->private;
+}
+
/* Call this function as a result of
1) A halt indication (T packet) containing a thread id
2) A direct query of currthread
@@ -1391,12 +1403,6 @@ static void
record_currthread (ptid_t currthread)
{
general_thread = currthread;
-
- if (ptid_equal (currthread, minus_one_ptid))
- /* We're just invalidating the local thread mirror. */
- return;
-
- remote_notice_new_inferior (currthread, 0);
}
static char *last_pass_packet;
@@ -2445,7 +2451,7 @@ remote_threads_info (struct target_ops *ops)
{
if (!ptid_equal (item->ptid, null_ptid))
{
- struct thread_info *info;
+ struct private_thread_info *info;
/* In non-stop mode, we assume new found threads
are running until proven otherwise with a
stop reply. In all-stop, we can only get
@@ -2454,19 +2460,10 @@ remote_threads_info (struct target_ops *ops)
remote_notice_new_inferior (item->ptid, running);
- info = find_thread_ptid (item->ptid);
- if (info)
- {
- if (!info->private) {
- info->private = (struct private_thread_info *)
- xmalloc (sizeof (struct private_thread_info));
- info->private_dtor = free_private_thread_info;
- }
-
- info->private->extra = item->extra;
- item->extra = 0;
- info->private->core = item->core;
- }
+ info = demand_private_info (item->ptid);
+ info->core = item->core;
+ info->extra = item->extra;
+ item->extra = 0;
}
xfree (item->extra);
}
@@ -4918,20 +4915,8 @@ process_stop_reply (struct stop_reply *stop_reply,
remote_stopped_by_watchpoint_p = stop_reply->stopped_by_watchpoint_p;
remote_watch_data_address = stop_reply->watch_data_address;
- /* Update the core associated with a thread when we process stop
- event in that thread. */
- info = find_thread_ptid (ptid);
- if (info && info->private)
- {
- info->private->core = stop_reply->core;
- }
- else
- {
- last_core = stop_reply->core;
- thread_of_last_core = ptid;
- }
-
remote_notice_new_inferior (ptid, 0);
+ demand_private_info (ptid)->core = stop_reply->core;
}
stop_reply_xfree (stop_reply);
@@ -9101,8 +9086,6 @@ remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
struct thread_info *info = find_thread_ptid (ptid);
if (info && info->private)
return info->private->core;
- else if (ptid_equal (ptid, thread_of_last_core))
- return last_core;
return -1;
}
diff --git a/gdb/target.h b/gdb/target.h
index a4fe06a..a4a0b18 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -599,11 +599,11 @@ struct target_ops
struct address_space *(*to_thread_address_space) (struct target_ops *,
ptid_t);
- /* Return the core that thread PTID is on. For a stopped thread, should
- return the core the thread was last running on. For a running thread,
+ /* Return the core that thread PTID is on. For a stopped thread, should
+ return the core the thread was last running on. For a running thread,
should return one of the cores that the thread was running between
the call to this function and return -- and if it was running on
- several cores, any other may be returned.
+ several cores, any other may be returned.
If the core cannot be determined -- either for the specified thread, or
right now, or in this debug session, or for this target -- return -1. */
int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
diff --git a/gdb/thread.c b/gdb/thread.c
index 9613393..16a207c 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -114,8 +114,6 @@ free_thread (struct thread_info *tp)
{
clear_thread_inferior_resources (tp);
- /* FIXME: do I ever need to call the back-end to give it a
- chance at this private data before deleting the thread? */
if (tp->private)
{
if (tp->private_dtor)
[-- Attachment #3: core4.diff --]
[-- Type: text/x-patch, Size: 60847 bytes --]
commit 7cee58904495e384af27525bb1221638a90fd814
Author: Vladimir Prus <vladimir@codesourcery.com>
Date: Wed Dec 16 23:39:19 2009 +0300
Implement core awareness.
gdb/
* bcache.c (compare_ints): Remove
(print_percentage): Use compare_positive_ints.
* defs.h (compare_positive_ints): Declare.
* linux-nat.c (linux_nat_core_of_thread): New.
(linux_nat_add_target): Register the above.
* remote.c (struct private_thread_info): New.
(free_private_thread_info, demand_private_info): New.
(PACKET_qXfer_threads, use_osdata_threads): New.
(struct thread_item, threads_parsing_context
(start_thread, end_thread, thread_attributes)
(thread_children, threads_children, threads_elements): New.
(remote_threads_info): Try qXfer:threads before anything
else.
(remote_protocol_packets): Register qXfer:threads.
(remote_open_1): Init use_osdata_threads.
(struct stop_reply): New field 'core'.
(remote_parse_stop_reply): Parse core number.
(process_stop_reply): Record core number.
(remote_xfer_partial): Handle qXfer:threads.
(remote_core_of_thread): New.
(init_remote_ops): Register remote_core_of_thread.
(_initialize_remote): Register qXfer:read.
* target.c (target_core_of_thread): New
* target.h (enum target_object): New value TARGET_OBJECT_THREADS.
(struct target_ops): New field to_core_of_threads.
(target_core_of_thread): Declare.
* gdbthread.h (struct thread_info): New field private_dtor.
* thread.c (print_thread_info): Report the core.
* ui-out.c (MAX_UI_OUT_LEVELS): Increase.
* utils.c (compare_positive_ints): New.
* features/threads.dtd: New.
* mi/mi-interp.c (mi_on_normal_stop): Report the core.
* mi/mi-main.c (struct collect_cores_data, collect_cores)
(do_nothing, free_vector_of_osdata_items)
(splay_tree_int_comparator, free_splay_tree): New.
(print_one_inferior_data): Implemented printing of selected
inferiors. Collect and print cores.
(output_cores): New.
(mi_cmd_list_thread_groups): Support --recurse. Permit specifying
thread groups together with --available.
gdbserver/
* linux-low.c (linux_core_of_thread): New.
(compare_ints, show_process, list_threads): New.
(linux_qxfer_osdata): Report threads and cores.
(linux_target_op): Register linux_core_of_thread.
* remote-utils.c (prepare_resume_reply): Report the core.
(buffer_xml_printf): Support %d specifier.
* server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
New.
(handle_query): Handle qXfer:threads. Announce availability
thereof.
* target.h (struct target_ops): New field core_for_threads.
gdb/doc
* gdb.texinfo (GDB/MI Thread Information): New.
(GDB/MI Async Records): Document the core field in *stopped.
(GDB/MI Miscellaneous Commands): Expand -list-thread-groups
documentation
(Process list): Document that osdata document may contain
threads.
(Remote Serial Protocol): Document qXfer:threads.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4d3e02a..31a0f76 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -444,7 +444,8 @@ 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/osdata.dtd
+ $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+ $(srcdir)/features/threads.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,
diff --git a/gdb/bcache.c b/gdb/bcache.c
index 1bc2eba..4badf6e 100644
--- a/gdb/bcache.c
+++ b/gdb/bcache.c
@@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
\f
/* Printing statistics. */
-static int
-compare_ints (const void *ap, const void *bp)
-{
- /* Because we know we're comparing two ints which are positive,
- there's no danger of overflow here. */
- return * (int *) ap - * (int *) bp;
-}
-
-
static void
print_percentage (int portion, int total)
{
@@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
/* To compute the median, we need the set of chain lengths sorted. */
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
- compare_ints);
+ compare_positive_ints);
qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
- compare_ints);
+ compare_positive_ints);
if (c->num_buckets > 0)
{
diff --git a/gdb/defs.h b/gdb/defs.h
index 5d251b5..b0a212d 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -417,6 +417,8 @@ char *ldirname (const char *filename);
char **gdb_buildargv (const char *);
+int compare_positive_ints (const void *ap, const void *bp);
+
/* From demangle.c */
extern void set_demangling_style (char *);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d0997b3..5057ade 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -15482,6 +15482,10 @@ are:
@tab @code{qXfer:siginfo:write}
@tab @code{set $_siginfo}
+@item @code{threads}
+@tab @code{qXfer:threads:read}
+@tab @code{info threads}
+
@item @code{get-thread-local-@*storage-address}
@tab @code{qGetTLSAddr}
@tab Displaying @code{__thread} variables
@@ -21768,6 +21772,7 @@ follow development on @email{gdb@@sourceware.org} and
* GDB/MI Stream Records::
* GDB/MI Async Records::
* GDB/MI Frame Information::
+* GDB/MI Thread Information::
@end menu
@node GDB/MI Result Records
@@ -21860,7 +21865,7 @@ several times, either for different threads, because it cannot resume
all threads together, or even for a single thread, if the thread must
be stepped though some code before letting it run freely.
-@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
+@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
The target has stopped. The @var{reason} field can have one of the
following values:
@@ -21900,7 +21905,9 @@ If all threads are stopped, the @var{stopped} field will have the
value of @code{"all"}. Otherwise, the value of the @var{stopped}
field will be a list of thread identifiers. Presently, this list will
always include a single thread, but frontend should be prepared to see
-several threads in the list.
+several threads in the list. The @var{core} field reports the
+processor core on which the stop event has happened. This field may be absent
+if such information is not available.
@item =thread-group-created,id="@var{id}"
@itemx =thread-group-exited,id="@var{id}"
@@ -21977,6 +21984,34 @@ corresponds to the frame's code address. This field may be absent.
@end table
+@node GDB/MI Thread Information
+@subsection @sc{gdb/mi} Thread Information
+
+Whenever @value{GDBN} has to report an information about a thread, it
+uses a tuple with the following fields:
+
+@table @code
+@item id
+The numeric id assigned to the thread by @value{GDBN}. This field is
+always present.
+
+@item target-id
+Target-specific string identifying the thread. This field is always present.
+
+@item details
+Additional information about the thread provided by the target.
+It is supposed to be human-readable and not interpreted by the
+frontend. This field is optional.
+
+@item state
+Either @samp{stopped} or @samp{running}, depending on whether the
+thread is presently running. This field is always present.
+
+@item core
+The value of this field is an integer number of the processor core the
+thread was last seen on. This field is optional.
+@end table
+
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Simple Examples
@@ -26289,20 +26324,84 @@ while the target is running.
@subheading Synopsis
@smallexample
--list-thread-groups [ --available ] [ @var{group} ]
+-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
@end smallexample
-When used without the @var{group} parameter, lists top-level thread
-groups that are being debugged. When used with the @var{group}
-parameter, the children of the specified group are listed. The
-children can be either threads, or other groups. At present,
-@value{GDBN} will not report both threads and groups as children at
-the same time, but it may change in future.
+Lists thread groups (@pxref{Thread groups}). When a single thread
+group is passed as the argument, lists the children of that group.
+When several thread group are passed, lists information about those
+thread groups. Without any parameters, lists information about all
+top-level thread groups.
+
+Normally, thread groups that are being debugged are reported.
+With the @samp{--available} option, @value{GDBN} reports thread groups
+available on the target.
+
+The output of this command may have either a @samp{threads} result or
+a @samp{groups} result. The @samp{thread} result has a list of tuples
+as value, with each tuple describing a thread (@pxref{GDB/MI Thread
+Information}). The @samp{groups} result has a list of tuples as value,
+each tuple describing a thread group. If top-level groups are
+requested (that is, no parameter is passed), or when several groups
+are passed, the output always has a @samp{groups} result. The format
+of the @samp{group} result is described below.
+
+To reduce the number of roundtrips it's possible to list thread groups
+together with their children, by passing the @samp{--recurse} option
+and the recursion depth. Presently, only recursion depth of 1 is
+permitted. If this option is present, then every reported thread group
+will also include its children, either as @samp{group} or
+@samp{threads} field.
+
+In general, any combination of option and parameters is permitted, with
+the following caveats:
+
+@itemize @bullet
+@item
+When a single thread group is passed, the output will typically
+be the @samp{threads} result. Because threads may not contain
+anything, the @samp{recurse} option will be ignored.
+
+@item
+When the @samp{--available} option is passed, limited information may
+be available. In particular, the list of threads of a process might
+be inaccessible. Further, specifying specific thread groups might
+not give any performance advantage over listing all thread groups.
+The frontend should assume that @samp{-list-thread-groups --available}
+is always an expensive operation and cache the results.
+
+@end itemize
+
+The @samp{groups} result is a list of tuples, where each tuple may
+have the following fields:
+
+@table @code
+@item id
+Identifier of the thread group. This field is always present.
+
+@item type
+The type of the thread group. At present, only @samp{process} is a
+valid type.
+
+@item pid
+The target-specific process identifier. This field is only present
+for thread groups of type @samp{process}.
-With the @samp{--available} option, instead of reporting groups that
-are been debugged, GDB will report all thread groups available on the
-target. Using the @samp{--available} option together with @var{group}
-is not allowed.
+@item num_children
+The number of children this thread group has. This field may be
+absent for an available thread group.
+
+@item threads
+This field has a list of tuples as value, each tuple describing a
+thread. It may be present if the @samp{--recurse} option is
+specified, and it's actually possible to obtain the threads.
+
+@item cores
+This field is a list of integers, each identifying a core that one
+thread of the group is running on. This field may be absent if
+such information is not available.
+
+@end table
@subheading Example
@@ -26316,6 +26415,16 @@ is not allowed.
@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
+-list-thread-groups --available
+^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
+-list-thread-groups --available --recurse 1
+ ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
+-list-thread-groups --available --recurse 1 17 18
+^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
@end smallexample
@subheading The @code{-interpreter-exec} Command
@@ -28065,6 +28174,7 @@ Show the current setting of the target wait timeout.
* File-I/O Remote Protocol Extension::
* Library List Format::
* Memory Map Format::
+* Thread List Format::
@end menu
@node Overview
@@ -28965,6 +29075,10 @@ If @var{n} is @samp{thread}, then @var{r} is the @var{thread-id} of
the stopped thread, as specified in @ref{thread-id syntax}.
@item
+If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
+the core on which the stop event was detected.
+
+@item
If @var{n} is a recognized @dfn{stop reason}, it describes a more
specific event that stopped the target. The currently defined stop
reasons are listed below. @var{aa} should be @samp{05}, the trap
@@ -28998,8 +29112,6 @@ logged execution events, because it has reached the end (or the
beginning when executing backward) of the log. The value of @var{r}
will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
for more information.
-
-
@end table
@item W @var{AA}
@@ -29539,6 +29651,12 @@ These are the currently defined stub features and their properties:
@tab @samp{-}
@tab Yes
+@item @samp{qXfer:threads:read}
+@tab No
+@tab @samp{-}
+@tab Yes
+
+
@item @samp{QNonStop}
@tab No
@tab @samp{-}
@@ -29622,6 +29740,10 @@ The remote stub understands the @samp{qXfer:siginfo:read} packet
The remote stub understands the @samp{qXfer:siginfo:write} packet
(@pxref{qXfer siginfo write}).
+@item qXfer:threads:read
+The remote stub understands the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}).
+
@item QNonStop
The remote stub understands the @samp{QNonStop} packet
(@pxref{QNonStop}).
@@ -29814,6 +29936,15 @@ This packet is not probed by default; the remote stub must request it,
by supplying an appropriate @samp{qSupported} response
(@pxref{qSupported}).
+@item qXfer:threads:read::@var{offset},@var{length}
+@anchor{qXfer threads read}
+Access the list of threads on target. @xref{Thread List Format}. The
+annex part of the generic @samp{qXfer} packet must be empty
+(@pxref{qXfer read}).
+
+This packet is not probed by default; the remote stub must request it,
+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}.
@@ -31827,6 +31958,30 @@ The formal DTD for memory map format is given below:
<!ATTLIST property name CDATA #REQUIRED>
@end smallexample
+@node Thread List Format
+@section Thread List Format
+@cindex thread list format
+
+To efficiently update the list of threads and their attributes,
+@value{GDBN} issues the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}) and obtains the XML document with
+the following structure:
+
+@smallexample
+<?xml version="1.0"?>
+<threads>
+ <thread id="id" core="0">
+ ... description ...
+ </thread>
+</threads>
+@end smallexample
+
+Each @samp{thread} element must have the @samp{id} attribute that
+identifies the thread (@pxref{thread-id syntax}). The
+@samp{core} attribute, if present, specifies which processor core
+the thread was last executing on. The content of the of @samp{thread}
+element is interpreted as human-readable auxilliary information.
+
@include agentexpr.texi
@node Target Descriptions
@@ -32386,6 +32541,7 @@ An example document is:
<column name="pid">1</column>
<column name="user">root</column>
<column name="command">/sbin/init</column>
+ <column name="cores">1,2,3</column>
</item>
</osdata>
@end smallexample
@@ -32393,7 +32549,9 @@ An example document is:
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,
+displayed by @value{GDBN}. The @samp{cores} column, if present,
+should contain a comma-separated list of cores that this process
+is running on. Target may provide additional columns,
which @value{GDBN} currently ignores.
@include gpl.texi
diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
new file mode 100644
index 0000000..20308ee
--- /dev/null
+++ b/gdb/features/threads.dtd
@@ -0,0 +1,13 @@
+<!-- Copyright (C) 2008, 2009 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. -->
+
+<!ELEMENT threads (thread*)>
+<!ATTLIST threads version CDATA #FIXED "1.0">
+
+<!ELEMENT thread (#PCDATA)>
+
+<!ATTLIST thread id CDATA #REQUIRED>
+<!ATTLIST thread core CDATA #IMPLIED>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index c20322e..5584691 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -140,6 +140,7 @@ static int check_removed_breakpoint (struct lwp_info *event_child);
static void *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int linux_core_of_thread (ptid_t ptid);
struct pending_signals
{
@@ -2802,6 +2803,175 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
#endif
static int
+compare_ints (const void *xa, const void *xb)
+{
+ int a = *(const int *)xa;
+ int b = *(const int *)xb;
+
+ return a - b;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+/* Given PID, iterates over all threads in that process.
+
+ Information about each thread, in a format suitable for qXfer:osdata:thread
+ is printed to BUFFER, if it's not NULL. BUFFER is assumed to be already
+ initialized, and the caller is responsible for finishing and appending '\0'
+ to it.
+
+ The list of cores that threads are running on is assigned to *CORES, if it
+ is not NULL. If no cores are found, *CORES will be set to NULL. Caller
+ should free *CORES. */
+
+static void
+list_threads (int pid, struct buffer *buffer, char **cores)
+{
+ int count = 0;
+ int allocated = 10;
+ int *core_numbers = xmalloc (sizeof (int) * allocated);
+ char pathname[128];
+ DIR *dir;
+ struct dirent *dp;
+ struct stat statbuf;
+
+ sprintf (pathname, "/proc/%d/task", pid);
+ if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+ {
+ dir = opendir (pathname);
+ if (!dir)
+ {
+ free (core_numbers);
+ return;
+ }
+
+ while ((dp = readdir (dir)) != NULL)
+ {
+ unsigned long lwp = strtoul (dp->d_name, NULL, 10);
+
+ if (lwp != 0)
+ {
+ unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
+
+ if (core != -1)
+ {
+ char s[sizeof ("4294967295")];
+ sprintf (s, "%u", core);
+
+ if (count == allocated)
+ {
+ allocated *= 2;
+ core_numbers = realloc (core_numbers,
+ sizeof (int) * allocated);
+ }
+ core_numbers[count++] = core;
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "<column name=\"core\">%s</column>"
+ "</item>", pid, dp->d_name, s);
+ }
+ else
+ {
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "</item>", pid, dp->d_name);
+ }
+ }
+ }
+ }
+
+ if (cores)
+ {
+ *cores = NULL;
+ if (count > 0)
+ {
+ struct buffer buffer2;
+ int *b;
+ int *e;
+ qsort (core_numbers, count, sizeof (int), compare_ints);
+
+ /* Remove duplicates. */
+ b = core_numbers;
+ e = unique (b, core_numbers + count);
+
+ buffer_init (&buffer2);
+
+ for (b = core_numbers; b != e; ++b)
+ {
+ char number[sizeof ("4294967295")];
+ sprintf (number, "%u", *b);
+ buffer_xml_printf (&buffer2, "%s%s",
+ (b == core_numbers) ? "" : ",", number);
+ }
+ buffer_grow_str0 (&buffer2, "");
+
+ *cores = buffer_finish (&buffer2);
+ }
+ }
+ free (core_numbers);
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+ char pathname[128];
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+
+ sprintf (pathname, "/proc/%d/cmdline", pid);
+
+ if ((f = fopen (pathname, "r")) != NULL)
+ {
+ size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+ if (len > 0)
+ {
+ char *cores = 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\">%d</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>",
+ pid,
+ username,
+ cmd);
+
+ /* This only collects core numbers, and does not print threads. */
+ list_threads (pid, NULL, &cores);
+
+ if (cores)
+ {
+ buffer_xml_printf (buffer,
+ "<column name=\"cores\">%s</column>", cores);
+ free (cores);
+ }
+
+ buffer_xml_printf (buffer, "</item>");
+ }
+ fclose (f);
+ }
+}
+
+static int
linux_qxfer_osdata (const char *annex,
unsigned char *readbuf, unsigned const char *writebuf,
CORE_ADDR offset, int len)
@@ -2811,10 +2981,16 @@ linux_qxfer_osdata (const char *annex,
static const char *buf;
static long len_avail = -1;
static struct buffer buffer;
+ int processes = 0;
+ int threads = 0;
DIR *dirp;
- if (strcmp (annex, "processes") != 0)
+ if (strcmp (annex, "processes") == 0)
+ processes = 1;
+ else if (strcmp (annex, "threads") == 0)
+ threads = 1;
+ else
return 0;
if (!readbuf || writebuf)
@@ -2827,7 +3003,10 @@ linux_qxfer_osdata (const char *annex,
len_avail = 0;
buf = NULL;
buffer_init (&buffer);
- buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ if (processes)
+ buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ else if (threads)
+ buffer_grow_str (&buffer, "<osdata type=\"threads\">");
dirp = opendir ("/proc");
if (dirp)
@@ -2846,37 +3025,16 @@ linux_qxfer_osdata (const char *annex,
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);
+ int pid = (int) strtoul (dp->d_name, NULL, 10);
- if ((f = fopen (pathname, "r")) != NULL)
+ if (processes)
{
- 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);
+ struct passwd *entry = getpwuid (statbuf.st_uid);
+ show_process (pid, entry ? entry->pw_name : "?", &buffer);
+ }
+ else if (threads)
+ {
+ list_threads (pid, &buffer, NULL);
}
}
}
@@ -3152,6 +3310,55 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
return ret;
}
+static int
+linux_core_of_thread (ptid_t ptid)
+{
+ char filename[sizeof ("/proc//task//stat")
+ + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
+ + 1];
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ sprintf (filename, "/proc/%d/task/%ld/stat",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid));
+ f = fopen (filename, "r");
+ if (!f)
+ return -1;
+
+ for (;;)
+ {
+ int n;
+ content = realloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ free (content);
+ fclose (f);
+
+ return core;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -3191,10 +3398,11 @@ static struct target_ops linux_target_ops = {
linux_start_non_stop,
linux_supports_multi_process,
#ifdef USE_THREAD_DB
- thread_db_handle_monitor_command
+ thread_db_handle_monitor_command,
#else
- NULL
+ NULL,
#endif
+ linux_core_of_thread
};
static void
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 9b5bad8..d638a3f 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
gdbserver to know what inferior_ptid is. */
if (1 || !ptid_equal (general_thread, ptid))
{
+ int core = -1;
/* In non-stop, don't change the general thread behind
GDB's back. */
if (!non_stop)
@@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
buf = write_ptid (buf, ptid);
strcat (buf, ";");
buf += strlen (buf);
+
+ if (the_target->core_for_thread)
+ core = (*the_target->core_for_thread) (ptid);
+ if (core != -1)
+ {
+ sprintf (buf, "core:");
+ buf += strlen (buf);
+ sprintf (buf, "%x", core);
+ strcat (buf, ";");
+ buf += strlen (buf);
+ }
}
}
@@ -1604,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...)
prev = f + 1;
}
break;
+ case 'd':
+ {
+ int i = va_arg (ap, int);
+ char b[sizeof ("4294967295")];
+
+ buffer_grow (buffer, prev, f - prev - 1);
+ sprintf (b, "%d", i);
+ buffer_grow_str (buffer, b);
+ prev = f + 1;
+ }
}
percent = 0;
}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 9254121..dfc92a8 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
}
}
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+ struct inferior_list_entry *thread;
+
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for (thread = all_threads.head; thread; thread = thread->next)
+ {
+ ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+ char ptid_s[100];
+ int core = -1;
+ char core_s[21];
+
+ write_ptid (ptid_s, ptid);
+
+ if (the_target->core_for_thread)
+ core = (*the_target->core_for_thread) (ptid);
+
+ if (core != -1)
+ {
+ sprintf (core_s, "%d", core);
+ buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+ ptid_s, core_s);
+ }
+ else
+ {
+ buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+ ptid_s);
+ }
+ }
+
+ buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+ unsigned char *readbuf,
+ CORE_ADDR offset, int length)
+{
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (annex && strcmp (annex, "") != 0)
+ return 0;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+ /* When asked for data at offset 0, generate everything and store into
+ 'result'. Successive reads will be served off 'result'. */
+ if (result)
+ free (result);
+
+ buffer_init (&buffer);
+
+ handle_threads_qxfer_proper (&buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (length > result_length - offset)
+ length = result_length - offset;
+
+ memcpy (readbuf, result + offset, length);
+
+ return length;
+
+}
+
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}
+ if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+ {
+ unsigned char *data;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+ char *annex;
+
+ require_running (own_buf);
+
+ /* Reject any annex; grab the offset and length. */
+ if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+ || annex[0] != '\0')
+ {
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ /* Read one extra byte, as an indicator of whether there is
+ more. */
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ data = malloc (len + 1);
+ if (!data)
+ return;
+ n = handle_threads_qxfer (annex, data, ofs, len + 1);
+ if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
+ strcat (own_buf, ";qXfer:threads:read+");
+
return;
}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index ad21eb7..0ee0d41 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -283,6 +283,9 @@ struct target_ops
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
+
+ /* Returns the core given a thread, or -1 if not known. */
+ int (*core_for_thread) (ptid_t);
};
extern struct target_ops *the_target;
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 4ab2111..cd24eaf 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -187,6 +187,10 @@ struct thread_info
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
+
+ /* Function that is called to free PRIVATE. If this is NULL, then
+ xfree will be called on PRIVATE. */
+ void (*private_dtor) (struct private_thread_info *);
};
/* Create an empty thread list, or empty the existing one. */
@@ -346,4 +350,6 @@ extern struct cleanup *make_cleanup_restore_current_thread (void);
INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
extern struct thread_info* inferior_thread (void);
+extern void update_thread_list (void);
+
#endif /* GDBTHREAD_H */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 0c95b95..78b1303 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -5423,6 +5423,66 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
return inf->aspace;
}
+/* Return the core for a thread. */
+
+static int
+linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct cleanup *back_to;
+ char *filename;
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ filename = xstrprintf ("/proc/%d/task/%ld/stat",
+ GET_PID (ptid), GET_LWP (ptid));
+ back_to = make_cleanup (xfree, filename);
+
+ f = fopen (filename, "r");
+ if (!f)
+ {
+ do_cleanups (back_to);
+ return -1;
+ }
+
+ make_cleanup_fclose (f);
+
+ for (;;)
+ {
+ int n;
+ content = xrealloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ make_cleanup (xfree, content);
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ /* If the first field after program name has index 0, then core number is
+ the field with index 36. There's no constant for that anywhere. */
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ do_cleanups (back_to);
+
+ return core;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -5463,6 +5523,8 @@ linux_nat_add_target (struct target_ops *t)
t->to_supports_multi_process = linux_nat_supports_multi_process;
+ t->to_core_of_thread = linux_nat_core_of_thread;
+
/* We don't change the stratum; this target will sit at
process_stratum and thread_db will set at thread_stratum. This
is a little strange, since this is a multi-threaded-capable
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 96e458c..41388bb 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
if (print_frame)
{
+ int core;
if (uiout != mi_uiout)
{
/* The normal_stop function has printed frame information into
@@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
}
else
ui_out_field_string (mi_uiout, "stopped-threads", "all");
+
+ core = target_core_of_thread (inferior_ptid);
+ if (core != -1)
+ ui_out_field_int (mi_uiout, "core", core);
}
fputs_unfiltered ("*stopped", raw_stdout);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 16f6102..aee246c 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -50,6 +50,7 @@
#include "valprint.h"
#include "inferior.h"
#include "osdata.h"
+#include "splay-tree.h"
#include <ctype.h>
#include <sys/time.h>
@@ -360,11 +361,55 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
print_thread_info (uiout, thread, -1);
}
+struct collect_cores_data
+{
+ int pid;
+
+ VEC (int) *cores;
+};
+
+static int
+collect_cores (struct thread_info *ti, void *xdata)
+{
+ struct collect_cores_data *data = xdata;
+
+ if (ptid_get_pid (ti->ptid) == data->pid)
+ {
+ int core = target_core_of_thread (ti->ptid);
+ if (core != -1)
+ VEC_safe_push (int, data->cores, core);
+ }
+
+ return 0;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+struct print_one_inferior_data
+{
+ int recurse;
+ VEC (int) *inferiors;
+};
+
static int
-print_one_inferior (struct inferior *inferior, void *arg)
+print_one_inferior (struct inferior *inferior, void *xdata)
{
- if (inferior->pid != 0)
+ struct print_one_inferior_data *top_data = xdata;
+
+ if (VEC_empty (int, top_data->inferiors)
+ || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
+ VEC_length (int, top_data->inferiors), sizeof (int),
+ compare_positive_ints))
{
+ struct collect_cores_data data;
struct cleanup *back_to
= make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -372,81 +417,299 @@ print_one_inferior (struct inferior *inferior, void *arg)
ui_out_field_string (uiout, "type", "process");
ui_out_field_int (uiout, "pid", inferior->pid);
+ data.pid = inferior->pid;
+ data.cores = 0;
+ iterate_over_threads (collect_cores, &data);
+
+ if (!VEC_empty (int, data.cores))
+ {
+ int elt;
+ int i;
+ int *b, *e;
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_list_begin_end (uiout, "cores");
+
+ qsort (VEC_address (int, data.cores),
+ VEC_length (int, data.cores), sizeof (int),
+ compare_positive_ints);
+
+ b = VEC_address (int, data.cores);
+ e = b + VEC_length (int, data.cores);
+ e = unique (b, e);
+
+ for (; b != e; ++b)
+ ui_out_field_int (uiout, NULL, *b);
+
+ do_cleanups (back_to_2);
+ }
+
+ if (top_data->recurse)
+ print_thread_info (uiout, -1, inferior->pid);
+
do_cleanups (back_to);
}
return 0;
}
-void
-mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+/* Output a field named 'cores' with a list as the value. The elements of
+ the list are obtained by splitting 'cores' on comma. */
+
+static void
+output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
{
- struct cleanup *back_to;
- int available = 0;
- char *id = NULL;
+ struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+ field_name);
+ char *cores = xstrdup (xcores);
+ char *p = cores;
- if (argc > 0 && strcmp (argv[0], "--available") == 0)
- {
- ++argv;
- --argc;
- available = 1;
- }
+ make_cleanup (xfree, cores);
- if (argc > 0)
- id = argv[0];
+ for (p = strtok (p, ","); p; p = strtok (NULL, ","))
+ ui_out_field_string (uiout, NULL, p);
- back_to = make_cleanup (null_cleanup, NULL);
+ do_cleanups (back_to);
+}
- if (available && id)
- {
- error (_("Can only report top-level available thread groups"));
- }
- else if (available)
- {
- struct osdata *data;
- struct osdata_item *item;
- int ix_items;
+static void
+free_vector_of_ints (void *xvector)
+{
+ VEC (int) **vector = xvector;
+ VEC_free (int, *vector);
+}
- data = get_osdata ("processes");
- make_cleanup_osdata_free (data);
+static void
+do_nothing (splay_tree_key k)
+{
+}
- make_cleanup_ui_out_list_begin_end (uiout, "groups");
+static void
+free_vector_of_osdata_items (splay_tree_value xvalue)
+{
+ VEC (osdata_item_s) *value = (VEC (osdata_item_s) *) xvalue;
+ /* We don't free the items itself, it will be done separately. */
+ VEC_free (osdata_item_s, value);
+}
+
+static int
+splay_tree_int_comparator (splay_tree_key xa, splay_tree_key xb)
+{
+ int a = xa;
+ int b = xb;
+ return a - b;
+}
+
+static void
+free_splay_tree (void *xt)
+{
+ splay_tree t = xt;
+ splay_tree_delete (t);
+}
+
+static void
+list_available_thread_groups (VEC (int) *ids, int recurse)
+{
+ struct osdata *data;
+ struct osdata_item *item;
+ int ix_items;
+ /* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
+ The vector contains information about all threads for the given
+ pid. */
+ splay_tree tree;
+
+ /* get_osdata will throw if it cannot return data. */
+ data = get_osdata ("processes");
+ make_cleanup_osdata_free (data);
+
+ if (recurse)
+ {
+ struct osdata *threads = get_osdata ("threads");
+ make_cleanup_osdata_free (threads);
+
+ tree = splay_tree_new (splay_tree_int_comparator,
+ do_nothing,
+ free_vector_of_osdata_items);
+ make_cleanup (free_splay_tree, tree);
for (ix_items = 0;
- VEC_iterate (osdata_item_s, data->items,
+ VEC_iterate (osdata_item_s, threads->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");
+ int pid_i = strtoul (pid, NULL, 0);
+ VEC (osdata_item_s) *vec = 0;
+
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (!n)
+ {
+ VEC_safe_push (osdata_item_s, vec, item);
+ splay_tree_insert (tree, pid_i, (splay_tree_value)vec);
+ }
+ else
+ {
+ vec = (VEC (osdata_item_s) *) n->value;
+ VEC_safe_push (osdata_item_s, vec, item);
+ n->value = (splay_tree_value) vec;
+ }
+ }
+ }
+
+ 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;
+
+ const char *pid = get_osdata_column (item, "pid");
+ const char *cmd = get_osdata_column (item, "command");
+ const char *user = get_osdata_column (item, "user");
+ const char *cores = get_osdata_column (item, "cores");
+
+ int pid_i = strtoul (pid, NULL, 0);
+
+ /* At present, the target will return all available processes
+ and if information about specific ones was required, we filter
+ undesired processes here. */
+ if (ids && bsearch (&pid_i, VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints) == NULL)
+ continue;
+
- 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);
+ back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
- do_cleanups (back_to);
+ 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);
+ if (cores)
+ output_cores (uiout, "cores", cores);
+
+ if (recurse)
+ {
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (n)
+ {
+ VEC (osdata_item_s) *children = (VEC (osdata_item_s) *) n->value;
+ struct osdata_item *child;
+ int ix_child;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+ for (ix_child = 0;
+ VEC_iterate (osdata_item_s, children, ix_child, child);
+ ++ix_child)
+ {
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *tid = get_osdata_column (child, "tid");
+ const char *tcore = get_osdata_column (child, "core");
+ ui_out_field_string (uiout, "id", tid);
+ if (tcore)
+ ui_out_field_string (uiout, "core", tcore);
+
+ do_cleanups (back_to_2);
+ }
+ }
}
+
+ do_cleanups (back_to);
}
- else if (id)
+}
+
+void
+mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+{
+ struct cleanup *back_to;
+ int available = 0;
+ int recurse = 0;
+ VEC (int) *ids = 0;
+
+ enum opt
{
- int pid = atoi (id);
+ AVAILABLE_OPT, RECURSE_OPT
+ };
+ static struct mi_opt opts[] =
+ {
+ {"-available", AVAILABLE_OPT, 0},
+ {"-recurse", RECURSE_OPT, 1},
+ { 0, 0, 0 }
+ };
+
+ int optind = 0;
+ char *optarg;
+
+ while (1)
+ {
+ int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+ &optind, &optarg);
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case AVAILABLE_OPT:
+ available = 1;
+ break;
+ case RECURSE_OPT:
+ if (strcmp (optarg, "0") == 0)
+ ;
+ else if (strcmp (optarg, "1") == 0)
+ recurse = 1;
+ else
+ error ("only '0' and '1' are valid values for the '--recurse' option");
+ break;
+ }
+ }
+
+ for (; optind < argc; ++optind)
+ {
+ char *end;
+ int inf = strtoul (argv[optind], &end, 0);
+ if (*end != '\0')
+ error ("invalid group id '%s'", argv[optind]);
+ VEC_safe_push (int, ids, inf);
+ }
+ if (VEC_length (int, ids) > 1)
+ qsort (VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints);
+
+ back_to = make_cleanup (free_vector_of_ints, &ids);
+
+ if (available)
+ {
+ list_available_thread_groups (ids, recurse);
+ }
+ else if (VEC_length (int, ids) == 1)
+ {
+ /* Local thread groups, single id. */
+ int pid = *VEC_address (int, ids);
if (!in_inferior_list (pid))
- error ("Invalid thread group id '%s'", id);
- print_thread_info (uiout, -1, pid);
+ error ("Invalid thread group id '%d'", pid);
+ print_thread_info (uiout, -1, pid);
}
else
{
+ struct print_one_inferior_data data;
+ data.recurse = recurse;
+ data.inferiors = ids;
+
+ /* Local thread groups. Either no explicit ids -- and we
+ print everything, or several explicit ids. In both cases,
+ we print more than one group, and have to use 'groups'
+ as the top-level element. */
make_cleanup_ui_out_list_begin_end (uiout, "groups");
- iterate_over_inferiors (print_one_inferior, NULL);
+ update_thread_list ();
+ iterate_over_inferiors (print_one_inferior, &data);
}
-
+
do_cleanups (back_to);
}
diff --git a/gdb/remote.c b/gdb/remote.c
index 7af67e5..ffd4ad3 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -60,6 +60,7 @@
#include "remote-fileio.h"
#include "gdb/fileio.h"
#include "gdb_stat.h"
+#include "xml-support.h"
#include "memory-map.h"
@@ -303,6 +304,20 @@ struct remote_state
int ctrlc_pending_p;
};
+/* Private data that we'll store in (struct thread_info)->private. */
+struct private_thread_info
+{
+ char *extra;
+ int core;
+};
+
+static void
+free_private_thread_info (struct private_thread_info *info)
+{
+ xfree (info->extra);
+ xfree (info);
+}
+
/* Returns true if the multi-process extensions are in effect. */
static int
remote_multi_process_p (struct remote_state *rs)
@@ -1054,6 +1069,7 @@ enum {
PACKET_qXfer_spu_read,
PACKET_qXfer_spu_write,
PACKET_qXfer_osdata,
+ PACKET_qXfer_threads,
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
@@ -1359,6 +1375,24 @@ remote_notice_new_inferior (ptid_t currthread, int running)
}
}
+/* Return the private thread data, creating it if necessary. */
+
+struct private_thread_info *
+demand_private_info (ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+
+ gdb_assert (info);
+
+ if (!info->private)
+ {
+ info->private = xmalloc (sizeof (*(info->private)));
+ info->private_dtor = free_private_thread_info;
+ }
+
+ return info->private;
+}
+
/* Call this function as a result of
1) A halt indication (T packet) containing a thread id
2) A direct query of currthread
@@ -1369,12 +1403,6 @@ static void
record_currthread (ptid_t currthread)
{
general_thread = currthread;
-
- if (ptid_equal (currthread, minus_one_ptid))
- /* We're just invalidating the local thread mirror. */
- return;
-
- remote_notice_new_inferior (currthread, 0);
}
static char *last_pass_packet;
@@ -2303,6 +2331,80 @@ remote_find_new_threads (void)
CRAZY_MAX_THREADS);
}
+#if defined(HAVE_LIBEXPAT)
+
+typedef struct thread_item
+{
+ ptid_t ptid;
+ char *extra;
+ int core;
+} thread_item_t;
+DEF_VEC_O(thread_item_t);
+
+struct threads_parsing_context
+{
+ VEC (thread_item_t) *items;
+};
+
+static void
+start_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct threads_parsing_context *data = user_data;
+
+ struct thread_item item;
+ char *id;
+
+ id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ item.ptid = read_ptid (id, NULL);
+
+ if (VEC_length (gdb_xml_value_s, attributes) > 1)
+ item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
+ else
+ item.core = -1;
+
+ item.extra = 0;
+
+ VEC_safe_push (thread_item_t, data->items, &item);
+}
+
+static void
+end_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, const char *body_text)
+{
+ struct threads_parsing_context *data = user_data;
+
+ if (body_text && *body_text)
+ VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
+}
+
+const struct gdb_xml_attribute thread_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element thread_children[] = {
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_children[] = {
+ { "thread", thread_attributes, thread_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ start_thread, end_thread },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_elements[] = {
+ { "threads", NULL, threads_children,
+ GDB_XML_EF_NONE, NULL, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+#endif
+
/*
* Find all threads for info threads command.
* Uses new thread protocol contributed by Cisco.
@@ -2320,6 +2422,61 @@ remote_threads_info (struct target_ops *ops)
if (remote_desc == 0) /* paranoia */
error (_("Command can only be used when connected to the remote target."));
+#if defined(HAVE_LIBEXPAT)
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ char *xml = target_read_stralloc (¤t_target,
+ TARGET_OBJECT_THREADS, NULL);
+
+ struct cleanup *back_to = make_cleanup (xfree, xml);
+ if (xml && *xml)
+ {
+ struct gdb_xml_parser *parser;
+ struct threads_parsing_context context;
+ struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+ context.items = 0;
+ parser = gdb_xml_create_parser_and_cleanup (_("threads"),
+ threads_elements,
+ &context);
+
+ gdb_xml_use_dtd (parser, "threads.dtd");
+
+ if (gdb_xml_parse (parser, xml) == 0)
+ {
+ int i;
+ struct thread_item *item;
+
+ for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
+ {
+ if (!ptid_equal (item->ptid, null_ptid))
+ {
+ struct private_thread_info *info;
+ /* In non-stop mode, we assume new found threads
+ are running until proven otherwise with a
+ stop reply. In all-stop, we can only get
+ here if all threads are stopped. */
+ int running = non_stop ? 1 : 0;
+
+ remote_notice_new_inferior (item->ptid, running);
+
+ info = demand_private_info (item->ptid);
+ info->core = item->core;
+ info->extra = item->extra;
+ item->extra = 0;
+ }
+ xfree (item->extra);
+ }
+ }
+
+ VEC_free (thread_item_t, context.items);
+ }
+
+ do_cleanups (back_to);
+ return;
+ }
+#endif
+
if (use_threadinfo_query)
{
putpkt ("qfThreadInfo");
@@ -2392,6 +2549,15 @@ remote_threads_extra_info (struct thread_info *tp)
server doesn't know about it. */
return NULL;
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ struct thread_info *info = find_thread_ptid (tp->ptid);
+ if (info && info->private)
+ return info->private->extra;
+ else
+ return NULL;
+ }
+
if (use_threadextra_query)
{
char *b = rs->buf;
@@ -3152,6 +3318,8 @@ static struct protocol_feature remote_protocol_features[] = {
PACKET_qXfer_spu_write },
{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
PACKET_qXfer_osdata },
+ { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
+ PACKET_qXfer_threads },
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
PACKET_QPassSignals },
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -4262,6 +4430,8 @@ struct stop_reply
int solibs_changed;
int replay_event;
+
+ int core;
};
/* The list of already fetched and acknowledged stop events. */
@@ -4425,6 +4595,7 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
event->replay_event = 0;
event->stopped_by_watchpoint_p = 0;
event->regcache = NULL;
+ event->core = -1;
switch (buf[0])
{
@@ -4451,7 +4622,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
/* If this packet is an awatch packet, don't parse the 'a'
as a register number. */
- if (strncmp (p, "awatch", strlen("awatch")) != 0)
+ if (strncmp (p, "awatch", strlen("awatch")) != 0
+ && strncmp (p, "core", strlen ("core") != 0))
{
/* Read the ``P'' register number. */
pnum = strtol (p, &p_temp, 16);
@@ -4497,6 +4669,12 @@ Packet: '%s'\n"),
if (p_temp)
p = p_temp;
}
+ else if (strncmp (p, "core", p1 - p) == 0)
+ {
+ ULONGEST c;
+ p = unpack_varlen_hex (++p1, &c);
+ event->core = c;
+ }
else
{
/* Silently skip unknown optional info. */
@@ -4706,6 +4884,7 @@ process_stop_reply (struct stop_reply *stop_reply,
struct target_waitstatus *status)
{
ptid_t ptid;
+ struct thread_info *info;
*status = stop_reply->ws;
ptid = stop_reply->ptid;
@@ -4737,6 +4916,7 @@ process_stop_reply (struct stop_reply *stop_reply,
remote_watch_data_address = stop_reply->watch_data_address;
remote_notice_new_inferior (ptid, 0);
+ demand_private_info (ptid)->core = stop_reply->core;
}
stop_reply_xfree (stop_reply);
@@ -7579,6 +7759,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
(ops, "osdata", annex, readbuf, offset, len,
&remote_protocol_packets[PACKET_qXfer_osdata]);
+ case TARGET_OBJECT_THREADS:
+ gdb_assert (annex == NULL);
+ return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
+ &remote_protocol_packets[PACKET_qXfer_threads]);
+
default:
return -1;
}
@@ -8895,6 +9080,15 @@ remote_supports_cond_tracepoints (void)
return rs->cond_tracepoints;
}
+static int
+remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+ if (info && info->private)
+ return info->private->core;
+ return -1;
+}
+
static void
init_remote_ops (void)
{
@@ -8958,6 +9152,7 @@ Specify the serial device it is connected to\n\
remote_ops.to_terminal_ours = remote_terminal_ours;
remote_ops.to_supports_non_stop = remote_supports_non_stop;
remote_ops.to_supports_multi_process = remote_supports_multi_process;
+ remote_ops.to_core_of_thread = remote_core_of_thread;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -9317,6 +9512,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
"qXfer:osdata:read", "osdata", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
+ "qXfer:threads:read", "threads", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
"qXfer:siginfo:read", "read-siginfo-object", 0);
diff --git a/gdb/target.c b/gdb/target.c
index 38cd508..899f61a 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3024,6 +3024,26 @@ target_store_registers (struct regcache *regcache, int regno)
noprocess ();
}
+int
+target_core_of_thread (ptid_t ptid)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_core_of_thread != NULL)
+ {
+ int retval = t->to_core_of_thread (t, ptid);
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
+ PIDGET (ptid), retval);
+ return retval;
+ }
+ }
+
+ return -1;
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
diff --git a/gdb/target.h b/gdb/target.h
index 9a89b93..a4a0b18 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -255,6 +255,8 @@ enum target_object
/* Extra signal info. Usually the contents of `siginfo_t' on unix
platforms. */
TARGET_OBJECT_SIGNAL_INFO,
+ /* The list of threads that are being debugged. */
+ TARGET_OBJECT_THREADS,
/* Possible future objects: TARGET_OBJECT_FILE, ... */
};
@@ -597,6 +599,15 @@ struct target_ops
struct address_space *(*to_thread_address_space) (struct target_ops *,
ptid_t);
+ /* Return the core that thread PTID is on. For a stopped thread, should
+ return the core the thread was last running on. For a running thread,
+ should return one of the cores that the thread was running between
+ the call to this function and return -- and if it was running on
+ several cores, any other may be returned.
+ If the core cannot be determined -- either for the specified thread, or
+ right now, or in this debug session, or for this target -- return -1. */
+ int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
+
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@@ -1246,6 +1257,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
(*current_target.to_log_command) (p); \
while (0)
+
+extern int target_core_of_thread (ptid_t ptid);
+
/* Routines for maintenance of the target structures...
add_target: Add a target to the list of all possible targets.
diff --git a/gdb/thread.c b/gdb/thread.c
index 80c3786..16a207c 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -114,10 +114,13 @@ free_thread (struct thread_info *tp)
{
clear_thread_inferior_resources (tp);
- /* FIXME: do I ever need to call the back-end to give it a
- chance at this private data before deleting the thread? */
if (tp->private)
- xfree (tp->private);
+ {
+ if (tp->private_dtor)
+ tp->private_dtor (tp->private);
+ else
+ xfree (tp->private);
+ }
xfree (tp);
}
@@ -468,8 +471,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
struct cleanup *cleanup_chain;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
@@ -748,8 +750,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
char *extra_info;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
current_ptid = inferior_ptid;
/* We'll be switching threads temporarily. */
@@ -759,6 +760,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
for (tp = thread_list; tp; tp = tp->next)
{
struct cleanup *chain2;
+ int core;
if (requested_thread != -1 && tp->num != requested_thread)
continue;
@@ -817,6 +819,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
ui_out_field_string (uiout, "state", state);
}
+ core = target_core_of_thread (tp->ptid);
+ if (ui_out_is_mi_like_p (uiout) && core != -1)
+ ui_out_field_int (uiout, "core", core);
+
do_cleanups (chain2);
}
@@ -1058,8 +1064,7 @@ thread_apply_all_command (char *cmd, int from_tty)
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
old_chain = make_cleanup_restore_current_thread ();
@@ -1245,6 +1250,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
return GDB_RC_OK;
}
+void
+update_thread_list (void)
+{
+ prune_threads ();
+ target_find_new_threads ();
+}
+
/* Commands with a prefix of `thread'. */
struct cmd_list_element *thread_cmd_list = NULL;
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index db8c894..e5fd474 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -44,7 +44,7 @@ struct ui_out_hdr
is always available. Stack/nested level 0 is reserved for the
top-level result. */
-enum { MAX_UI_OUT_LEVELS = 6 };
+enum { MAX_UI_OUT_LEVELS = 8 };
struct ui_out_level
{
diff --git a/gdb/utils.c b/gdb/utils.c
index 4c03655..f72a9e3 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
return argv;
}
+int
+compare_positive_ints (const void *ap, const void *bp)
+{
+ /* Because we know we're comparing two ints which are positive,
+ there's no danger of overflow here. */
+ return * (int *) ap - * (int *) bp;
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_utils;
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2010-01-04 15:11 ` Vladimir Prus
@ 2010-01-08 20:30 ` Pedro Alves
2010-01-09 12:41 ` Vladimir Prus
0 siblings, 1 reply; 17+ messages in thread
From: Pedro Alves @ 2010-01-08 20:30 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
On Monday 04 January 2010 15:11:04, Vladimir Prus wrote:
> On Thursday 31 December 2009 14:50:57 Pedro Alves wrote:
> > > > > + /* Update the core associated with a thread when we process stop
> > > > > + event in that thread. */
> > > > > + info = find_thread_ptid (ptid);
> > > > > + if (info && info->private)
> > > > > + info->private->core = stop_reply->core;
> > > >
> > > > Consider all-stop, multi-threaded applications:
> > > >
> > > > What about the core of the other threads?
> > > > Shouldn't they all be invalidated?
> > > > Won't then be stale?
> > > >
> > > > (see comment below)
> > >
> > > Yes, it will be stale. However, it's approximation only, and the only two
> > > places where cores number is used is *stopped response (which uses core of
> > > the stopped thread, updated above) and -list-thread-groups (which updates
> > > this information anyway). It does not seem right to additionally update
> > > this even if nothing will use it.
> >
> > Not update, _invalidate_: tag that the core is unknown and needs fetching
> > from the target somehow (say, adding a info->private->core_p field?
> > Or setting it to info->private->core = -2?). The invalidation could be
> > done on resume instead of on stop (e.g., linux-nat.c:resume_callback). I
> > believe it is bad design practice to design an API and assume
> > what it's callers do with it.
>
> I think it's perfectly valid to optimize the API for the use cases we expect.
I agree, but I'm not talking about optimization, I'm talking about
not handling valid input, or relying on undocumented conditions of
when the API can and should be used. See below for the current
example of what I'm talking about.
> Otherwise, it's easy to introduce complexity and pessimization that is not
> necessary.
This is not a valid argument for not getting things done right in
the first place. That's only a guideline. Certainly considering all
valid inputs does not necessarily mean complexity and pessimization.
I'll try to explain what I think isn't correct with either the
new target method's remote implementation and its assumptions
a bit further below, or with the documentation of the new target
hook.
>
> > We can certainly come up with other
> > target_core_of_thread callers in the near future, and at that point,
> > we'll hit this issue. But all that lead me to the real question I have in
> > my mind: how will we update the core of the thread, when we implement what
> > I just described? Will we make remote_core_of_thread update all
> > threads, or come up with a special packet to get at the core of
> > a thread? Probably the former is okay (albeit, slow, although
> > done only at most once), but if not, I'd rather consider the
> > packet upfront, than leave a hole in the design --- if we
> > considered such packet, the case for qXfer:threads would get
> > smaller (it becomes only justifiable due to supposedly fewer
> > packet roundtrips to list all threads and their extra info).
>
> For a start, I certainly find that a single packet that returns all information
> about threads -- which a frontend might be requesting on each step -- is a good
> idea regardless of "core awareness". As for invalidation, I think it's reasonable
> to fetch information about all threads, just because the time to get that information
> is probably less than communication latency for a single packet.
Right, agreed on that then. Good that it's considered.
>
> So, for avoidance of doubt, how about this scheme:
> - core information of each thread is invalidated when that thread is resumed
> (where resumption is reported by the 'target_resumed' observer)
> - remote_core_of_thread checks if it's asked for a core of the most recently
> stopped thread. If so, it reports it. Otherwise, it refreshes the thread list,
> and then reports core it has fetched
> - this all is for all-stop only. For non-stop, we get stop packet of each stopped
> thread and therefore remote_core_of_thread never needs to fetch information
> about all threads.
>
> If this sounds OK, I'll implement such a scheme.
Sounds good, although I still don't see why you need to special
case the last-reported-thread. One concern about non-stop and
running threads though. Looking at the new documentation of
the new target interface makes it clearer what I'm talking about:
/* Return the core that thread PTID is on. For a stopped thread, should
return the core the thread was last running on. For a running thread,
^^^^^^^^^^^^^^^^^^^^^
should return one of the cores that the thread was running between
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
the call to this function and return -- and if it was running on
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
several cores, any other may be returned.
If the core cannot be determined -- either for the specified thread, or
right now, or in this debug session, or for this target -- return -1. */
int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
Specifically, the underlined bit. If we look at the current
remote.c implementation of that method,
static int
remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
{
struct thread_info *info = find_thread_ptid (ptid);
if (info && info->private)
return info->private->core;
else if (ptid_equal (ptid, thread_of_last_core))
return last_core;
return -1;
}
we see that there's nothing here that considers the fact
that `info->private->core' gets stale real quick, and does
not actually represent "one of the cores that the thread
was running between the call to this function and return".
This is relying on the callers of target_core_of_thread
having refreshed the thread list, but there's
no reason a native target would need to update the
thread list to query the core of a single thread
when it has direct access to that info.
So, we either update the documentation of
target_core_of_thread's assumptions to match reality,
which feels a bit hackish given that it is a remote.c
related assumption, or we change the remote
implementation to refetch the data if the thread is
running, which has the problem that with the
current API, for every running thread, you'd re-update
the whole thread list (and this is what made me
wonder before how would you consider the protocol
should handle updating the core data of a
single thread).
> Well, while I presumably could have split /proc/cpuinfo on empty lines,
> and somehow print that in 'info os cores' I am unsure if we can promise
> any fields to be present in the output, given that man:proc does not
> mention any field except of "processor" and "bogomips". So, all that
> "info os cores" can meaningfully print is the numeric ids of cores,
> and this does not add very much to the information already available
> to the frontend.
E.g., if all threads in a system are pinned to / running on only one
of multiple cores, the frontend does not have have any way to know
about all the cores on the target system. No way to show to the
user 40 threads on core 1, and 0 on core 2. It simply has no
info about core 2. That's fine, I was only wondering if you
had considered it; we can add this at any time.
> > The function inits the buffer, finishes the buffer and appends \0 to it, so
> > I'm confused by that comment.
>
> The function does not init or finish BUFFER. It does init/finish a second buffer, but
> that buffer is only used internally. Am I missing something?
No. I was.
> > Could you please add an empty new line between function describing
> > comment and function definition (here and elsewhere)? We're trying
> > to follow that style everywhere in gdb.
>
> Is that for function definitions only? Or also for function declarations
> and variable declarations/definitions?
AFAIK, we've only been trying to standardize that on function
definitions.
> The primary problem is that when we process a stop reply
> for a new thread, the thread is not created and added to gdb
> thread table yet, so it's not possible to set its info->private.
> I don't see any way around this.
The remote_notice_new_inferior calls always return
with the possibly new thread added to the list already. From
the patched code:
remote_threads_info:
...
remote_notice_new_inferior (item->ptid, running);
info = find_thread_ptid (item->ptid);
if (info)
{
if (!info->private) {
info->private = (struct private_thread_info *)
xmalloc (sizeof (struct private_thread_info));
info->private_dtor = free_private_thread_info;
}
info->private->extra = item->extra;
item->extra = 0;
info->private->core = item->core;
}
process_stop_reply:
...
/* Update the core associated with a thread when we process stop
event in that thread. */
info = find_thread_ptid (ptid);
if (info && info->private)
{
info->private->core = stop_reply->core;
}
else
{
last_core = stop_reply->core;
thread_of_last_core = ptid;
}
remote_notice_new_inferior (ptid, 0);
You can, for example, pass the core to remote_notice_new_inferior,
and have remote_notice_new_inferior itself update the core field.
All the remote_notice_new_inferior calls other than the
one from within record_currthread have the new `core' handy,
and, I think the remote_notice_new_inferior call within
record_currthread is now always a nop, since the only case that
mattered (remote_wait_as) already calls remote_notice_new_inferior
from within process_stop_reply when handling 'T', etc. packets,
so it could be removed. Do you confirm this?
> We can, in theory, make
> remote_core_of_thread request the list of all threads even when
> called for a thread that has just stopped, but that sounds wasteful.
That would indeed be wasteful.
> > I think info->private->extra is leaking when threads are deleted,
> > because gdb/threads.c simply xfree's ->private. I guess you're the
> > lucky first to need a destructor for thread private data.
> > nto-procfs.c was close, but avoids it by using a poor man's flexible
> > array member (nto-tdep.h).
>
> Done.
Thanks. Only a couple of small comments to the patch itself:
> -several threads in the list.
> +several threads in the list. The @var{core} field reports the
> +processor core on which the stop event has happened. This field may be absent
> +if such information is not available.
s/stop event/stop event/
Formatting here is wrong:
> + if (!info->private) {
> + info->private = (struct private_thread_info *)
> + xmalloc (sizeof (struct private_thread_info));
> + info->private_dtor = free_private_thread_info;
> + }
( If you write that as `info->private = xmalloc (sizeof (*info->private))'
it's a shorter line. )
Also:
$ quilt refresh
Warning: trailing whitespace in line 118 of gdb/thread.c
Warning: trailing whitespace in lines 602,603,606 of gdb/target.h
Refreshed patch core3.diff
--
Pedro Alves
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2009-12-31 11:51 ` Pedro Alves
@ 2010-01-04 15:11 ` Vladimir Prus
2010-01-08 20:30 ` Pedro Alves
0 siblings, 1 reply; 17+ messages in thread
From: Vladimir Prus @ 2010-01-04 15:11 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: Text/Plain, Size: 12156 bytes --]
On Thursday 31 December 2009 14:50:57 Pedro Alves wrote:
> On Monday 21 December 2009 14:48:18, Vladimir Prus wrote:
> > On Thursday 17 December 2009 18:33:21 Pedro Alves wrote:
> >
> > > > +@item qXfer:threads:read::@var{offset},@var{length}
> > > > +@anchor{qXfer threads read}
> > > > +Access the list of threads on target. @xref{Thread List Format}. The
> > > > +annex part of the generic @samp{qXfer} packet must be empty
> > > > +(@pxref{qXfer read}).
> > > > +
> > > > +This packet is not probed by default; the remote stub must request it,
> > >
> > > What's the advantage of this, rather than probing it? I would
> > > see it nice for GDB to know in advance that the target can
> > > report core data, but that's not that reporting support
> > > for this packet means, since the "core" field is optional.
> >
> > Every other qXfer packet works this way. And it does not seem like much burden
> > on the stub to announce that this packet is available.
>
> True. I'm seeing a proliferation of qSupported additions (with
> the tracepoint support going in), and I get the feeling that
> qSupported will end up being the largest packet a stub has
> to receive/send. Maybe it's an unfounded concern.
I think we still have some room before we run out of packet size limits. Anyway,
it is not a problem that is introduced, or made significantly worse by this
patch, so I would rather not solve it at present.
> > > > + if (core != -1)
> > > > + {
> > > > + char s[11];
> > > > + sprintf (s, "%u", core);
> > > > +
> > > > + if (count == allocated)
> > > > + core_numbers = realloc (core_numbers,
> > > > + sizeof (int ) * (allocated *= 2));
> > >
> > > No space after int. Please move the 'allocated *= 2' in its own
> > > statement. My GM hat tells me that I should point out that there are a
> > > bunch of hardcoded buffer sizes in the patch, that the GNU conventions
> > > tells us we should avoid.
> >
> > I have replaced '11' with sizeof ("4294967295") + 1.
>
> Thanks. Though, sizeof ("4294967295") already includes the
> size for the \0, so you've replaced 11 with 12.
OK, noted and fixed everywhere.
> > It is surely possible, but the original motivation here is mostly embedded
> > systems, so I'd prefer to have gdbserver-only version checked in, and then
> > anybody interested in native linux can fill the bits for linux.
>
> You're adding linux-nat.c:linux_nat_core_of_thread already, so we're
> mostly there ... I assume it's mostly a matter of copy/paste. I'll
> probably do it myself, for the sake of keeping the linux-nat.c and
> linux-low.c osdata code in sync.
OK, so I'll skip this part for now.
> > > > + /* Update the core associated with a thread when we process stop
> > > > + event in that thread. */
> > > > + info = find_thread_ptid (ptid);
> > > > + if (info && info->private)
> > > > + info->private->core = stop_reply->core;
> > >
> > > Consider all-stop, multi-threaded applications:
> > >
> > > What about the core of the other threads?
> > > Shouldn't they all be invalidated?
> > > Won't then be stale?
> > >
> > > (see comment below)
> >
> > Yes, it will be stale. However, it's approximation only, and the only two
> > places where cores number is used is *stopped response (which uses core of
> > the stopped thread, updated above) and -list-thread-groups (which updates
> > this information anyway). It does not seem right to additionally update
> > this even if nothing will use it.
>
> Not update, _invalidate_: tag that the core is unknown and needs fetching
> from the target somehow (say, adding a info->private->core_p field?
> Or setting it to info->private->core = -2?). The invalidation could be
> done on resume instead of on stop (e.g., linux-nat.c:resume_callback). I
> believe it is bad design practice to design an API and assume
> what it's callers do with it.
I think it's perfectly valid to optimize the API for the use cases we expect.
Otherwise, it's easy to introduce complexity and pessimization that is not
necessary.
> We can certainly come up with other
> target_core_of_thread callers in the near future, and at that point,
> we'll hit this issue. But all that lead me to the real question I have in
> my mind: how will we update the core of the thread, when we implement what
> I just described? Will we make remote_core_of_thread update all
> threads, or come up with a special packet to get at the core of
> a thread? Probably the former is okay (albeit, slow, although
> done only at most once), but if not, I'd rather consider the
> packet upfront, than leave a hole in the design --- if we
> considered such packet, the case for qXfer:threads would get
> smaller (it becomes only justifiable due to supposedly fewer
> packet roundtrips to list all threads and their extra info).
For a start, I certainly find that a single packet that returns all information
about threads -- which a frontend might be requesting on each step -- is a good
idea regardless of "core awareness". As for invalidation, I think it's reasonable
to fetch information about all threads, just because the time to get that information
is probably less than communication latency for a single packet.
So, for avoidance of doubt, how about this scheme:
- core information of each thread is invalidated when that thread is resumed
(where resumption is reported by the 'target_resumed' observer)
- remote_core_of_thread checks if it's asked for a core of the most recently
stopped thread. If so, it reports it. Otherwise, it refreshes the thread list,
and then reports core it has fetched
- this all is for all-stop only. For non-stop, we get stop packet of each stopped
thread and therefore remote_core_of_thread never needs to fetch information
about all threads.
If this sounds OK, I'll implement such a scheme.
> > > > + /* Return the core that thread PTID is on, or -1 if such information
> > > > + is not available. For a stopped thread, this is supposed to return
> > > > + the core the thread was last running on. For running threads, it
> > > > + should return one of the cores that the thread was running between
> > > > + the call to this function and return -- and if it was running on
> > > > + several cores, any other may be returned. */
> > > > + int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
> > >
> > > I guess that on some targets, it will be impossible to know which core
> > > a running thread was running on. Should it be simply documented as
> > > undefined instead?
> >
> > I am not sure I understand. On such target, this method will return -1.
>
> So, covered by the "-1 if such information is not available"?
> I was assuming that meant "not available now or ever, period. No
> need to request again, I'll always return -1".
I see, I have clarified the wording.
> > > Other:
> > >
> > > - did you consider being able to list the available
> > > cores? A candidate for "info os cores", I guess.
> >
> > Unfortunately, I do not know a suitable interface to get that information.
> > Parsing /proc/cpuinfo is not really attractive.
>
> We have more code that parses /proc/foo already though,
> e.g., linux-nat.c:pid_is_stopped, and I don't suppose it would
> be much worse than the new parsing of /proc/pid/task/lwp/stat
> to get at the core for a thread, though.
Well, while I presumably could have split /proc/cpuinfo on empty lines,
and somehow print that in 'info os cores' I am unsure if we can promise
any fields to be present in the output, given that man:proc does not
mention any field except of "processor" and "bogomips". So, all that
"info os cores" can meaningfully print is the numeric ids of cores,
and this does not add very much to the information already available
to the frontend.
> > I attach a revision with yours and Eli's comments addressed, expect as
> > mentioned above, and a delta.
>
> Thanks. Patch comments inline below.
>
>
> On Monday 21 December 2009 14:48:18, Vladimir Prus wrote:
> > @@ -32269,15 +32424,34 @@ An example document is:
> > <column name="pid">1</column>
> > <column name="user">root</column>
> > <column name="command">/sbin/init</column>
> > + <column name="cores">1,2,3</column>
> > + <threads>
> > + <item>
> > + <column name="tid">12</column>
> > + <column name="core">3</column>
> > + </item>
> > + </threads>
> > </item>
> > </osdata>
>
> I think this part of the documentation is now stale?
Yes, fixed.
> > +/* Given PID, iterates over all threads in that process.
> > +
> > + Information about each thread, in a format suitable for qXfer:osdata:thread
> > + is printed to BUFFER, if it's not NULL. The buffer will not be either
> > + initialized, or finished, or have '\0' written to it. Caller is responsible
> > + for such things.
>
> About:
>
> "The buffer will not be either initialized, or finished, or have '\0' written to it.
> Caller is responsible for such things."
>
> Do you mean?:
>
> "The buffer is assumed to be already initialized, and the caller is responsible for
> finishing and appending '\0' to it."
>
> ?
Yes, this is better wording.
> The function inits the buffer, finishes the buffer and appends \0 to it, so
> I'm confused by that comment.
The function does not init or finish BUFFER. It does init/finish a second buffer, but
that buffer is only used internally. Am I missing something?
> > +
> > + buffer_grow (buffer, prev, f - prev - 1);
> > + sprintf (b, "%d", i);
> > + buffer_grow_str (buffer, b);
> > + prev = f + 1;
> > + }
>
>
> > +/* Return the core for a thread. */
> > +static int
>
> Could you please add an empty new line between function describing
> comment and function definition (here and elsewhere)? We're trying
> to follow that style everywhere in gdb.
Is that for function definitions only? Or also for function declarations
and variable declarations/definitions?
> > diff --git a/gdb/remote.c b/gdb/remote.c
> > index 9fa92fb..e393e47 100644
> > --- a/gdb/remote.c
> > +++ b/gdb/remote.c
> (...)
> > +/* The core number that was last seed by process_stop_reply. */
> > +static int last_core = -1;
>
> Is "seed" a typo here?
Yes.
>
> > +/* The thread that corresponds to last_core. */
> > +static ptid_t thread_of_last_core;
>
> Are the last_core and thread_of_last_core globals needed
> for when the target reports the "core" in the stop reply,
> but doesn't support qxfer:threads:read? Should we simply
> not care for that, and assume that a stub that wants to
> report core info uses the new way to fetch thread info?
> I'd prefer not to have these globals, by e.g., creating
> info->private on the spot when processing the stop reply.
The primary problem is that when we process a stop reply
for a new thread, the thread is not created and added to gdb
thread table yet, so it's not possible to set its info->private.
I don't see any way around this. We can, in theory, make
remote_core_of_thread request the list of all threads even when
called for a thread that has just stopped, but that sounds wasteful.
> >@@ -2320,6 +2409,68 @@ remote_threads_info (struct target_ops *ops)
> ...
> > + if (!info->private)
> > + info->private = (struct private_thread_info *)
> > + xmalloc (sizeof (struct private_thread_info));
> > +
> > + info->private->extra = item->extra;
> > + item->extra = 0;
> > + info->private->core = item->core;
>
> I think info->private->extra is leaking when threads are deleted,
> because gdb/threads.c simply xfree's ->private. I guess you're the
> lucky first to need a destructor for thread private data.
> nto-procfs.c was close, but avoids it by using a poor man's flexible
> array member (nto-tdep.h).
Done.
I attach a revised patch.
- Volodya
[-- Attachment #2: core3.diff --]
[-- Type: text/x-patch, Size: 60784 bytes --]
commit 82ec7c5a3017e306857a41e8c7ceac8dc5afdb3f
Author: Vladimir Prus <vladimir@codesourcery.com>
Date: Wed Dec 16 23:39:19 2009 +0300
Implement core awareness.
gdb/
* bcache.c (compare_ints): Remove
(print_percentage): Use compare_positive_ints.
* defs.h (compare_positive_ints): Declare.
* linux-nat.c (linux_nat_core_of_thread): New.
(linux_nat_add_target): Register the above.
* remote.c (struct private_thread_info): New.
(free_private_thread_info): New.
(last_core, thread_of_last_core): New.
(PACKET_qXfer_threads, use_osdata_threads): New.
(struct thread_item, threads_parsing_context
(start_thread, end_thread, thread_attributes)
(thread_children, threads_children, threads_elements): New.
(remote_threads_info): Try qXfer:threads before anything
else.
(remote_protocol_packets): Register qXfer:threads.
(remote_open_1): Init use_osdata_threads.
(struct stop_reply): New field 'core'.
(remote_parse_stop_reply): Parse core number.
(process_stop_reply): Record core number.
(remote_xfer_partial): Handle qXfer:threads.
(remote_core_of_thread): New.
(init_remote_ops): Register remote_core_of_thread.
(_initialize_remote): Register qXfer:read.
* target.c (target_core_of_thread): New
* target.h (enum target_object): New value TARGET_OBJECT_THREADS.
(struct target_ops): New field to_core_of_threads.
(target_core_of_thread): Declare.
* gdbthread.h (struct thread_info): New field private_dtor.
* thread.c (print_thread_info): Report the core.
* ui-out.c (MAX_UI_OUT_LEVELS): Increase.
* utils.c (compare_positive_ints): New.
* features/threads.dtd: New.
* mi/mi-interp.c (mi_on_normal_stop): Report the core.
* mi/mi-main.c (struct collect_cores_data, collect_cores)
(do_nothing, free_vector_of_osdata_items)
(splay_tree_int_comparator, free_splay_tree): New.
(print_one_inferior_data): Implemented printing of selected
inferiors. Collect and print cores.
(output_cores): New.
(mi_cmd_list_thread_groups): Support --recurse. Permit specifying
thread groups together with --available.
gdbserver/
* linux-low.c (linux_core_of_thread): New.
(compare_ints, show_process, list_threads): New.
(linux_qxfer_osdata): Report threads and cores.
(linux_target_op): Register linux_core_of_thread.
* remote-utils.c (prepare_resume_reply): Report the core.
(buffer_xml_printf): Support %d specifier.
* server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
New.
(handle_query): Handle qXfer:threads. Announce availability
thereof.
* target.h (struct target_ops): New field core_for_threads.
gdb/doc
* gdb.texinfo (GDB/MI Thread Information): New.
(GDB/MI Async Records): Document the core field in *stopped.
(GDB/MI Miscellaneous Commands): Expand -list-thread-groups
documentation
(Process list): Document that osdata document may contain
threads.
(Remote Serial Protocol): Document qXfer:threads.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4d3e02a..31a0f76 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -444,7 +444,8 @@ 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/osdata.dtd
+ $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+ $(srcdir)/features/threads.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,
diff --git a/gdb/bcache.c b/gdb/bcache.c
index 1bc2eba..4badf6e 100644
--- a/gdb/bcache.c
+++ b/gdb/bcache.c
@@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
\f
/* Printing statistics. */
-static int
-compare_ints (const void *ap, const void *bp)
-{
- /* Because we know we're comparing two ints which are positive,
- there's no danger of overflow here. */
- return * (int *) ap - * (int *) bp;
-}
-
-
static void
print_percentage (int portion, int total)
{
@@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
/* To compute the median, we need the set of chain lengths sorted. */
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
- compare_ints);
+ compare_positive_ints);
qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
- compare_ints);
+ compare_positive_ints);
if (c->num_buckets > 0)
{
diff --git a/gdb/defs.h b/gdb/defs.h
index 5d251b5..b0a212d 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -417,6 +417,8 @@ char *ldirname (const char *filename);
char **gdb_buildargv (const char *);
+int compare_positive_ints (const void *ap, const void *bp);
+
/* From demangle.c */
extern void set_demangling_style (char *);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d0997b3..ca5e92c 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -15482,6 +15482,10 @@ are:
@tab @code{qXfer:siginfo:write}
@tab @code{set $_siginfo}
+@item @code{threads}
+@tab @code{qXfer:threads:read}
+@tab @code{info threads}
+
@item @code{get-thread-local-@*storage-address}
@tab @code{qGetTLSAddr}
@tab Displaying @code{__thread} variables
@@ -21768,6 +21772,7 @@ follow development on @email{gdb@@sourceware.org} and
* GDB/MI Stream Records::
* GDB/MI Async Records::
* GDB/MI Frame Information::
+* GDB/MI Thread Information::
@end menu
@node GDB/MI Result Records
@@ -21860,7 +21865,7 @@ several times, either for different threads, because it cannot resume
all threads together, or even for a single thread, if the thread must
be stepped though some code before letting it run freely.
-@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
+@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
The target has stopped. The @var{reason} field can have one of the
following values:
@@ -21900,7 +21905,9 @@ If all threads are stopped, the @var{stopped} field will have the
value of @code{"all"}. Otherwise, the value of the @var{stopped}
field will be a list of thread identifiers. Presently, this list will
always include a single thread, but frontend should be prepared to see
-several threads in the list.
+several threads in the list. The @var{core} field reports the
+processor core on which the stop event has happened. This field may be absent
+if such information is not available.
@item =thread-group-created,id="@var{id}"
@itemx =thread-group-exited,id="@var{id}"
@@ -21977,6 +21984,34 @@ corresponds to the frame's code address. This field may be absent.
@end table
+@node GDB/MI Thread Information
+@subsection @sc{gdb/mi} Thread Information
+
+Whenever @value{GDBN} has to report an information about a thread, it
+uses a tuple with the following fields:
+
+@table @code
+@item id
+The numeric id assigned to the thread by @value{GDBN}. This field is
+always present.
+
+@item target-id
+Target-specific string identifying the thread. This field is always present.
+
+@item details
+Additional information about the thread provided by the target.
+It is supposed to be human-readable and not interpreted by the
+frontend. This field is optional.
+
+@item state
+Either @samp{stopped} or @samp{running}, depending on whether the
+thread is presently running. This field is always present.
+
+@item core
+The value of this field is an integer number of the processor core the
+thread was last seen on. This field is optional.
+@end table
+
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Simple Examples
@@ -26289,20 +26324,84 @@ while the target is running.
@subheading Synopsis
@smallexample
--list-thread-groups [ --available ] [ @var{group} ]
+-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
@end smallexample
-When used without the @var{group} parameter, lists top-level thread
-groups that are being debugged. When used with the @var{group}
-parameter, the children of the specified group are listed. The
-children can be either threads, or other groups. At present,
-@value{GDBN} will not report both threads and groups as children at
-the same time, but it may change in future.
+Lists thread groups (@pxref{Thread groups}). When a single thread
+group is passed as the argument, lists the children of that group.
+When several thread group are passed, lists information about those
+thread groups. Without any parameters, lists information about all
+top-level thread groups.
+
+Normally, thread groups that are being debugged are reported.
+With the @samp{--available} option, @value{GDBN} reports thread groups
+available on the target.
+
+The output of this command may have either a @samp{threads} result or
+a @samp{groups} result. The @samp{thread} result has a list of tuples
+as value, with each tuple describing a thread (@pxref{GDB/MI Thread
+Information}). The @samp{groups} result has a list of tuples as value,
+each tuple describing a thread group. If top-level groups are
+requested (that is, no parameter is passed), or when several groups
+are passed, the output always has a @samp{groups} result. The format
+of the @samp{group} result is described below.
+
+To reduce the number of roundtrips it's possible to list thread groups
+together with their children, by passing the @samp{--recurse} option
+and the recursion depth. Presently, only recursion depth of 1 is
+permitted. If this option is present, then every reported thread group
+will also include its children, either as @samp{group} or
+@samp{threads} field.
+
+In general, any combination of option and parameters is permitted, with
+the following caveats:
+
+@itemize @bullet
+@item
+When a single thread group is passed, the output will typically
+be the @samp{threads} result. Because threads may not contain
+anything, the @samp{recurse} option will be ignored.
+
+@item
+When the @samp{--available} option is passed, limited information may
+be available. In particular, the list of threads of a process might
+be inaccessible. Further, specifying specific thread groups might
+not give any performance advantage over listing all thread groups.
+The frontend should assume that @samp{-list-thread-groups --available}
+is always an expensive operation and cache the results.
+
+@end itemize
+
+The @samp{groups} result is a list of tuples, where each tuple may
+have the following fields:
+
+@table @code
+@item id
+Identifier of the thread group. This field is always present.
+
+@item type
+The type of the thread group. At present, only @samp{process} is a
+valid type.
+
+@item pid
+The target-specific process identifier. This field is only present
+for thread groups of type @samp{process}.
-With the @samp{--available} option, instead of reporting groups that
-are been debugged, GDB will report all thread groups available on the
-target. Using the @samp{--available} option together with @var{group}
-is not allowed.
+@item num_children
+The number of children this thread group has. This field may be
+absent for an available thread group.
+
+@item threads
+This field has a list of tuples as value, each tuple describing a
+thread. It may be present if the @samp{--recurse} option is
+specified, and it's actually possible to obtain the threads.
+
+@item cores
+This field is a list of integers, each identifying a core that one
+thread of the group is running on. This field may be absent if
+such information is not available.
+
+@end table
@subheading Example
@@ -26316,6 +26415,16 @@ is not allowed.
@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
+-list-thread-groups --available
+^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
+-list-thread-groups --available --recurse 1
+ ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
+-list-thread-groups --available --recurse 1 17 18
+^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
@end smallexample
@subheading The @code{-interpreter-exec} Command
@@ -28065,6 +28174,7 @@ Show the current setting of the target wait timeout.
* File-I/O Remote Protocol Extension::
* Library List Format::
* Memory Map Format::
+* Thread List Format::
@end menu
@node Overview
@@ -28965,6 +29075,10 @@ If @var{n} is @samp{thread}, then @var{r} is the @var{thread-id} of
the stopped thread, as specified in @ref{thread-id syntax}.
@item
+If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
+the core on which the stop event was detected.
+
+@item
If @var{n} is a recognized @dfn{stop reason}, it describes a more
specific event that stopped the target. The currently defined stop
reasons are listed below. @var{aa} should be @samp{05}, the trap
@@ -28998,8 +29112,6 @@ logged execution events, because it has reached the end (or the
beginning when executing backward) of the log. The value of @var{r}
will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
for more information.
-
-
@end table
@item W @var{AA}
@@ -29539,6 +29651,12 @@ These are the currently defined stub features and their properties:
@tab @samp{-}
@tab Yes
+@item @samp{qXfer:threads:read}
+@tab No
+@tab @samp{-}
+@tab Yes
+
+
@item @samp{QNonStop}
@tab No
@tab @samp{-}
@@ -29622,6 +29740,10 @@ The remote stub understands the @samp{qXfer:siginfo:read} packet
The remote stub understands the @samp{qXfer:siginfo:write} packet
(@pxref{qXfer siginfo write}).
+@item qXfer:threads:read
+The remote stub understands the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}).
+
@item QNonStop
The remote stub understands the @samp{QNonStop} packet
(@pxref{QNonStop}).
@@ -29814,6 +29936,15 @@ This packet is not probed by default; the remote stub must request it,
by supplying an appropriate @samp{qSupported} response
(@pxref{qSupported}).
+@item qXfer:threads:read::@var{offset},@var{length}
+@anchor{qXfer threads read}
+Access the list of threads on target. @xref{Thread List Format}. The
+annex part of the generic @samp{qXfer} packet must be empty
+(@pxref{qXfer read}).
+
+This packet is not probed by default; the remote stub must request it,
+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}.
@@ -31827,6 +31958,30 @@ The formal DTD for memory map format is given below:
<!ATTLIST property name CDATA #REQUIRED>
@end smallexample
+@node Thread List Format
+@section Thread List Format
+@cindex thread list format
+
+To efficiently update the list of threads and their attributes,
+@value{GDBN} issues the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}) and obtains the XML document with
+the following structure:
+
+@smallexample
+<?xml version="1.0"?>
+<threads>
+ <thread id="id" core="0">
+ ... description ...
+ </thread>
+</threads>
+@end smallexample
+
+Each @samp{thread} element must have the @samp{id} attribute that
+identifies the thread (@pxref{thread-id syntax}). The
+@samp{core} attribute, if present, specifies which processor core
+the thread was last executing on. The content of the of @samp{thread}
+element is interpreted as human-readable auxilliary information.
+
@include agentexpr.texi
@node Target Descriptions
@@ -32386,6 +32541,7 @@ An example document is:
<column name="pid">1</column>
<column name="user">root</column>
<column name="command">/sbin/init</column>
+ <column name="cores">1,2,3</column>
</item>
</osdata>
@end smallexample
@@ -32393,7 +32549,9 @@ An example document is:
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,
+displayed by @value{GDBN}. The @samp{cores} column, if present,
+should contain a comma-separated list of cores that this process
+is running on. Target may provide additional columns,
which @value{GDBN} currently ignores.
@include gpl.texi
diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
new file mode 100644
index 0000000..20308ee
--- /dev/null
+++ b/gdb/features/threads.dtd
@@ -0,0 +1,13 @@
+<!-- Copyright (C) 2008, 2009 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. -->
+
+<!ELEMENT threads (thread*)>
+<!ATTLIST threads version CDATA #FIXED "1.0">
+
+<!ELEMENT thread (#PCDATA)>
+
+<!ATTLIST thread id CDATA #REQUIRED>
+<!ATTLIST thread core CDATA #IMPLIED>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index c20322e..5584691 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -140,6 +140,7 @@ static int check_removed_breakpoint (struct lwp_info *event_child);
static void *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int linux_core_of_thread (ptid_t ptid);
struct pending_signals
{
@@ -2802,6 +2803,175 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
#endif
static int
+compare_ints (const void *xa, const void *xb)
+{
+ int a = *(const int *)xa;
+ int b = *(const int *)xb;
+
+ return a - b;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+/* Given PID, iterates over all threads in that process.
+
+ Information about each thread, in a format suitable for qXfer:osdata:thread
+ is printed to BUFFER, if it's not NULL. BUFFER is assumed to be already
+ initialized, and the caller is responsible for finishing and appending '\0'
+ to it.
+
+ The list of cores that threads are running on is assigned to *CORES, if it
+ is not NULL. If no cores are found, *CORES will be set to NULL. Caller
+ should free *CORES. */
+
+static void
+list_threads (int pid, struct buffer *buffer, char **cores)
+{
+ int count = 0;
+ int allocated = 10;
+ int *core_numbers = xmalloc (sizeof (int) * allocated);
+ char pathname[128];
+ DIR *dir;
+ struct dirent *dp;
+ struct stat statbuf;
+
+ sprintf (pathname, "/proc/%d/task", pid);
+ if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+ {
+ dir = opendir (pathname);
+ if (!dir)
+ {
+ free (core_numbers);
+ return;
+ }
+
+ while ((dp = readdir (dir)) != NULL)
+ {
+ unsigned long lwp = strtoul (dp->d_name, NULL, 10);
+
+ if (lwp != 0)
+ {
+ unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
+
+ if (core != -1)
+ {
+ char s[sizeof ("4294967295")];
+ sprintf (s, "%u", core);
+
+ if (count == allocated)
+ {
+ allocated *= 2;
+ core_numbers = realloc (core_numbers,
+ sizeof (int) * allocated);
+ }
+ core_numbers[count++] = core;
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "<column name=\"core\">%s</column>"
+ "</item>", pid, dp->d_name, s);
+ }
+ else
+ {
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "</item>", pid, dp->d_name);
+ }
+ }
+ }
+ }
+
+ if (cores)
+ {
+ *cores = NULL;
+ if (count > 0)
+ {
+ struct buffer buffer2;
+ int *b;
+ int *e;
+ qsort (core_numbers, count, sizeof (int), compare_ints);
+
+ /* Remove duplicates. */
+ b = core_numbers;
+ e = unique (b, core_numbers + count);
+
+ buffer_init (&buffer2);
+
+ for (b = core_numbers; b != e; ++b)
+ {
+ char number[sizeof ("4294967295")];
+ sprintf (number, "%u", *b);
+ buffer_xml_printf (&buffer2, "%s%s",
+ (b == core_numbers) ? "" : ",", number);
+ }
+ buffer_grow_str0 (&buffer2, "");
+
+ *cores = buffer_finish (&buffer2);
+ }
+ }
+ free (core_numbers);
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+ char pathname[128];
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+
+ sprintf (pathname, "/proc/%d/cmdline", pid);
+
+ if ((f = fopen (pathname, "r")) != NULL)
+ {
+ size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+ if (len > 0)
+ {
+ char *cores = 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\">%d</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>",
+ pid,
+ username,
+ cmd);
+
+ /* This only collects core numbers, and does not print threads. */
+ list_threads (pid, NULL, &cores);
+
+ if (cores)
+ {
+ buffer_xml_printf (buffer,
+ "<column name=\"cores\">%s</column>", cores);
+ free (cores);
+ }
+
+ buffer_xml_printf (buffer, "</item>");
+ }
+ fclose (f);
+ }
+}
+
+static int
linux_qxfer_osdata (const char *annex,
unsigned char *readbuf, unsigned const char *writebuf,
CORE_ADDR offset, int len)
@@ -2811,10 +2981,16 @@ linux_qxfer_osdata (const char *annex,
static const char *buf;
static long len_avail = -1;
static struct buffer buffer;
+ int processes = 0;
+ int threads = 0;
DIR *dirp;
- if (strcmp (annex, "processes") != 0)
+ if (strcmp (annex, "processes") == 0)
+ processes = 1;
+ else if (strcmp (annex, "threads") == 0)
+ threads = 1;
+ else
return 0;
if (!readbuf || writebuf)
@@ -2827,7 +3003,10 @@ linux_qxfer_osdata (const char *annex,
len_avail = 0;
buf = NULL;
buffer_init (&buffer);
- buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ if (processes)
+ buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ else if (threads)
+ buffer_grow_str (&buffer, "<osdata type=\"threads\">");
dirp = opendir ("/proc");
if (dirp)
@@ -2846,37 +3025,16 @@ linux_qxfer_osdata (const char *annex,
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);
+ int pid = (int) strtoul (dp->d_name, NULL, 10);
- if ((f = fopen (pathname, "r")) != NULL)
+ if (processes)
{
- 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);
+ struct passwd *entry = getpwuid (statbuf.st_uid);
+ show_process (pid, entry ? entry->pw_name : "?", &buffer);
+ }
+ else if (threads)
+ {
+ list_threads (pid, &buffer, NULL);
}
}
}
@@ -3152,6 +3310,55 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
return ret;
}
+static int
+linux_core_of_thread (ptid_t ptid)
+{
+ char filename[sizeof ("/proc//task//stat")
+ + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
+ + 1];
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ sprintf (filename, "/proc/%d/task/%ld/stat",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid));
+ f = fopen (filename, "r");
+ if (!f)
+ return -1;
+
+ for (;;)
+ {
+ int n;
+ content = realloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ free (content);
+ fclose (f);
+
+ return core;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -3191,10 +3398,11 @@ static struct target_ops linux_target_ops = {
linux_start_non_stop,
linux_supports_multi_process,
#ifdef USE_THREAD_DB
- thread_db_handle_monitor_command
+ thread_db_handle_monitor_command,
#else
- NULL
+ NULL,
#endif
+ linux_core_of_thread
};
static void
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 9b5bad8..d638a3f 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
gdbserver to know what inferior_ptid is. */
if (1 || !ptid_equal (general_thread, ptid))
{
+ int core = -1;
/* In non-stop, don't change the general thread behind
GDB's back. */
if (!non_stop)
@@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
buf = write_ptid (buf, ptid);
strcat (buf, ";");
buf += strlen (buf);
+
+ if (the_target->core_for_thread)
+ core = (*the_target->core_for_thread) (ptid);
+ if (core != -1)
+ {
+ sprintf (buf, "core:");
+ buf += strlen (buf);
+ sprintf (buf, "%x", core);
+ strcat (buf, ";");
+ buf += strlen (buf);
+ }
}
}
@@ -1604,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...)
prev = f + 1;
}
break;
+ case 'd':
+ {
+ int i = va_arg (ap, int);
+ char b[sizeof ("4294967295")];
+
+ buffer_grow (buffer, prev, f - prev - 1);
+ sprintf (b, "%d", i);
+ buffer_grow_str (buffer, b);
+ prev = f + 1;
+ }
}
percent = 0;
}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 9254121..dfc92a8 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
}
}
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+ struct inferior_list_entry *thread;
+
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for (thread = all_threads.head; thread; thread = thread->next)
+ {
+ ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+ char ptid_s[100];
+ int core = -1;
+ char core_s[21];
+
+ write_ptid (ptid_s, ptid);
+
+ if (the_target->core_for_thread)
+ core = (*the_target->core_for_thread) (ptid);
+
+ if (core != -1)
+ {
+ sprintf (core_s, "%d", core);
+ buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+ ptid_s, core_s);
+ }
+ else
+ {
+ buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+ ptid_s);
+ }
+ }
+
+ buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+ unsigned char *readbuf,
+ CORE_ADDR offset, int length)
+{
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (annex && strcmp (annex, "") != 0)
+ return 0;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+ /* When asked for data at offset 0, generate everything and store into
+ 'result'. Successive reads will be served off 'result'. */
+ if (result)
+ free (result);
+
+ buffer_init (&buffer);
+
+ handle_threads_qxfer_proper (&buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (length > result_length - offset)
+ length = result_length - offset;
+
+ memcpy (readbuf, result + offset, length);
+
+ return length;
+
+}
+
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}
+ if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+ {
+ unsigned char *data;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+ char *annex;
+
+ require_running (own_buf);
+
+ /* Reject any annex; grab the offset and length. */
+ if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+ || annex[0] != '\0')
+ {
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ /* Read one extra byte, as an indicator of whether there is
+ more. */
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ data = malloc (len + 1);
+ if (!data)
+ return;
+ n = handle_threads_qxfer (annex, data, ofs, len + 1);
+ if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
+ strcat (own_buf, ";qXfer:threads:read+");
+
return;
}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index ad21eb7..0ee0d41 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -283,6 +283,9 @@ struct target_ops
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
+
+ /* Returns the core given a thread, or -1 if not known. */
+ int (*core_for_thread) (ptid_t);
};
extern struct target_ops *the_target;
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 4ab2111..cd24eaf 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -187,6 +187,10 @@ struct thread_info
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
+
+ /* Function that is called to free PRIVATE. If this is NULL, then
+ xfree will be called on PRIVATE. */
+ void (*private_dtor) (struct private_thread_info *);
};
/* Create an empty thread list, or empty the existing one. */
@@ -346,4 +350,6 @@ extern struct cleanup *make_cleanup_restore_current_thread (void);
INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
extern struct thread_info* inferior_thread (void);
+extern void update_thread_list (void);
+
#endif /* GDBTHREAD_H */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 0c95b95..78b1303 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -5423,6 +5423,66 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
return inf->aspace;
}
+/* Return the core for a thread. */
+
+static int
+linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct cleanup *back_to;
+ char *filename;
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ filename = xstrprintf ("/proc/%d/task/%ld/stat",
+ GET_PID (ptid), GET_LWP (ptid));
+ back_to = make_cleanup (xfree, filename);
+
+ f = fopen (filename, "r");
+ if (!f)
+ {
+ do_cleanups (back_to);
+ return -1;
+ }
+
+ make_cleanup_fclose (f);
+
+ for (;;)
+ {
+ int n;
+ content = xrealloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ make_cleanup (xfree, content);
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ /* If the first field after program name has index 0, then core number is
+ the field with index 36. There's no constant for that anywhere. */
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ do_cleanups (back_to);
+
+ return core;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -5463,6 +5523,8 @@ linux_nat_add_target (struct target_ops *t)
t->to_supports_multi_process = linux_nat_supports_multi_process;
+ t->to_core_of_thread = linux_nat_core_of_thread;
+
/* We don't change the stratum; this target will sit at
process_stratum and thread_db will set at thread_stratum. This
is a little strange, since this is a multi-threaded-capable
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 96e458c..41388bb 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
if (print_frame)
{
+ int core;
if (uiout != mi_uiout)
{
/* The normal_stop function has printed frame information into
@@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
}
else
ui_out_field_string (mi_uiout, "stopped-threads", "all");
+
+ core = target_core_of_thread (inferior_ptid);
+ if (core != -1)
+ ui_out_field_int (mi_uiout, "core", core);
}
fputs_unfiltered ("*stopped", raw_stdout);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 16f6102..aee246c 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -50,6 +50,7 @@
#include "valprint.h"
#include "inferior.h"
#include "osdata.h"
+#include "splay-tree.h"
#include <ctype.h>
#include <sys/time.h>
@@ -360,11 +361,55 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
print_thread_info (uiout, thread, -1);
}
+struct collect_cores_data
+{
+ int pid;
+
+ VEC (int) *cores;
+};
+
+static int
+collect_cores (struct thread_info *ti, void *xdata)
+{
+ struct collect_cores_data *data = xdata;
+
+ if (ptid_get_pid (ti->ptid) == data->pid)
+ {
+ int core = target_core_of_thread (ti->ptid);
+ if (core != -1)
+ VEC_safe_push (int, data->cores, core);
+ }
+
+ return 0;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+struct print_one_inferior_data
+{
+ int recurse;
+ VEC (int) *inferiors;
+};
+
static int
-print_one_inferior (struct inferior *inferior, void *arg)
+print_one_inferior (struct inferior *inferior, void *xdata)
{
- if (inferior->pid != 0)
+ struct print_one_inferior_data *top_data = xdata;
+
+ if (VEC_empty (int, top_data->inferiors)
+ || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
+ VEC_length (int, top_data->inferiors), sizeof (int),
+ compare_positive_ints))
{
+ struct collect_cores_data data;
struct cleanup *back_to
= make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -372,81 +417,299 @@ print_one_inferior (struct inferior *inferior, void *arg)
ui_out_field_string (uiout, "type", "process");
ui_out_field_int (uiout, "pid", inferior->pid);
+ data.pid = inferior->pid;
+ data.cores = 0;
+ iterate_over_threads (collect_cores, &data);
+
+ if (!VEC_empty (int, data.cores))
+ {
+ int elt;
+ int i;
+ int *b, *e;
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_list_begin_end (uiout, "cores");
+
+ qsort (VEC_address (int, data.cores),
+ VEC_length (int, data.cores), sizeof (int),
+ compare_positive_ints);
+
+ b = VEC_address (int, data.cores);
+ e = b + VEC_length (int, data.cores);
+ e = unique (b, e);
+
+ for (; b != e; ++b)
+ ui_out_field_int (uiout, NULL, *b);
+
+ do_cleanups (back_to_2);
+ }
+
+ if (top_data->recurse)
+ print_thread_info (uiout, -1, inferior->pid);
+
do_cleanups (back_to);
}
return 0;
}
-void
-mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+/* Output a field named 'cores' with a list as the value. The elements of
+ the list are obtained by splitting 'cores' on comma. */
+
+static void
+output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
{
- struct cleanup *back_to;
- int available = 0;
- char *id = NULL;
+ struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+ field_name);
+ char *cores = xstrdup (xcores);
+ char *p = cores;
- if (argc > 0 && strcmp (argv[0], "--available") == 0)
- {
- ++argv;
- --argc;
- available = 1;
- }
+ make_cleanup (xfree, cores);
- if (argc > 0)
- id = argv[0];
+ for (p = strtok (p, ","); p; p = strtok (NULL, ","))
+ ui_out_field_string (uiout, NULL, p);
- back_to = make_cleanup (null_cleanup, NULL);
+ do_cleanups (back_to);
+}
- if (available && id)
- {
- error (_("Can only report top-level available thread groups"));
- }
- else if (available)
- {
- struct osdata *data;
- struct osdata_item *item;
- int ix_items;
+static void
+free_vector_of_ints (void *xvector)
+{
+ VEC (int) **vector = xvector;
+ VEC_free (int, *vector);
+}
- data = get_osdata ("processes");
- make_cleanup_osdata_free (data);
+static void
+do_nothing (splay_tree_key k)
+{
+}
- make_cleanup_ui_out_list_begin_end (uiout, "groups");
+static void
+free_vector_of_osdata_items (splay_tree_value xvalue)
+{
+ VEC (osdata_item_s) *value = (VEC (osdata_item_s) *) xvalue;
+ /* We don't free the items itself, it will be done separately. */
+ VEC_free (osdata_item_s, value);
+}
+
+static int
+splay_tree_int_comparator (splay_tree_key xa, splay_tree_key xb)
+{
+ int a = xa;
+ int b = xb;
+ return a - b;
+}
+
+static void
+free_splay_tree (void *xt)
+{
+ splay_tree t = xt;
+ splay_tree_delete (t);
+}
+
+static void
+list_available_thread_groups (VEC (int) *ids, int recurse)
+{
+ struct osdata *data;
+ struct osdata_item *item;
+ int ix_items;
+ /* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
+ The vector contains information about all threads for the given
+ pid. */
+ splay_tree tree;
+
+ /* get_osdata will throw if it cannot return data. */
+ data = get_osdata ("processes");
+ make_cleanup_osdata_free (data);
+
+ if (recurse)
+ {
+ struct osdata *threads = get_osdata ("threads");
+ make_cleanup_osdata_free (threads);
+
+ tree = splay_tree_new (splay_tree_int_comparator,
+ do_nothing,
+ free_vector_of_osdata_items);
+ make_cleanup (free_splay_tree, tree);
for (ix_items = 0;
- VEC_iterate (osdata_item_s, data->items,
+ VEC_iterate (osdata_item_s, threads->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");
+ int pid_i = strtoul (pid, NULL, 0);
+ VEC (osdata_item_s) *vec = 0;
+
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (!n)
+ {
+ VEC_safe_push (osdata_item_s, vec, item);
+ splay_tree_insert (tree, pid_i, (splay_tree_value)vec);
+ }
+ else
+ {
+ vec = (VEC (osdata_item_s) *) n->value;
+ VEC_safe_push (osdata_item_s, vec, item);
+ n->value = (splay_tree_value) vec;
+ }
+ }
+ }
+
+ 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;
+
+ const char *pid = get_osdata_column (item, "pid");
+ const char *cmd = get_osdata_column (item, "command");
+ const char *user = get_osdata_column (item, "user");
+ const char *cores = get_osdata_column (item, "cores");
+
+ int pid_i = strtoul (pid, NULL, 0);
+
+ /* At present, the target will return all available processes
+ and if information about specific ones was required, we filter
+ undesired processes here. */
+ if (ids && bsearch (&pid_i, VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints) == NULL)
+ continue;
+
- 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);
+ back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
- do_cleanups (back_to);
+ 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);
+ if (cores)
+ output_cores (uiout, "cores", cores);
+
+ if (recurse)
+ {
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (n)
+ {
+ VEC (osdata_item_s) *children = (VEC (osdata_item_s) *) n->value;
+ struct osdata_item *child;
+ int ix_child;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+ for (ix_child = 0;
+ VEC_iterate (osdata_item_s, children, ix_child, child);
+ ++ix_child)
+ {
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *tid = get_osdata_column (child, "tid");
+ const char *tcore = get_osdata_column (child, "core");
+ ui_out_field_string (uiout, "id", tid);
+ if (tcore)
+ ui_out_field_string (uiout, "core", tcore);
+
+ do_cleanups (back_to_2);
+ }
+ }
}
+
+ do_cleanups (back_to);
}
- else if (id)
+}
+
+void
+mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+{
+ struct cleanup *back_to;
+ int available = 0;
+ int recurse = 0;
+ VEC (int) *ids = 0;
+
+ enum opt
{
- int pid = atoi (id);
+ AVAILABLE_OPT, RECURSE_OPT
+ };
+ static struct mi_opt opts[] =
+ {
+ {"-available", AVAILABLE_OPT, 0},
+ {"-recurse", RECURSE_OPT, 1},
+ { 0, 0, 0 }
+ };
+
+ int optind = 0;
+ char *optarg;
+
+ while (1)
+ {
+ int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+ &optind, &optarg);
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case AVAILABLE_OPT:
+ available = 1;
+ break;
+ case RECURSE_OPT:
+ if (strcmp (optarg, "0") == 0)
+ ;
+ else if (strcmp (optarg, "1") == 0)
+ recurse = 1;
+ else
+ error ("only '0' and '1' are valid values for the '--recurse' option");
+ break;
+ }
+ }
+
+ for (; optind < argc; ++optind)
+ {
+ char *end;
+ int inf = strtoul (argv[optind], &end, 0);
+ if (*end != '\0')
+ error ("invalid group id '%s'", argv[optind]);
+ VEC_safe_push (int, ids, inf);
+ }
+ if (VEC_length (int, ids) > 1)
+ qsort (VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints);
+
+ back_to = make_cleanup (free_vector_of_ints, &ids);
+
+ if (available)
+ {
+ list_available_thread_groups (ids, recurse);
+ }
+ else if (VEC_length (int, ids) == 1)
+ {
+ /* Local thread groups, single id. */
+ int pid = *VEC_address (int, ids);
if (!in_inferior_list (pid))
- error ("Invalid thread group id '%s'", id);
- print_thread_info (uiout, -1, pid);
+ error ("Invalid thread group id '%d'", pid);
+ print_thread_info (uiout, -1, pid);
}
else
{
+ struct print_one_inferior_data data;
+ data.recurse = recurse;
+ data.inferiors = ids;
+
+ /* Local thread groups. Either no explicit ids -- and we
+ print everything, or several explicit ids. In both cases,
+ we print more than one group, and have to use 'groups'
+ as the top-level element. */
make_cleanup_ui_out_list_begin_end (uiout, "groups");
- iterate_over_inferiors (print_one_inferior, NULL);
+ update_thread_list ();
+ iterate_over_inferiors (print_one_inferior, &data);
}
-
+
do_cleanups (back_to);
}
diff --git a/gdb/remote.c b/gdb/remote.c
index 7af67e5..9d343b2 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -60,6 +60,7 @@
#include "remote-fileio.h"
#include "gdb/fileio.h"
#include "gdb_stat.h"
+#include "xml-support.h"
#include "memory-map.h"
@@ -303,6 +304,26 @@ struct remote_state
int ctrlc_pending_p;
};
+/* Private data that we'll store in (struct thread_info)->private. */
+struct private_thread_info
+{
+ char *extra;
+ int core;
+};
+
+static void
+free_private_thread_info (struct private_thread_info *info)
+{
+ xfree (info->extra);
+ xfree (info);
+}
+
+/* The core number that was last seen by process_stop_reply. */
+static int last_core = -1;
+
+/* The thread that corresponds to last_core. */
+static ptid_t thread_of_last_core;
+
/* Returns true if the multi-process extensions are in effect. */
static int
remote_multi_process_p (struct remote_state *rs)
@@ -1054,6 +1075,7 @@ enum {
PACKET_qXfer_spu_read,
PACKET_qXfer_spu_write,
PACKET_qXfer_osdata,
+ PACKET_qXfer_threads,
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
@@ -2303,6 +2325,80 @@ remote_find_new_threads (void)
CRAZY_MAX_THREADS);
}
+#if defined(HAVE_LIBEXPAT)
+
+typedef struct thread_item
+{
+ ptid_t ptid;
+ char *extra;
+ int core;
+} thread_item_t;
+DEF_VEC_O(thread_item_t);
+
+struct threads_parsing_context
+{
+ VEC (thread_item_t) *items;
+};
+
+static void
+start_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct threads_parsing_context *data = user_data;
+
+ struct thread_item item;
+ char *id;
+
+ id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ item.ptid = read_ptid (id, NULL);
+
+ if (VEC_length (gdb_xml_value_s, attributes) > 1)
+ item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
+ else
+ item.core = -1;
+
+ item.extra = 0;
+
+ VEC_safe_push (thread_item_t, data->items, &item);
+}
+
+static void
+end_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, const char *body_text)
+{
+ struct threads_parsing_context *data = user_data;
+
+ if (body_text && *body_text)
+ VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
+}
+
+const struct gdb_xml_attribute thread_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element thread_children[] = {
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_children[] = {
+ { "thread", thread_attributes, thread_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ start_thread, end_thread },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_elements[] = {
+ { "threads", NULL, threads_children,
+ GDB_XML_EF_NONE, NULL, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+#endif
+
/*
* Find all threads for info threads command.
* Uses new thread protocol contributed by Cisco.
@@ -2320,6 +2416,70 @@ remote_threads_info (struct target_ops *ops)
if (remote_desc == 0) /* paranoia */
error (_("Command can only be used when connected to the remote target."));
+#if defined(HAVE_LIBEXPAT)
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ char *xml = target_read_stralloc (¤t_target,
+ TARGET_OBJECT_THREADS, NULL);
+
+ struct cleanup *back_to = make_cleanup (xfree, xml);
+ if (xml && *xml)
+ {
+ struct gdb_xml_parser *parser;
+ struct threads_parsing_context context;
+ struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+ context.items = 0;
+ parser = gdb_xml_create_parser_and_cleanup (_("threads"),
+ threads_elements,
+ &context);
+
+ gdb_xml_use_dtd (parser, "threads.dtd");
+
+ if (gdb_xml_parse (parser, xml) == 0)
+ {
+ int i;
+ struct thread_item *item;
+
+ for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
+ {
+ if (!ptid_equal (item->ptid, null_ptid))
+ {
+ struct thread_info *info;
+ /* In non-stop mode, we assume new found threads
+ are running until proven otherwise with a
+ stop reply. In all-stop, we can only get
+ here if all threads are stopped. */
+ int running = non_stop ? 1 : 0;
+
+ remote_notice_new_inferior (item->ptid, running);
+
+ info = find_thread_ptid (item->ptid);
+ if (info)
+ {
+ if (!info->private) {
+ info->private = (struct private_thread_info *)
+ xmalloc (sizeof (struct private_thread_info));
+ info->private_dtor = free_private_thread_info;
+ }
+
+ info->private->extra = item->extra;
+ item->extra = 0;
+ info->private->core = item->core;
+ }
+ }
+ xfree (item->extra);
+ }
+ }
+
+ VEC_free (thread_item_t, context.items);
+ }
+
+ do_cleanups (back_to);
+ return;
+ }
+#endif
+
if (use_threadinfo_query)
{
putpkt ("qfThreadInfo");
@@ -2392,6 +2552,15 @@ remote_threads_extra_info (struct thread_info *tp)
server doesn't know about it. */
return NULL;
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ struct thread_info *info = find_thread_ptid (tp->ptid);
+ if (info && info->private)
+ return info->private->extra;
+ else
+ return NULL;
+ }
+
if (use_threadextra_query)
{
char *b = rs->buf;
@@ -3152,6 +3321,8 @@ static struct protocol_feature remote_protocol_features[] = {
PACKET_qXfer_spu_write },
{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
PACKET_qXfer_osdata },
+ { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
+ PACKET_qXfer_threads },
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
PACKET_QPassSignals },
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -4262,6 +4433,8 @@ struct stop_reply
int solibs_changed;
int replay_event;
+
+ int core;
};
/* The list of already fetched and acknowledged stop events. */
@@ -4425,6 +4598,7 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
event->replay_event = 0;
event->stopped_by_watchpoint_p = 0;
event->regcache = NULL;
+ event->core = -1;
switch (buf[0])
{
@@ -4451,7 +4625,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
/* If this packet is an awatch packet, don't parse the 'a'
as a register number. */
- if (strncmp (p, "awatch", strlen("awatch")) != 0)
+ if (strncmp (p, "awatch", strlen("awatch")) != 0
+ && strncmp (p, "core", strlen ("core") != 0))
{
/* Read the ``P'' register number. */
pnum = strtol (p, &p_temp, 16);
@@ -4497,6 +4672,12 @@ Packet: '%s'\n"),
if (p_temp)
p = p_temp;
}
+ else if (strncmp (p, "core", p1 - p) == 0)
+ {
+ ULONGEST c;
+ p = unpack_varlen_hex (++p1, &c);
+ event->core = c;
+ }
else
{
/* Silently skip unknown optional info. */
@@ -4706,6 +4887,7 @@ process_stop_reply (struct stop_reply *stop_reply,
struct target_waitstatus *status)
{
ptid_t ptid;
+ struct thread_info *info;
*status = stop_reply->ws;
ptid = stop_reply->ptid;
@@ -4736,6 +4918,19 @@ process_stop_reply (struct stop_reply *stop_reply,
remote_stopped_by_watchpoint_p = stop_reply->stopped_by_watchpoint_p;
remote_watch_data_address = stop_reply->watch_data_address;
+ /* Update the core associated with a thread when we process stop
+ event in that thread. */
+ info = find_thread_ptid (ptid);
+ if (info && info->private)
+ {
+ info->private->core = stop_reply->core;
+ }
+ else
+ {
+ last_core = stop_reply->core;
+ thread_of_last_core = ptid;
+ }
+
remote_notice_new_inferior (ptid, 0);
}
@@ -7579,6 +7774,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
(ops, "osdata", annex, readbuf, offset, len,
&remote_protocol_packets[PACKET_qXfer_osdata]);
+ case TARGET_OBJECT_THREADS:
+ gdb_assert (annex == NULL);
+ return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
+ &remote_protocol_packets[PACKET_qXfer_threads]);
+
default:
return -1;
}
@@ -8895,6 +9095,17 @@ remote_supports_cond_tracepoints (void)
return rs->cond_tracepoints;
}
+static int
+remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+ if (info && info->private)
+ return info->private->core;
+ else if (ptid_equal (ptid, thread_of_last_core))
+ return last_core;
+ return -1;
+}
+
static void
init_remote_ops (void)
{
@@ -8958,6 +9169,7 @@ Specify the serial device it is connected to\n\
remote_ops.to_terminal_ours = remote_terminal_ours;
remote_ops.to_supports_non_stop = remote_supports_non_stop;
remote_ops.to_supports_multi_process = remote_supports_multi_process;
+ remote_ops.to_core_of_thread = remote_core_of_thread;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -9317,6 +9529,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
"qXfer:osdata:read", "osdata", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
+ "qXfer:threads:read", "threads", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
"qXfer:siginfo:read", "read-siginfo-object", 0);
diff --git a/gdb/target.c b/gdb/target.c
index 38cd508..899f61a 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3024,6 +3024,26 @@ target_store_registers (struct regcache *regcache, int regno)
noprocess ();
}
+int
+target_core_of_thread (ptid_t ptid)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_core_of_thread != NULL)
+ {
+ int retval = t->to_core_of_thread (t, ptid);
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
+ PIDGET (ptid), retval);
+ return retval;
+ }
+ }
+
+ return -1;
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
diff --git a/gdb/target.h b/gdb/target.h
index 9a89b93..a4fe06a 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -255,6 +255,8 @@ enum target_object
/* Extra signal info. Usually the contents of `siginfo_t' on unix
platforms. */
TARGET_OBJECT_SIGNAL_INFO,
+ /* The list of threads that are being debugged. */
+ TARGET_OBJECT_THREADS,
/* Possible future objects: TARGET_OBJECT_FILE, ... */
};
@@ -597,6 +599,15 @@ struct target_ops
struct address_space *(*to_thread_address_space) (struct target_ops *,
ptid_t);
+ /* Return the core that thread PTID is on. For a stopped thread, should
+ return the core the thread was last running on. For a running thread,
+ should return one of the cores that the thread was running between
+ the call to this function and return -- and if it was running on
+ several cores, any other may be returned.
+ If the core cannot be determined -- either for the specified thread, or
+ right now, or in this debug session, or for this target -- return -1. */
+ int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
+
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@@ -1246,6 +1257,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
(*current_target.to_log_command) (p); \
while (0)
+
+extern int target_core_of_thread (ptid_t ptid);
+
/* Routines for maintenance of the target structures...
add_target: Add a target to the list of all possible targets.
diff --git a/gdb/thread.c b/gdb/thread.c
index 80c3786..9613393 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -115,9 +115,14 @@ free_thread (struct thread_info *tp)
clear_thread_inferior_resources (tp);
/* FIXME: do I ever need to call the back-end to give it a
- chance at this private data before deleting the thread? */
+ chance at this private data before deleting the thread? */
if (tp->private)
- xfree (tp->private);
+ {
+ if (tp->private_dtor)
+ tp->private_dtor (tp->private);
+ else
+ xfree (tp->private);
+ }
xfree (tp);
}
@@ -468,8 +473,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
struct cleanup *cleanup_chain;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
@@ -748,8 +752,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
char *extra_info;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
current_ptid = inferior_ptid;
/* We'll be switching threads temporarily. */
@@ -759,6 +762,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
for (tp = thread_list; tp; tp = tp->next)
{
struct cleanup *chain2;
+ int core;
if (requested_thread != -1 && tp->num != requested_thread)
continue;
@@ -817,6 +821,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
ui_out_field_string (uiout, "state", state);
}
+ core = target_core_of_thread (tp->ptid);
+ if (ui_out_is_mi_like_p (uiout) && core != -1)
+ ui_out_field_int (uiout, "core", core);
+
do_cleanups (chain2);
}
@@ -1058,8 +1066,7 @@ thread_apply_all_command (char *cmd, int from_tty)
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
old_chain = make_cleanup_restore_current_thread ();
@@ -1245,6 +1252,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
return GDB_RC_OK;
}
+void
+update_thread_list (void)
+{
+ prune_threads ();
+ target_find_new_threads ();
+}
+
/* Commands with a prefix of `thread'. */
struct cmd_list_element *thread_cmd_list = NULL;
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index db8c894..e5fd474 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -44,7 +44,7 @@ struct ui_out_hdr
is always available. Stack/nested level 0 is reserved for the
top-level result. */
-enum { MAX_UI_OUT_LEVELS = 6 };
+enum { MAX_UI_OUT_LEVELS = 8 };
struct ui_out_level
{
diff --git a/gdb/utils.c b/gdb/utils.c
index 4c03655..f72a9e3 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
return argv;
}
+int
+compare_positive_ints (const void *ap, const void *bp)
+{
+ /* Because we know we're comparing two ints which are positive,
+ there's no danger of overflow here. */
+ return * (int *) ap - * (int *) bp;
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_utils;
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2009-12-21 14:48 ` Vladimir Prus
@ 2009-12-31 11:51 ` Pedro Alves
2010-01-04 15:11 ` Vladimir Prus
0 siblings, 1 reply; 17+ messages in thread
From: Pedro Alves @ 2009-12-31 11:51 UTC (permalink / raw)
To: Vladimir Prus; +Cc: gdb-patches
On Monday 21 December 2009 14:48:18, Vladimir Prus wrote:
> On Thursday 17 December 2009 18:33:21 Pedro Alves wrote:
>
> > > +@item qXfer:threads:read::@var{offset},@var{length}
> > > +@anchor{qXfer threads read}
> > > +Access the list of threads on target. @xref{Thread List Format}. The
> > > +annex part of the generic @samp{qXfer} packet must be empty
> > > +(@pxref{qXfer read}).
> > > +
> > > +This packet is not probed by default; the remote stub must request it,
> >
> > What's the advantage of this, rather than probing it? I would
> > see it nice for GDB to know in advance that the target can
> > report core data, but that's not that reporting support
> > for this packet means, since the "core" field is optional.
>
> Every other qXfer packet works this way. And it does not seem like much burden
> on the stub to announce that this packet is available.
True. I'm seeing a proliferation of qSupported additions (with
the tracepoint support going in), and I get the feeling that
qSupported will end up being the largest packet a stub has
to receive/send. Maybe it's an unfounded concern.
> > > diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd
> > > index a04506b..67de837 100644
> > > --- a/gdb/features/osdata.dtd
> > > +++ b/gdb/features/osdata.dtd
> > > @@ -10,7 +10,10 @@
> > > <!ATTLIST osdata version CDATA #FIXED "1.0">
> > > <!ATTLIST osdata type CDATA #REQUIRED>
> > >
> > > -<!ELEMENT item (column*)>
> > > +<!ELEMENT item (column*, threads?)>
> > >
> > > <!ELEMENT column (#PCDATA)>
> > > <!ATTLIST column name CDATA #REQUIRED>
> > > +
> > > +<!ELEMENT threads (item*)>
> > > +
> >
> > This dtd change isn't right. "osdata" isn't specific to
> > processes, it is just a generic table. A "threads" element
> > doesn't fit in, it wouldn't make sense for anything other
> > than listing processes. Two alternative solutions:
> >
> > - Allow multiple sub-table elements in the osdata schema, and
> > allow a "name" attribute on this element. In the listing
> > processes case, you'd have a sub-table whose type/name
> > attribute would be "threads". You should change
> > info_osdata_command to descend into subtables somehow.
> >
> > - Don't nest "threads" within processes osdata. Treat it as
> > a different request / table (e.g., <osdata type="threads">).
> > That is, on the CLI, "info os processes" works as is, unmodified, and,
> > you'd add support for "info os threads". No schema changes
> > are necessary with this option. At the MI implementation level, you'd
> > do two requests to get the same data: get_osdata("processes"), and
> > get_osdata("threads").
>
> I have implemented the second approach.
Thanks.
>
>
> > > + if (core != -1)
> > > + {
> > > + char s[11];
> > > + sprintf (s, "%u", core);
> > > +
> > > + if (count == allocated)
> > > + core_numbers = realloc (core_numbers,
> > > + sizeof (int ) * (allocated *= 2));
> >
> > No space after int. Please move the 'allocated *= 2' in its own
> > statement. My GM hat tells me that I should point out that there are a
> > bunch of hardcoded buffer sizes in the patch, that the GNU conventions
> > tells us we should avoid.
>
> I have replaced '11' with sizeof ("4294967295") + 1.
Thanks. Though, sizeof ("4294967295") already includes the
size for the \0, so you've replaced 11 with 12.
> It is surely possible, but the original motivation here is mostly embedded
> systems, so I'd prefer to have gdbserver-only version checked in, and then
> anybody interested in native linux can fill the bits for linux.
You're adding linux-nat.c:linux_nat_core_of_thread already, so we're
mostly there ... I assume it's mostly a matter of copy/paste. I'll
probably do it myself, for the sake of keeping the linux-nat.c and
linux-low.c osdata code in sync.
>
> > > + char filename[sizeof ("/proc//task//stat")
> >
> > s,//,/
>
> I don't think so, actually. The final path will look like
>
> /proc/NNN/task/MMM/stat
>
> so it will indeed have two slashes between 'proc' and 'task'
Oh, it looked like a typo, sorry. I see now.
>
> > > + case RECURSE_OPT:
> > > + if (strcmp (optarg, "1") != 0)
> > > + error ("only '1' is permitted value for the '--recurse' option");
> >
> > Seems strange not to allow '--recurse 0'.
>
> I've made it allowed.
>
> > What future values are you thinking of?
>
> '2', 'inf', for example.
>
> > Wouldn't it make more sense to call this
> > "--threads"? We may add other level-1 sublists to thread-groups
> > in the future, and _not_ want --recurse 1 to always show
> > them, no?
>
> I would say we rather want them. If frontend wishes to grab entire
> thread group hierarchy, he should be able to pass --recurse 1 or
> in future '--recurse inf' to get absolutely everything.
Fine.
(((For the record, in case I wasn't clear, here's what I
was thinking about: listing the threads is an
expensive operation. At some point we may add
one or more sub-trees directly descendent of processes other
than threads. If the frontend is interested in one or more of
these cheap-requesting subtrees, but not in expensive-threads,
it has no way, because then `--recurse 1' will always fetch
threads.)))
>
> > > + VEC_safe_push (int, ids, inf);
> > > }
> > > - else if (available)
> > > + qsort (VEC_address (int, ids),
> > > + VEC_length (int, ids),
> > > + sizeof (int), compare_positive_ints);
> > > +
> > > + back_to = make_cleanup ((void (*)(void *))VEC_OP (int, free), &ids);
> >
> > Please don't cast the function's type: instead write a
> > small function that has the proper cleanup prototype, and
> > call free there.
>
> Is casting function type going to cause practical problems on some targets?
It's certainly bad practice. There used to be a make_cleanup_func
typedef <http://sourceware.org/ml/gdb-patches/2000-q1/msg00791.html>,
<PR7196> that was eliminated years ago. Your cast would go a step
even further back than that dubious old typedef.
>
> > > + /* Update the core associated with a thread when we process stop
> > > + event in that thread. */
> > > + info = find_thread_ptid (ptid);
> > > + if (info && info->private)
> > > + info->private->core = stop_reply->core;
> >
> > Consider all-stop, multi-threaded applications:
> >
> > What about the core of the other threads?
> > Shouldn't they all be invalidated?
> > Won't then be stale?
> >
> > (see comment below)
>
> Yes, it will be stale. However, it's approximation only, and the only two
> places where cores number is used is *stopped response (which uses core of
> the stopped thread, updated above) and -list-thread-groups (which updates
> this information anyway). It does not seem right to additionally update
> this even if nothing will use it.
Not update, _invalidate_: tag that the core is unknown and needs fetching
from the target somehow (say, adding a info->private->core_p field?
Or setting it to info->private->core = -2?). The invalidation could be
done on resume instead of on stop (e.g., linux-nat.c:resume_callback). I
believe it is bad design practice to design an API and assume
what it's callers do with it. We can certainly come up with other
target_core_of_thread callers in the near future, and at that point,
we'll hit this issue. But all that lead me to the real question I have in
my mind: how will we update the core of the thread, when we implement what
I just described? Will we make remote_core_of_thread update all
threads, or come up with a special packet to get at the core of
a thread? Probably the former is okay (albeit, slow, although
done only at most once), but if not, I'd rather consider the
packet upfront, than leave a hole in the design --- if we
considered such packet, the case for qXfer:threads would get
smaller (it becomes only justifiable due to supposedly fewer
packet roundtrips to list all threads and their extra info).
> > > + /* Return the core that thread PTID is on, or -1 if such information
> > > + is not available. For a stopped thread, this is supposed to return
> > > + the core the thread was last running on. For running threads, it
> > > + should return one of the cores that the thread was running between
> > > + the call to this function and return -- and if it was running on
> > > + several cores, any other may be returned. */
> > > + int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
> >
> > I guess that on some targets, it will be impossible to know which core
> > a running thread was running on. Should it be simply documented as
> > undefined instead?
>
> I am not sure I understand. On such target, this method will return -1.
So, covered by the "-1 if such information is not available"?
I was assuming that meant "not available now or ever, period. No
need to request again, I'll always return -1".
> > Other:
> >
> > - did you consider being able to list the available
> > cores? A candidate for "info os cores", I guess.
>
> Unfortunately, I do not know a suitable interface to get that information.
> Parsing /proc/cpuinfo is not really attractive.
We have more code that parses /proc/foo already though,
e.g., linux-nat.c:pid_is_stopped, and I don't suppose it would
be much worse than the new parsing of /proc/pid/task/lwp/stat
to get at the core for a thread, though.
> I attach a revision with yours and Eli's comments addressed, expect as
> mentioned above, and a delta.
Thanks. Patch comments inline below.
On Monday 21 December 2009 14:48:18, Vladimir Prus wrote:
> @@ -32269,15 +32424,34 @@ An example document is:
> <column name="pid">1</column>
> <column name="user">root</column>
> <column name="command">/sbin/init</column>
> + <column name="cores">1,2,3</column>
> + <threads>
> + <item>
> + <column name="tid">12</column>
> + <column name="core">3</column>
> + </item>
> + </threads>
> </item>
> </osdata>
I think this part of the documentation is now stale?
> +{
> + int *d = b;
> + while (++b != e)
> + if (*d != *b)
> + *++d = *b;
> + return ++d;
Bad space/tab/indentation.
> +/* Given PID, iterates over all threads in that process.
> +
> + Information about each thread, in a format suitable for qXfer:osdata:thread
> + is printed to BUFFER, if it's not NULL. The buffer will not be either
> + initialized, or finished, or have '\0' written to it. Caller is responsible
> + for such things.
About:
"The buffer will not be either initialized, or finished, or have '\0' written to it.
Caller is responsible for such things."
Do you mean?:
"The buffer is assumed to be already initialized, and the caller is responsible for
finishing and appending '\0' to it."
?
The function inits the buffer, finishes the buffer and appends \0 to it, so
I'm confused by that comment.
> +
> + The list of cores that threads are running on is assigned to *CORES, if it
> + is not NULL. If not cores are found, *CORES will be set to NULL.
Typo: "If no cores". A sentence saying who is responsible for releasing *CORES
would be nice.
> +
> +static void
> +list_threads (int pid, struct buffer *buffer, char **cores)
> +{
> + int count = 0;
> + int allocated = 10;
> + int *core_numbers = xmalloc (sizeof (int) * allocated);
> + char pathname[128];
> + DIR *dir;
> + struct dirent *dp;
> + struct stat statbuf;
> +
> + sprintf (pathname, "/proc/%d/task", pid);
> + if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
> + {
> + dir = opendir (pathname);
> + if (!dir)
> + return;
> +
> + while ((dp = readdir (dir)) != NULL)
> + {
> + unsigned long lwp = strtoul (dp->d_name, NULL, 10);
> +
> + if (lwp != 0)
> + {
> + unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
> +
> + if (core != -1)
> + {
> + char s[sizeof ("4294967295") + 1];
> + sprintf (s, "%u", core);
> +
> + if (count == allocated) {
'{' in new line, reindent block afterwards.
> + allocated *= 2;
> + core_numbers = realloc (core_numbers,
> + sizeof (int ) * allocated);
Still has spurious space in '(int )'.
> +
Spurious new line.
> + }
> + core_numbers[count++] = core;
> + if (buffer)
> + buffer_xml_printf (buffer,
> + "<item>"
> + "<column name=\"pid\">%d</column>"
> + "<column name=\"tid\">%s</column>"
> + "<column name=\"core\">%s</column>"
> + "</item>", pid, dp->d_name, s);
> + }
> + else
> + {
> + if (buffer)
> + buffer_xml_printf (buffer,
> + "<item>"
> + "<column name=\"pid\">%d</column>"
> + "<column name=\"tid\">%s</column>"
> + "</item>", pid, dp->d_name);
> + }
> + }
> + }
> + }
> +
> + if (cores)
> + {
> + *cores = NULL;
> + if (count > 0)
> + {
> + struct buffer buffer2;
> + int *b;
> + int *e;
> + qsort (core_numbers, count, sizeof (int), compare_ints);
> +
> + /* Remove duplicates. */
> + b = core_numbers;
> + e = unique (b, core_numbers + count);
> +
> + buffer_init (&buffer2);
> +
> + for (b = core_numbers; b != e; ++b)
> + {
> + char number[sizeof ("4294967295") + 1];
> + sprintf (number, "%u", *b);
> + buffer_xml_printf (&buffer2, "%s%s",
> + (b == core_numbers) ? "" : ",", number);
> + }
> + buffer_grow_str0 (&buffer2, "");
> +
> + *cores = buffer_finish (&buffer2);
> + }
> + }
> +}
`core_numbers' is leaking.
> @@ -1604,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...)
> prev = f + 1;
> }
> break;
> + case 'd':
> + {
> + int i = va_arg (ap, char *);
Should be:
int i = va_arg (ap, int);
> + char b[sizeof ("4294967295") + 1];
Off-by-one-too-much (sizeof includes \0).
> +
> + buffer_grow (buffer, prev, f - prev - 1);
> + sprintf (b, "%d", i);
> + buffer_grow_str (buffer, b);
> + prev = f + 1;
> + }
> +/* Return the core for a thread. */
> +static int
Could you please add an empty new line between function describing
comment and function definition (here and elsewhere)? We're trying
to follow that style everywhere in gdb.
> +linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
> +{
> + struct cleanup *back_to;
> + char *filename;
> + FILE *f;
> + char *content = NULL;
> + char *p;
> + char *ts = 0;
> + int content_read = 0;
> + int i;
> + int core;
> +
> + filename = xstrprintf ("/proc/%d/task/%ld/stat",
> + GET_PID (ptid), GET_LWP (ptid));
> + back_to = make_cleanup (xfree, filename);
> +
> + f = fopen (filename, "r");
> + if (!f)
> + return -1;
No biggie, but might as well run the back_to cleanup before
returning.
> static int
> -print_one_inferior (struct inferior *inferior, void *arg)
> +print_one_inferior (struct inferior *inferior, void *xdata)
> {
> - if (inferior->pid != 0)
> + struct print_one_inferior_data *top_data = xdata;
> +
> + if (VEC_length (int, top_data->inferiors) == 0
Might as well use VEC_empty here too, like you've
fixed just a bit below.
> +void
> +mi_cmd_list_thread_groups (char *command, char **argv, int argc)
> +{
(...)
> + qsort (VEC_address (int, ids),
> + VEC_length (int, ids),
> + sizeof (int), compare_positive_ints);
VEC_address (int, ids) can be NULL here. It is undefined
to pass NULL as array to qsort. We've had problems
on solaris recently due to this. There's never a need to
call qsort if the vector is empty (or only if length > 1 if
you prefer) anyway.
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 9fa92fb..e393e47 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
(...)
> +/* The core number that was last seed by process_stop_reply. */
> +static int last_core = -1;
Is "seed" a typo here?
> +/* The thread that corresponds to last_core. */
> +static ptid_t thread_of_last_core;
Are the last_core and thread_of_last_core globals needed
for when the target reports the "core" in the stop reply,
but doesn't support qxfer:threads:read? Should we simply
not care for that, and assume that a stub that wants to
report core info uses the new way to fetch thread info?
I'd prefer not to have these globals, by e.g., creating
info->private on the spot when processing the stop reply.
I understand that you did things this way so
that the presence of info->private means that there's no
need to fetch thread extra info with the old packet, so
I'll probably let this one go, but I'm quite certain
we'll end up using info->private for other
(non qxfer:threads) things in the future anyway, so we'll
need a discriminator anyway.
>@@ -2320,6 +2409,68 @@ remote_threads_info (struct target_ops *ops)
...
> + if (!info->private)
> + info->private = (struct private_thread_info *)
> + xmalloc (sizeof (struct private_thread_info));
> +
> + info->private->extra = item->extra;
> + item->extra = 0;
> + info->private->core = item->core;
I think info->private->extra is leaking when threads are deleted,
because gdb/threads.c simply xfree's ->private. I guess you're the
lucky first to need a destructor for thread private data.
nto-procfs.c was close, but avoids it by using a poor man's flexible
array member (nto-tdep.h).
--
Pedro Alves
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
2009-12-17 15:33 Pedro Alves
@ 2009-12-21 14:48 ` Vladimir Prus
2009-12-31 11:51 ` Pedro Alves
0 siblings, 1 reply; 17+ messages in thread
From: Vladimir Prus @ 2009-12-21 14:48 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
[-- Attachment #1: Type: Text/Plain, Size: 9518 bytes --]
On Thursday 17 December 2009 18:33:21 Pedro Alves wrote:
> (resending in us-ascii)
>
> On Wednesday 16 December 2009 20:52:38, Vladimir Prus wrote:
> > The attached patch implements the core awareness, as discussed at:
> >
> > http://thread.gmane.org/gmane.comp.gdb.devel/27468/
> >
> > The most current spec can be found at:
> >
> > http://article.gmane.org/gmane.comp.gdb.devel/27548
> >
> > To summarize, with this patch, when debugging against gdbserver:
> >
> > * The -list-thread-groups command will report the cores that each
> > process runs on (with or without --available)
> > * It's possible to pass several thread groups to -list-thread-groups
> > * The *stopped output includes core number
> >
> > For native debugging on Linux, roughly the same is also possible.
> >
>
> > This is mostly MI change, but includes some remote protocol tweaks,
> > and documentation. I'd appreciate review of those parts.
>
> More like 80% not-MI changes. :-)
Uhm, well, it accidentally happened.
> > +@item qXfer:threads:read::@var{offset},@var{length}
> > +@anchor{qXfer threads read}
> > +Access the list of threads on target. @xref{Thread List Format}. The
> > +annex part of the generic @samp{qXfer} packet must be empty
> > +(@pxref{qXfer read}).
> > +
> > +This packet is not probed by default; the remote stub must request it,
>
> What's the advantage of this, rather than probing it? I would
> see it nice for GDB to know in advance that the target can
> report core data, but that's not that reporting support
> for this packet means, since the "core" field is optional.
Every other qXfer packet works this way. And it does not seem like much burden
on the stub to announce that this packet is available.
> > diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd
> > index a04506b..67de837 100644
> > --- a/gdb/features/osdata.dtd
> > +++ b/gdb/features/osdata.dtd
> > @@ -10,7 +10,10 @@
> > <!ATTLIST osdata version CDATA #FIXED "1.0">
> > <!ATTLIST osdata type CDATA #REQUIRED>
> >
> > -<!ELEMENT item (column*)>
> > +<!ELEMENT item (column*, threads?)>
> >
> > <!ELEMENT column (#PCDATA)>
> > <!ATTLIST column name CDATA #REQUIRED>
> > +
> > +<!ELEMENT threads (item*)>
> > +
>
> This dtd change isn't right. "osdata" isn't specific to
> processes, it is just a generic table. A "threads" element
> doesn't fit in, it wouldn't make sense for anything other
> than listing processes. Two alternative solutions:
>
> - Allow multiple sub-table elements in the osdata schema, and
> allow a "name" attribute on this element. In the listing
> processes case, you'd have a sub-table whose type/name
> attribute would be "threads". You should change
> info_osdata_command to descend into subtables somehow.
>
> - Don't nest "threads" within processes osdata. Treat it as
> a different request / table (e.g., <osdata type="threads">).
> That is, on the CLI, "info os processes" works as is, unmodified, and,
> you'd add support for "info os threads". No schema changes
> are necessary with this option. At the MI implementation level, you'd
> do two requests to get the same data: get_osdata("processes"), and
> get_osdata("threads").
I have implemented the second approach.
> > + if (core != -1)
> > + {
> > + char s[11];
> > + sprintf (s, "%u", core);
> > +
> > + if (count == allocated)
> > + core_numbers = realloc (core_numbers,
> > + sizeof (int ) * (allocated *= 2));
>
> No space after int. Please move the 'allocated *= 2' in its own
> statement. My GM hat tells me that I should point out that there are a
> bunch of hardcoded buffer sizes in the patch, that the GNU conventions
> tells us we should avoid.
I have replaced '11' with sizeof ("4294967295") + 1.
> > + char number[11];
> > + sprintf (number, "%u", *b);
> > + buffer_xml_printf (&buffer2, "%s%s",
> > + (b == core_numbers) ? "" : ",", number);
> > + }
> > + buffer_grow_str0 (&buffer2, "");
>
>
> There's a bug here somewhere in the duplicates detection that
> we talked about off-list, that I know you've fixed
> already. :-)
Yeah, in attempt to reimplement std::unique in C I've ended up implementing
something else. Of course, if gdb were just *compiled* by a C++ compiler,
I would be able to call std::unique, and that would just work.
> > @@ -2857,11 +2968,29 @@ linux_qxfer_osdata (const char *annex,
> > "<item>"
> > "<column name=\"pid\">%s</column>"
> > "<column name=\"user\">%s</column>"
> > - "<column name=\"command\">%s</column>"
> > - "</item>",
> > + "<column name=\"command\">%s</column>",
> > dp->d_name,
> > entry ? entry->pw_name : "?",
> > cmd);
> > +
> > + buffer_init (&thread_buffer);
> > + list_threads (pid, &thread_buffer, &cores);
> > +
> > + if (cores)
> > + {
> > + buffer_xml_printf (
> > + &buffer,
> > + "<column name=\"cores\">%s</column>", cores);
> > + free (cores);
> > + }
> > +
> > + buffer_grow (&buffer,
> > + thread_buffer.buffer,
> > + thread_buffer.used_size);
> > + free (buffer_finish (&thread_buffer));
> > +
> > +
> > + buffer_xml_printf (&buffer, "</item>");
>
>
> Any chance linux-nat.c also gets support for this (however it
> ends up looking like)?
It is surely possible, but the original motivation here is mostly embedded
systems, so I'd prefer to have gdbserver-only version checked in, and then
anybody interested in native linux can fill the bits for linux.
> > + char filename[sizeof ("/proc//task//stat")
>
> s,//,/
I don't think so, actually. The final path will look like
/proc/NNN/task/MMM/stat
so it will indeed have two slashes between 'proc' and 'task'
> > + case RECURSE_OPT:
> > + if (strcmp (optarg, "1") != 0)
> > + error ("only '1' is permitted value for the '--recurse' option");
>
> Seems strange not to allow '--recurse 0'.
I've made it allowed.
> What future values are you thinking of?
'2', 'inf', for example.
> Wouldn't it make more sense to call this
> "--threads"? We may add other level-1 sublists to thread-groups
> in the future, and _not_ want --recurse 1 to always show
> them, no?
I would say we rather want them. If frontend wishes to grab entire
thread group hierarchy, he should be able to pass --recurse 1 or
in future '--recurse inf' to get absolutely everything.
> > + VEC_safe_push (int, ids, inf);
> > }
> > - else if (available)
> > + qsort (VEC_address (int, ids),
> > + VEC_length (int, ids),
> > + sizeof (int), compare_positive_ints);
> > +
> > + back_to = make_cleanup ((void (*)(void *))VEC_OP (int, free), &ids);
>
> Please don't cast the function's type: instead write a
> small function that has the proper cleanup prototype, and
> call free there.
Is casting function type going to cause practical problems on some targets?
> > + /* Update the core associated with a thread when we process stop
> > + event in that thread. */
> > + info = find_thread_ptid (ptid);
> > + if (info && info->private)
> > + info->private->core = stop_reply->core;
>
> Consider all-stop, multi-threaded applications:
>
> What about the core of the other threads?
> Shouldn't they all be invalidated?
> Won't then be stale?
>
> (see comment below)
Yes, it will be stale. However, it's approximation only, and the only two
places where cores number is used is *stopped response (which uses core of
the stopped thread, updated above) and -list-thread-groups (which updates
this information anyway). It does not seem right to additionally update
this even if nothing will use it.
> > + /* Return the core that thread PTID is on, or -1 if such information
> > + is not available. For a stopped thread, this is supposed to return
> > + the core the thread was last running on. For running threads, it
> > + should return one of the cores that the thread was running between
> > + the call to this function and return -- and if it was running on
> > + several cores, any other may be returned. */
> > + int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
>
> I guess that on some targets, it will be impossible to know which core
> a running thread was running on. Should it be simply documented as
> undefined instead?
I am not sure I understand. On such target, this method will return -1.
> Other:
>
> - did you consider being able to list the available
> cores? A candidate for "info os cores", I guess.
Unfortunately, I do not know a suitable interface to get that information.
Parsing /proc/cpuinfo is not really attractive.
I attach a revision with yours and Eli's comments addressed, expect as
mentioned above, and a delta.
- Volodya
[-- Attachment #2: core2.diff --]
[-- Type: text/x-patch, Size: 60105 bytes --]
commit d4255491201ce7bfe0972b2da30aa8a21f90c451
Author: Vladimir Prus <vladimir@codesourcery.com>
Date: Wed Dec 16 23:39:19 2009 +0300
Implement core awareness.
gdb/
* bcache.c (compare_ints): Remove
(print_percentage): Use compare_positive_ints.
* defs.h (compare_positive_ints): Declare.
* linux-nat.c (linux_nat_core_of_thread): New.
(linux_nat_add_target): Register the above.
* remote.c (struct private_thread_info): New.
(last_core, thread_of_last_core): New.
(PACKET_qXfer_threads, use_osdata_threads): New.
(struct thread_item, threads_parsing_context
(start_thread, end_thread, thread_attributes)
(thread_children, threads_children, threads_elements): New.
(remote_threads_info): Try qXfer:threads before anything
else.
(remote_protocol_packets): Register qXfer:threads.
(remote_open_1): Init use_osdata_threads.
(struct stop_reply): New field 'core'.
(remote_parse_stop_reply): Parse core number.
(process_stop_reply): Record core number.
(remote_xfer_partial): Handle qXfer:threads.
(remote_core_of_thread): New.
(init_remote_ops): Register remote_core_of_thread.
(_initialize_remote): Register qXfer:read.
* target.c (target_core_of_thread): New
* target.h (enum target_object): New value TARGET_OBJECT_THREADS.
(struct target_ops): New field to_core_of_threads.
(target_core_of_thread): Declare.
* thread.c (print_thread_info): Report the core.
* ui-out.c (MAX_UI_OUT_LEVELS): Increase.
* utils.c (compare_positive_ints): New.
* features/threads.dtd: New.
* mi/mi-interp.c (mi_on_normal_stop): Report the core.
* mi/mi-main.c (struct collect_cores_data, collect_cores)
(do_nothing, free_vector_of_osdata_items)
(splay_tree_int_comparator, free_splay_tree): New.
(print_one_inferior_data): Implemented printing of selected
inferiors. Collect and print cores.
(output_cores): New.
(mi_cmd_list_thread_groups): Support --recurse. Permit specifying
thread groups together with --available.
gdbserver/
* linux-low.c (linux_core_of_thread): New.
(compare_ints, show_process, list_threads): New.
(linux_qxfer_osdata): Report threads and cores.
(linux_target_op): Register linux_core_of_thread.
* remote-utils.c (prepare_resume_reply): Report the core.
(buffer_xml_printf): Support %d specifier.
* server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
New.
(handle_query): Handle qXfer:threads. Announce availability
thereof.
* target.h (struct target_ops): New field core_for_threads.
gdb/doc
* gdb.texinfo (GDB/MI Thread Information): New.
(GDB/MI Async Records): Document the core field in *stopped.
(GDB/MI Miscellaneous Commands): Expand -list-thread-groups
documentation
(Process list): Document that osdata document may contain
threads.
(Remote Serial Protocol): Document qXfer:threads.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 348d853..882be85 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -444,7 +444,8 @@ 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/osdata.dtd
+ $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+ $(srcdir)/features/threads.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,
diff --git a/gdb/bcache.c b/gdb/bcache.c
index d56df07..e467c5b 100644
--- a/gdb/bcache.c
+++ b/gdb/bcache.c
@@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
\f
/* Printing statistics. */
-static int
-compare_ints (const void *ap, const void *bp)
-{
- /* Because we know we're comparing two ints which are positive,
- there's no danger of overflow here. */
- return * (int *) ap - * (int *) bp;
-}
-
-
static void
print_percentage (int portion, int total)
{
@@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
/* To compute the median, we need the set of chain lengths sorted. */
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
- compare_ints);
+ compare_positive_ints);
qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
- compare_ints);
+ compare_positive_ints);
if (c->num_buckets > 0)
{
diff --git a/gdb/defs.h b/gdb/defs.h
index b944ffb..396e9a8 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -417,6 +417,8 @@ char *ldirname (const char *filename);
char **gdb_buildargv (const char *);
+int compare_positive_ints (const void *ap, const void *bp);
+
/* From demangle.c */
extern void set_demangling_style (char *);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e880838..4a2f44e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -15402,6 +15402,10 @@ are:
@tab @code{qXfer:siginfo:write}
@tab @code{set $_siginfo}
+@item @code{threads}
+@tab @code{qXfer:threads:read}
+@tab @code{info threads}
+
@item @code{get-thread-local-@*storage-address}
@tab @code{qGetTLSAddr}
@tab Displaying @code{__thread} variables
@@ -21688,6 +21692,7 @@ follow development on @email{gdb@@sourceware.org} and
* GDB/MI Stream Records::
* GDB/MI Async Records::
* GDB/MI Frame Information::
+* GDB/MI Thread Information::
@end menu
@node GDB/MI Result Records
@@ -21780,7 +21785,7 @@ several times, either for different threads, because it cannot resume
all threads together, or even for a single thread, if the thread must
be stepped though some code before letting it run freely.
-@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
+@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
The target has stopped. The @var{reason} field can have one of the
following values:
@@ -21820,7 +21825,9 @@ If all threads are stopped, the @var{stopped} field will have the
value of @code{"all"}. Otherwise, the value of the @var{stopped}
field will be a list of thread identifiers. Presently, this list will
always include a single thread, but frontend should be prepared to see
-several threads in the list.
+several threads in the list. The @var{core} field reports the
+processor core on which the stop event has happened. This field may be absent
+if such information is not available.
@item =thread-group-created,id="@var{id}"
@itemx =thread-group-exited,id="@var{id}"
@@ -21897,6 +21904,34 @@ corresponds to the frame's code address. This field may be absent.
@end table
+@node GDB/MI Thread Information
+@subsection @sc{gdb/mi} Thread Information
+
+Whenever @value{GDBN} has to report an information about a thread, it
+uses a tuple with the following fields:
+
+@table @code
+@item id
+The numeric id assigned to the thread by @value{GDBN}. This field is
+always present.
+
+@item target-id
+Target-specific string identifying the thread. This field is always present.
+
+@item details
+Additional information about the thread provided by the target.
+It is supposed to be human-readable and not interpreted by the
+frontend. This field is optional.
+
+@item state
+Either @samp{stopped} or @samp{running}, depending on whether the
+thread is presently running. This field is always present.
+
+@item core
+The value of this field is an integer number of the processor core the
+thread was last seen on. This field is optional.
+@end table
+
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@node GDB/MI Simple Examples
@@ -26202,20 +26237,84 @@ while the target is running.
@subheading Synopsis
@smallexample
--list-thread-groups [ --available ] [ @var{group} ]
+-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
@end smallexample
-When used without the @var{group} parameter, lists top-level thread
-groups that are being debugged. When used with the @var{group}
-parameter, the children of the specified group are listed. The
-children can be either threads, or other groups. At present,
-@value{GDBN} will not report both threads and groups as children at
-the same time, but it may change in future.
+Lists thread groups (@pxref{Thread groups}). When a single thread
+group is passed as the argument, lists the children of that group.
+When several thread group are passed, lists information about those
+thread groups. Without any parameters, lists information about all
+top-level thread groups.
+
+Normally, thread groups that are being debugged are reported.
+With the @samp{--available} option, @value{GDBN} reports thread groups
+available on the target.
+
+The output of this command may have either a @samp{threads} result or
+a @samp{groups} result. The @samp{thread} result has a list of tuples
+as value, with each tuple describing a thread (@pxref{GDB/MI Thread
+Information}). The @samp{groups} result has a list of tuples as value,
+each tuple describing a thread group. If top-level groups are
+requested (that is, no parameter is passed), or when several groups
+are passed, the output always has a @samp{groups} result. The format
+of the @samp{group} result is described below.
+
+To reduce the number of roundtrips it's possible to list thread groups
+together with their children, by passing the @samp{--recurse} option
+and the recursion depth. Presently, only recursion depth of 1 is
+permitted. If this option is present, then every reported thread group
+will also include its children, either as @samp{group} or
+@samp{threads} field.
+
+In general, any combination of option and parameters is permitted, with
+the following caveats:
+
+@itemize @bullet
+@item
+When a single thread group is passed, the output will typically
+be the @samp{threads} result. Because threads may not contain
+anything, the @samp{recurse} option will be ignored.
+
+@item
+When the @samp{--available} option is passed, limited information may
+be available. In particular, the list of threads of a process might
+be inaccessible. Further, specifying specific thread groups might
+not give any performance advantage over listing all thread groups.
+The frontend should assume that @samp{-list-thread-groups --available}
+is always an expensive operation and cache the results.
+
+@end itemize
+
+The @samp{groups} result is a list of tuples, where each tuple may
+have the following fields:
+
+@table @code
+@item id
+Identifier of the thread group. This field is always present.
+
+@item type
+The type of the thread group. At present, only @samp{process} is a
+valid type.
+
+@item pid
+The target-specific process identifier. This field is only present
+for thread groups of type @samp{process}.
+
+@item num_children
+The number of children this thread group has. This field may be
+absent for an available thread group.
-With the @samp{--available} option, instead of reporting groups that
-are been debugged, GDB will report all thread groups available on the
-target. Using the @samp{--available} option together with @var{group}
-is not allowed.
+@item threads
+This field has a list of tuples as value, each tuple describing a
+thread. It may be present if the @samp{--recurse} option is
+specified, and it's actually possible to obtain the threads.
+
+@item cores
+This field is a list of integers, each identifying a core that one
+thread of the group is running on. This field may be absent if
+such information is not available.
+
+@end table
@subheading Example
@@ -26229,6 +26328,16 @@ is not allowed.
@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
+-list-thread-groups --available
+^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
+-list-thread-groups --available --recurse 1
+ ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
+-list-thread-groups --available --recurse 1 17 18
+^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+ threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+ @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
@end smallexample
@subheading The @code{-interpreter-exec} Command
@@ -27978,6 +28087,7 @@ Show the current setting of the target wait timeout.
* File-I/O Remote Protocol Extension::
* Library List Format::
* Memory Map Format::
+* Thread List Format::
@end menu
@node Overview
@@ -28878,6 +28988,10 @@ If @var{n} is @samp{thread}, then @var{r} is the @var{thread-id} of
the stopped thread, as specified in @ref{thread-id syntax}.
@item
+If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
+the core on which the stop event was detected.
+
+@item
If @var{n} is a recognized @dfn{stop reason}, it describes a more
specific event that stopped the target. The currently defined stop
reasons are listed below. @var{aa} should be @samp{05}, the trap
@@ -28911,8 +29025,6 @@ logged execution events, because it has reached the end (or the
beginning when executing backward) of the log. The value of @var{r}
will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
for more information.
-
-
@end table
@item W @var{AA}
@@ -29452,6 +29564,12 @@ These are the currently defined stub features and their properties:
@tab @samp{-}
@tab Yes
+@item @samp{qXfer:threads:read}
+@tab No
+@tab @samp{-}
+@tab Yes
+
+
@item @samp{QNonStop}
@tab No
@tab @samp{-}
@@ -29535,6 +29653,10 @@ The remote stub understands the @samp{qXfer:siginfo:read} packet
The remote stub understands the @samp{qXfer:siginfo:write} packet
(@pxref{qXfer siginfo write}).
+@item qXfer:threads:read
+The remote stub understands the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}).
+
@item QNonStop
The remote stub understands the @samp{QNonStop} packet
(@pxref{QNonStop}).
@@ -29727,6 +29849,15 @@ This packet is not probed by default; the remote stub must request it,
by supplying an appropriate @samp{qSupported} response
(@pxref{qSupported}).
+@item qXfer:threads:read::@var{offset},@var{length}
+@anchor{qXfer threads read}
+Access the list of threads on target. @xref{Thread List Format}. The
+annex part of the generic @samp{qXfer} packet must be empty
+(@pxref{qXfer read}).
+
+This packet is not probed by default; the remote stub must request it,
+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}.
@@ -31710,6 +31841,30 @@ The formal DTD for memory map format is given below:
<!ATTLIST property name CDATA #REQUIRED>
@end smallexample
+@node Thread List Format
+@section Thread List Format
+@cindex thread list format
+
+To efficiently update the list of threads and their attributes,
+@value{GDBN} issues the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}) and obtains the XML document with
+the following structure:
+
+@smallexample
+<?xml version="1.0"?>
+<threads>
+ <thread id="id" core="0">
+ ... description ...
+ </thread>
+</threads>
+@end smallexample
+
+Each @samp{thread} element must have the @samp{id} attribute that
+identifies the thread (@pxref{thread-id syntax}). The
+@samp{core} attribute, if present, specifies which processor core
+the thread was last executing on. The content of the of @samp{thread}
+element is interpreted as human-readable auxilliary information.
+
@include agentexpr.texi
@node Target Descriptions
@@ -32269,15 +32424,34 @@ An example document is:
<column name="pid">1</column>
<column name="user">root</column>
<column name="command">/sbin/init</column>
+ <column name="cores">1,2,3</column>
+ <threads>
+ <item>
+ <column name="tid">12</column>
+ <column name="core">3</column>
+ </item>
+ </threads>
</item>
</osdata>
@end smallexample
-Each item should include a column whose name is @samp{pid}. The value
+The document has an @samp{osdata} element that contains a number of
+@samp{item} elements describing processes. Each process @samp{item}
+may contain @samp{threads} element with items describing threads of
+that process. Both process and thread items contain a number of
+@samp{column} elements with various information.
+
+Each process 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.
+displayed by @value{GDBN}. The @samp{cores} column, if present,
+should contain a comma-separated list of cores that this process
+is running on.
+
+Each thread item should include a column whose name is @samp{tid}.
+The value of that is a thread identifier, specific to the operating
+system. The @samp{core} column, if present, is the integer number of
+the core the thread is running on.
@include gpl.texi
diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
new file mode 100644
index 0000000..20308ee
--- /dev/null
+++ b/gdb/features/threads.dtd
@@ -0,0 +1,13 @@
+<!-- Copyright (C) 2008, 2009 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. -->
+
+<!ELEMENT threads (thread*)>
+<!ATTLIST threads version CDATA #FIXED "1.0">
+
+<!ELEMENT thread (#PCDATA)>
+
+<!ATTLIST thread id CDATA #REQUIRED>
+<!ATTLIST thread core CDATA #IMPLIED>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 45dd9f7..f042338 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -137,6 +137,7 @@ static void *add_lwp (ptid_t ptid);
static int my_waitpid (int pid, int *status, int flags);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int linux_core_of_thread (ptid_t ptid);
struct pending_signals
{
@@ -2789,6 +2790,171 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
#endif
static int
+compare_ints (const void *xa, const void *xb)
+{
+ int a = *(const int *)xa;
+ int b = *(const int *)xb;
+
+ return a - b;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+/* Given PID, iterates over all threads in that process.
+
+ Information about each thread, in a format suitable for qXfer:osdata:thread
+ is printed to BUFFER, if it's not NULL. The buffer will not be either
+ initialized, or finished, or have '\0' written to it. Caller is responsible
+ for such things.
+
+ The list of cores that threads are running on is assigned to *CORES, if it
+ is not NULL. If not cores are found, *CORES will be set to NULL.
+*/
+
+static void
+list_threads (int pid, struct buffer *buffer, char **cores)
+{
+ int count = 0;
+ int allocated = 10;
+ int *core_numbers = xmalloc (sizeof (int) * allocated);
+ char pathname[128];
+ DIR *dir;
+ struct dirent *dp;
+ struct stat statbuf;
+
+ sprintf (pathname, "/proc/%d/task", pid);
+ if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+ {
+ dir = opendir (pathname);
+ if (!dir)
+ return;
+
+ while ((dp = readdir (dir)) != NULL)
+ {
+ unsigned long lwp = strtoul (dp->d_name, NULL, 10);
+
+ if (lwp != 0)
+ {
+ unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
+
+ if (core != -1)
+ {
+ char s[sizeof ("4294967295") + 1];
+ sprintf (s, "%u", core);
+
+ if (count == allocated) {
+ allocated *= 2;
+ core_numbers = realloc (core_numbers,
+ sizeof (int ) * allocated);
+
+ }
+ core_numbers[count++] = core;
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "<column name=\"core\">%s</column>"
+ "</item>", pid, dp->d_name, s);
+ }
+ else
+ {
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "</item>", pid, dp->d_name);
+ }
+ }
+ }
+ }
+
+ if (cores)
+ {
+ *cores = NULL;
+ if (count > 0)
+ {
+ struct buffer buffer2;
+ int *b;
+ int *e;
+ qsort (core_numbers, count, sizeof (int), compare_ints);
+
+ /* Remove duplicates. */
+ b = core_numbers;
+ e = unique (b, core_numbers + count);
+
+ buffer_init (&buffer2);
+
+ for (b = core_numbers; b != e; ++b)
+ {
+ char number[sizeof ("4294967295") + 1];
+ sprintf (number, "%u", *b);
+ buffer_xml_printf (&buffer2, "%s%s",
+ (b == core_numbers) ? "" : ",", number);
+ }
+ buffer_grow_str0 (&buffer2, "");
+
+ *cores = buffer_finish (&buffer2);
+ }
+ }
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+ char pathname[128];
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+
+ sprintf (pathname, "/proc/%d/cmdline", pid);
+
+ if ((f = fopen (pathname, "r")) != NULL)
+ {
+ size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+ if (len > 0)
+ {
+ char *cores = 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\">%d</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>",
+ pid,
+ username,
+ cmd);
+
+ /* This only collects core numbers, and does not print threads. */
+ list_threads (pid, NULL, &cores);
+
+ if (cores)
+ {
+ buffer_xml_printf (buffer,
+ "<column name=\"cores\">%s</column>", cores);
+ free (cores);
+ }
+
+ buffer_xml_printf (buffer, "</item>");
+ }
+ fclose (f);
+ }
+}
+
+static int
linux_qxfer_osdata (const char *annex,
unsigned char *readbuf, unsigned const char *writebuf,
CORE_ADDR offset, int len)
@@ -2798,10 +2964,16 @@ linux_qxfer_osdata (const char *annex,
static const char *buf;
static long len_avail = -1;
static struct buffer buffer;
+ int processes = 0;
+ int threads = 0;
DIR *dirp;
- if (strcmp (annex, "processes") != 0)
+ if (strcmp (annex, "processes") == 0)
+ processes = 1;
+ else if (strcmp (annex, "threads") == 0)
+ threads = 1;
+ else
return 0;
if (!readbuf || writebuf)
@@ -2814,7 +2986,10 @@ linux_qxfer_osdata (const char *annex,
len_avail = 0;
buf = NULL;
buffer_init (&buffer);
- buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ if (processes)
+ buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ else if (threads)
+ buffer_grow_str (&buffer, "<osdata type=\"threads\">");
dirp = opendir ("/proc");
if (dirp)
@@ -2833,37 +3008,16 @@ linux_qxfer_osdata (const char *annex,
if (stat (procentry, &statbuf) == 0
&& S_ISDIR (statbuf.st_mode))
{
- char pathname[128];
- FILE *f;
- char cmd[MAXPATHLEN + 1];
- struct passwd *entry;
+ int pid = (int) strtoul (dp->d_name, NULL, 10);
- sprintf (pathname, "/proc/%s/cmdline", dp->d_name);
- entry = getpwuid (statbuf.st_uid);
-
- if ((f = fopen (pathname, "r")) != NULL)
+ if (processes)
+ {
+ struct passwd *entry = getpwuid (statbuf.st_uid);
+ show_process (pid, entry ? entry->pw_name : "?", &buffer);
+ }
+ else if (threads)
{
- 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);
+ list_threads (pid, &buffer, NULL);
}
}
}
@@ -3139,6 +3293,55 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
return ret;
}
+static int
+linux_core_of_thread (ptid_t ptid)
+{
+ char filename[sizeof ("/proc//task//stat")
+ + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
+ + 1];
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ sprintf (filename, "/proc/%d/task/%ld/stat",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid));
+ f = fopen (filename, "r");
+ if (!f)
+ return -1;
+
+ for (;;)
+ {
+ int n;
+ content = realloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ free (content);
+ fclose (f);
+
+ return core;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -3178,10 +3381,11 @@ static struct target_ops linux_target_ops = {
linux_start_non_stop,
linux_supports_multi_process,
#ifdef USE_THREAD_DB
- thread_db_handle_monitor_command
+ thread_db_handle_monitor_command,
#else
- NULL
+ NULL,
#endif
+ linux_core_of_thread
};
static void
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 158b653..4e17ba1 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
gdbserver to know what inferior_ptid is. */
if (1 || !ptid_equal (general_thread, ptid))
{
+ int core = -1;
/* In non-stop, don't change the general thread behind
GDB's back. */
if (!non_stop)
@@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
buf = write_ptid (buf, ptid);
strcat (buf, ";");
buf += strlen (buf);
+
+ if (the_target->core_for_thread)
+ core = (*the_target->core_for_thread) (ptid);
+ if (core != -1)
+ {
+ sprintf (buf, "core:");
+ buf += strlen (buf);
+ sprintf (buf, "%x", core);
+ strcat (buf, ";");
+ buf += strlen (buf);
+ }
}
}
@@ -1604,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...)
prev = f + 1;
}
break;
+ case 'd':
+ {
+ int i = va_arg (ap, char *);
+ char b[sizeof ("4294967295") + 1];
+
+ buffer_grow (buffer, prev, f - prev - 1);
+ sprintf (b, "%d", i);
+ buffer_grow_str (buffer, b);
+ prev = f + 1;
+ }
}
percent = 0;
}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 14bc7e7..56806e2 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
}
}
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+ struct inferior_list_entry *thread;
+
+ buffer_grow_str (buffer, "<threads>\n");
+
+ for (thread = all_threads.head; thread; thread = thread->next)
+ {
+ ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+ char ptid_s[100];
+ int core = -1;
+ char core_s[21];
+
+ write_ptid (ptid_s, ptid);
+
+ if (the_target->core_for_thread)
+ core = (*the_target->core_for_thread) (ptid);
+
+ if (core != -1)
+ {
+ sprintf (core_s, "%d", core);
+ buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+ ptid_s, core_s);
+ }
+ else
+ {
+ buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+ ptid_s);
+ }
+ }
+
+ buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+ unsigned char *readbuf,
+ CORE_ADDR offset, int length)
+{
+ static char *result = 0;
+ static unsigned int result_length = 0;
+
+ if (annex && strcmp (annex, "") != 0)
+ return 0;
+
+ if (offset == 0)
+ {
+ struct buffer buffer;
+ /* When asked for data at offset 0, generate everything and store into
+ 'result'. Successive reads will be served off 'result'. */
+ if (result)
+ free (result);
+
+ buffer_init (&buffer);
+
+ handle_threads_qxfer_proper (&buffer);
+
+ result = buffer_finish (&buffer);
+ result_length = strlen (result);
+ buffer_free (&buffer);
+ }
+
+ if (offset >= result_length)
+ {
+ /* We're out of data. */
+ free (result);
+ result = NULL;
+ result_length = 0;
+ return 0;
+ }
+
+ if (length > result_length - offset)
+ length = result_length - offset;
+
+ memcpy (readbuf, result + offset, length);
+
+ return length;
+
+}
+
/* Handle all of the extended 'q' packets. */
void
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}
+ if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+ {
+ unsigned char *data;
+ int n;
+ CORE_ADDR ofs;
+ unsigned int len;
+ char *annex;
+
+ require_running (own_buf);
+
+ /* Reject any annex; grab the offset and length. */
+ if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+ || annex[0] != '\0')
+ {
+ strcpy (own_buf, "E00");
+ return;
+ }
+
+ /* Read one extra byte, as an indicator of whether there is
+ more. */
+ if (len > PBUFSIZ - 2)
+ len = PBUFSIZ - 2;
+ data = malloc (len + 1);
+ if (!data)
+ return;
+ n = handle_threads_qxfer (annex, data, ofs, len + 1);
+ if (n < 0)
+ write_enn (own_buf);
+ else if (n > len)
+ *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+ else
+ *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+ free (data);
+ return;
+ }
+
/* Protocol features query. */
if (strncmp ("qSupported", own_buf, 10) == 0
&& (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
+ strcat (own_buf, ";qXfer:threads:read+");
+
return;
}
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 0e4b127..8e8778a 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -283,6 +283,9 @@ struct target_ops
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
+
+ /* Returns the core given a thread, or -1 if not known. */
+ int (*core_for_thread) (ptid_t);
};
extern struct target_ops *the_target;
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 2f388fb..bd0044b 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -346,4 +346,6 @@ extern struct cleanup *make_cleanup_restore_current_thread (void);
INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
extern struct thread_info* inferior_thread (void);
+extern void update_thread_list (void);
+
#endif /* GDBTHREAD_H */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index c0afecd..7c528f2 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -5421,6 +5421,62 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
return inf->aspace;
}
+/* Return the core for a thread. */
+static int
+linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct cleanup *back_to;
+ char *filename;
+ FILE *f;
+ char *content = NULL;
+ char *p;
+ char *ts = 0;
+ int content_read = 0;
+ int i;
+ int core;
+
+ filename = xstrprintf ("/proc/%d/task/%ld/stat",
+ GET_PID (ptid), GET_LWP (ptid));
+ back_to = make_cleanup (xfree, filename);
+
+ f = fopen (filename, "r");
+ if (!f)
+ return -1;
+
+ make_cleanup_fclose (f);
+
+ for (;;)
+ {
+ int n;
+ content = xrealloc (content, content_read + 1024);
+ n = fread (content + content_read, 1, 1024, f);
+ content_read += n;
+ if (n < 1024)
+ {
+ content[content_read] = '\0';
+ break;
+ }
+ }
+
+ make_cleanup (xfree, content);
+
+ p = strchr (content, '(');
+ p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+ /* If the first field after program name has index 0, then core number is
+ the field with index 36. There's no constant for that anywhere. */
+ p = strtok_r (p, " ", &ts);
+ for (i = 0; i != 36; ++i)
+ p = strtok_r (NULL, " ", &ts);
+
+ if (sscanf (p, "%d", &core) == 0)
+ core = -1;
+
+ do_cleanups (back_to);
+
+ return core;
+}
+
void
linux_nat_add_target (struct target_ops *t)
{
@@ -5461,6 +5517,8 @@ linux_nat_add_target (struct target_ops *t)
t->to_supports_multi_process = linux_nat_supports_multi_process;
+ t->to_core_of_thread = linux_nat_core_of_thread;
+
/* We don't change the stratum; this target will sit at
process_stratum and thread_db will set at thread_stratum. This
is a little strange, since this is a multi-threaded-capable
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 248cd66..af51bde 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
if (print_frame)
{
+ int core;
if (uiout != mi_uiout)
{
/* The normal_stop function has printed frame information into
@@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
}
else
ui_out_field_string (mi_uiout, "stopped-threads", "all");
+
+ core = target_core_of_thread (inferior_ptid);
+ if (core != -1)
+ ui_out_field_int (mi_uiout, "core", core);
}
fputs_unfiltered ("*stopped", raw_stdout);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 2332752..96bc39e 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -50,6 +50,7 @@
#include "valprint.h"
#include "inferior.h"
#include "osdata.h"
+#include "splay-tree.h"
#include <ctype.h>
#include <sys/time.h>
@@ -359,11 +360,55 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
print_thread_info (uiout, thread, -1);
}
+struct collect_cores_data
+{
+ int pid;
+
+ VEC (int) *cores;
+};
+
+static int
+collect_cores (struct thread_info *ti, void *xdata)
+{
+ struct collect_cores_data *data = xdata;
+
+ if (ptid_get_pid (ti->ptid) == data->pid)
+ {
+ int core = target_core_of_thread (ti->ptid);
+ if (core != -1)
+ VEC_safe_push (int, data->cores, core);
+ }
+
+ return 0;
+}
+
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+struct print_one_inferior_data
+{
+ int recurse;
+ VEC (int) *inferiors;
+};
+
static int
-print_one_inferior (struct inferior *inferior, void *arg)
+print_one_inferior (struct inferior *inferior, void *xdata)
{
- if (inferior->pid != 0)
+ struct print_one_inferior_data *top_data = xdata;
+
+ if (VEC_length (int, top_data->inferiors) == 0
+ || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
+ VEC_length (int, top_data->inferiors), sizeof (int),
+ compare_positive_ints))
{
+ struct collect_cores_data data;
struct cleanup *back_to
= make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -371,81 +416,298 @@ print_one_inferior (struct inferior *inferior, void *arg)
ui_out_field_string (uiout, "type", "process");
ui_out_field_int (uiout, "pid", inferior->pid);
+ data.pid = inferior->pid;
+ data.cores = 0;
+ iterate_over_threads (collect_cores, &data);
+
+ if (!VEC_empty (int, data.cores))
+ {
+ int elt;
+ int i;
+ int *b, *e;
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_list_begin_end (uiout, "cores");
+
+ qsort (VEC_address (int, data.cores),
+ VEC_length (int, data.cores), sizeof (int),
+ compare_positive_ints);
+
+ b = VEC_address (int, data.cores);
+ e = b + VEC_length (int, data.cores);
+ e = unique (b, e);
+
+ for (; b != e; ++b)
+ ui_out_field_int (uiout, NULL, *b);
+
+ do_cleanups (back_to_2);
+ }
+
+ if (top_data->recurse)
+ print_thread_info (uiout, -1, inferior->pid);
+
do_cleanups (back_to);
}
return 0;
}
-void
-mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+/* Output a field named 'cores' with a list as the value. The elements of
+ the list are obtained by splitting 'cores' on comma. */
+
+static void
+output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
{
- struct cleanup *back_to;
- int available = 0;
- char *id = NULL;
+ struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+ field_name);
+ char *cores = xstrdup (xcores);
+ char *p = cores;
- if (argc > 0 && strcmp (argv[0], "--available") == 0)
- {
- ++argv;
- --argc;
- available = 1;
- }
+ make_cleanup (xfree, cores);
- if (argc > 0)
- id = argv[0];
+ for (p = strtok (p, ","); p; p = strtok (NULL, ","))
+ ui_out_field_string (uiout, NULL, p);
- back_to = make_cleanup (null_cleanup, NULL);
+ do_cleanups (back_to);
+}
- if (available && id)
- {
- error (_("Can only report top-level available thread groups"));
- }
- else if (available)
- {
- struct osdata *data;
- struct osdata_item *item;
- int ix_items;
+static void
+free_vector_of_ints (void *xvector)
+{
+ VEC (int) **vector = xvector;
+ VEC_free (int, *vector);
+}
- data = get_osdata ("processes");
- make_cleanup_osdata_free (data);
+static void
+do_nothing (splay_tree_key k)
+{
+}
- make_cleanup_ui_out_list_begin_end (uiout, "groups");
+static void
+free_vector_of_osdata_items (splay_tree_value xvalue)
+{
+ VEC (osdata_item_s) *value = (VEC (osdata_item_s) *) xvalue;
+ /* We don't free the items itself, it will be done separately. */
+ VEC_free (osdata_item_s, value);
+}
+
+static int
+splay_tree_int_comparator (splay_tree_key xa, splay_tree_key xb)
+{
+ int a = xa;
+ int b = xb;
+ return a - b;
+}
+
+static void
+free_splay_tree (void *xt)
+{
+ splay_tree t = xt;
+ splay_tree_delete (t);
+}
+
+static void
+list_available_thread_groups (VEC (int) *ids, int recurse)
+{
+ struct osdata *data;
+ struct osdata_item *item;
+ int ix_items;
+ /* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
+ The vector contains information about all threads for the given
+ pid. */
+ splay_tree tree;
+
+ /* get_osdata will throw if it cannot return data. */
+ data = get_osdata ("processes");
+ make_cleanup_osdata_free (data);
+
+ if (recurse)
+ {
+ struct osdata *threads = get_osdata ("threads");
+ make_cleanup_osdata_free (threads);
+
+ tree = splay_tree_new (splay_tree_int_comparator,
+ do_nothing,
+ free_vector_of_osdata_items);
+ make_cleanup (free_splay_tree, tree);
for (ix_items = 0;
- VEC_iterate (osdata_item_s, data->items,
+ VEC_iterate (osdata_item_s, threads->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");
+ int pid_i = strtoul (pid, NULL, 0);
+ VEC (osdata_item_s) *vec = 0;
+
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (!n)
+ {
+ VEC_safe_push (osdata_item_s, vec, item);
+ splay_tree_insert (tree, pid_i, (splay_tree_value)vec);
+ }
+ else
+ {
+ vec = (VEC (osdata_item_s) *) n->value;
+ VEC_safe_push (osdata_item_s, vec, item);
+ n->value = (splay_tree_value) vec;
+ }
+ }
+ }
+
+ 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;
+
+ const char *pid = get_osdata_column (item, "pid");
+ const char *cmd = get_osdata_column (item, "command");
+ const char *user = get_osdata_column (item, "user");
+ const char *cores = get_osdata_column (item, "cores");
+
+ int pid_i = strtoul (pid, NULL, 0);
+
+ /* At present, the target will return all available processes
+ and if information about specific ones was required, we filter
+ undesired processes here. */
+ if (ids && bsearch (&pid_i, VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints) == NULL)
+ continue;
+
- 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);
+ back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
- do_cleanups (back_to);
+ 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);
+ if (cores)
+ output_cores (uiout, "cores", cores);
+
+ if (recurse)
+ {
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (n)
+ {
+ VEC (osdata_item_s) *children = (VEC (osdata_item_s) *) n->value;
+ struct osdata_item *child;
+ int ix_child;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+ for (ix_child = 0;
+ VEC_iterate (osdata_item_s, children, ix_child, child);
+ ++ix_child)
+ {
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *tid = get_osdata_column (child, "tid");
+ const char *tcore = get_osdata_column (child, "core");
+ ui_out_field_string (uiout, "id", tid);
+ if (tcore)
+ ui_out_field_string (uiout, "core", tcore);
+
+ do_cleanups (back_to_2);
+ }
+ }
}
+
+ do_cleanups (back_to);
}
- else if (id)
+}
+
+void
+mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+{
+ struct cleanup *back_to;
+ int available = 0;
+ int recurse = 0;
+ VEC (int) *ids = 0;
+
+ enum opt
{
- int pid = atoi (id);
+ AVAILABLE_OPT, RECURSE_OPT
+ };
+ static struct mi_opt opts[] =
+ {
+ {"-available", AVAILABLE_OPT, 0},
+ {"-recurse", RECURSE_OPT, 1},
+ { 0, 0, 0 }
+ };
+
+ int optind = 0;
+ char *optarg;
+
+ while (1)
+ {
+ int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+ &optind, &optarg);
+ if (opt < 0)
+ break;
+ switch ((enum opt) opt)
+ {
+ case AVAILABLE_OPT:
+ available = 1;
+ break;
+ case RECURSE_OPT:
+ if (strcmp (optarg, "0") == 0)
+ ;
+ else if (strcmp (optarg, "1") == 0)
+ recurse = 1;
+ else
+ error ("only '0' and '1' are valid values for the '--recurse' option");
+ break;
+ }
+ }
+
+ for (; optind < argc; ++optind)
+ {
+ char *end;
+ int inf = strtoul (argv[optind], &end, 0);
+ if (*end != '\0')
+ error ("invalid group id '%s'", argv[optind]);
+ VEC_safe_push (int, ids, inf);
+ }
+ qsort (VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints);
+
+ back_to = make_cleanup (free_vector_of_ints, &ids);
+
+ if (available)
+ {
+ list_available_thread_groups (ids, recurse);
+ }
+ else if (VEC_length (int, ids) == 1)
+ {
+ /* Local thread groups, single id. */
+ int pid = *VEC_address (int, ids);
if (!in_inferior_list (pid))
- error ("Invalid thread group id '%s'", id);
- print_thread_info (uiout, -1, pid);
+ error ("Invalid thread group id '%d'", pid);
+ print_thread_info (uiout, -1, pid);
}
else
{
+ struct print_one_inferior_data data;
+ data.recurse = recurse;
+ data.inferiors = ids;
+
+ /* Local thread groups. Either no explicit ids -- and we
+ print everything, or several explicit ids. In both cases,
+ we print more than one group, and have to use 'groups'
+ as the top-level element. */
make_cleanup_ui_out_list_begin_end (uiout, "groups");
- iterate_over_inferiors (print_one_inferior, NULL);
+ update_thread_list ();
+ iterate_over_inferiors (print_one_inferior, &data);
}
-
+
do_cleanups (back_to);
}
diff --git a/gdb/remote.c b/gdb/remote.c
index 9fa92fb..e393e47 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -60,6 +60,7 @@
#include "remote-fileio.h"
#include "gdb/fileio.h"
#include "gdb_stat.h"
+#include "xml-support.h"
#include "memory-map.h"
@@ -303,6 +304,19 @@ struct remote_state
int ctrlc_pending_p;
};
+/* Private data that we'll store in (struct thread_info)->private. */
+struct private_thread_info
+{
+ char *extra;
+ int core;
+};
+
+/* The core number that was last seed by process_stop_reply. */
+static int last_core = -1;
+
+/* The thread that corresponds to last_core. */
+static ptid_t thread_of_last_core;
+
/* Returns true if the multi-process extensions are in effect. */
static int
remote_multi_process_p (struct remote_state *rs)
@@ -1054,6 +1068,7 @@ enum {
PACKET_qXfer_spu_read,
PACKET_qXfer_spu_write,
PACKET_qXfer_osdata,
+ PACKET_qXfer_threads,
PACKET_qGetTLSAddr,
PACKET_qSupported,
PACKET_QPassSignals,
@@ -2303,6 +2318,80 @@ remote_find_new_threads (void)
CRAZY_MAX_THREADS);
}
+#if defined(HAVE_LIBEXPAT)
+
+typedef struct thread_item
+{
+ ptid_t ptid;
+ char *extra;
+ int core;
+} thread_item_t;
+DEF_VEC_O(thread_item_t);
+
+struct threads_parsing_context
+{
+ VEC (thread_item_t) *items;
+};
+
+static void
+start_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+ struct threads_parsing_context *data = user_data;
+
+ struct thread_item item;
+ char *id;
+
+ id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+ item.ptid = read_ptid (id, NULL);
+
+ if (VEC_length (gdb_xml_value_s, attributes) > 1)
+ item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
+ else
+ item.core = -1;
+
+ item.extra = 0;
+
+ VEC_safe_push (thread_item_t, data->items, &item);
+}
+
+static void
+end_thread (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data, const char *body_text)
+{
+ struct threads_parsing_context *data = user_data;
+
+ if (body_text && *body_text)
+ VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
+}
+
+const struct gdb_xml_attribute thread_attributes[] = {
+ { "id", GDB_XML_AF_NONE, NULL, NULL },
+ { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element thread_children[] = {
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_children[] = {
+ { "thread", thread_attributes, thread_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ start_thread, end_thread },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_elements[] = {
+ { "threads", NULL, threads_children,
+ GDB_XML_EF_NONE, NULL, NULL },
+ { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+#endif
+
/*
* Find all threads for info threads command.
* Uses new thread protocol contributed by Cisco.
@@ -2320,6 +2409,68 @@ remote_threads_info (struct target_ops *ops)
if (remote_desc == 0) /* paranoia */
error (_("Command can only be used when connected to the remote target."));
+#if defined(HAVE_LIBEXPAT)
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ char *xml = target_read_stralloc (¤t_target,
+ TARGET_OBJECT_THREADS, NULL);
+
+ struct cleanup *back_to = make_cleanup (xfree, xml);
+ if (xml && *xml)
+ {
+ struct gdb_xml_parser *parser;
+ struct threads_parsing_context context;
+ struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+ context.items = 0;
+ parser = gdb_xml_create_parser_and_cleanup (_("threads"),
+ threads_elements,
+ &context);
+
+ gdb_xml_use_dtd (parser, "threads.dtd");
+
+ if (gdb_xml_parse (parser, xml) == 0)
+ {
+ int i;
+ struct thread_item *item;
+
+ for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
+ {
+ if (!ptid_equal (item->ptid, null_ptid))
+ {
+ struct thread_info *info;
+ /* In non-stop mode, we assume new found threads
+ are running until proven otherwise with a
+ stop reply. In all-stop, we can only get
+ here if all threads are stopped. */
+ int running = non_stop ? 1 : 0;
+
+ remote_notice_new_inferior (item->ptid, running);
+
+ info = find_thread_ptid (item->ptid);
+ if (info)
+ {
+ if (!info->private)
+ info->private = (struct private_thread_info *)
+ xmalloc (sizeof (struct private_thread_info));
+
+ info->private->extra = item->extra;
+ item->extra = 0;
+ info->private->core = item->core;
+ }
+ }
+ xfree (item->extra);
+ }
+ }
+
+ VEC_free (thread_item_t, context.items);
+ }
+
+ do_cleanups (back_to);
+ return;
+ }
+#endif
+
if (use_threadinfo_query)
{
putpkt ("qfThreadInfo");
@@ -2392,6 +2543,15 @@ remote_threads_extra_info (struct thread_info *tp)
server doesn't know about it. */
return NULL;
+ if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+ {
+ struct thread_info *info = find_thread_ptid (tp->ptid);
+ if (info && info->private)
+ return info->private->extra;
+ else
+ return NULL;
+ }
+
if (use_threadextra_query)
{
char *b = rs->buf;
@@ -3152,6 +3312,8 @@ static struct protocol_feature remote_protocol_features[] = {
PACKET_qXfer_spu_write },
{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
PACKET_qXfer_osdata },
+ { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
+ PACKET_qXfer_threads },
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
PACKET_QPassSignals },
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -4262,6 +4424,8 @@ struct stop_reply
int solibs_changed;
int replay_event;
+
+ int core;
};
/* The list of already fetched and acknowledged stop events. */
@@ -4425,6 +4589,7 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
event->replay_event = 0;
event->stopped_by_watchpoint_p = 0;
event->regcache = NULL;
+ event->core = -1;
switch (buf[0])
{
@@ -4451,7 +4616,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
/* If this packet is an awatch packet, don't parse the 'a'
as a register number. */
- if (strncmp (p, "awatch", strlen("awatch")) != 0)
+ if (strncmp (p, "awatch", strlen("awatch")) != 0
+ && strncmp (p, "core", strlen ("core") != 0))
{
/* Read the ``P'' register number. */
pnum = strtol (p, &p_temp, 16);
@@ -4497,6 +4663,12 @@ Packet: '%s'\n"),
if (p_temp)
p = p_temp;
}
+ else if (strncmp (p, "core", p1 - p) == 0)
+ {
+ ULONGEST c;
+ p = unpack_varlen_hex (++p1, &c);
+ event->core = c;
+ }
else
{
/* Silently skip unknown optional info. */
@@ -4706,6 +4878,7 @@ process_stop_reply (struct stop_reply *stop_reply,
struct target_waitstatus *status)
{
ptid_t ptid;
+ struct thread_info *info;
*status = stop_reply->ws;
ptid = stop_reply->ptid;
@@ -4736,6 +4909,19 @@ process_stop_reply (struct stop_reply *stop_reply,
remote_stopped_by_watchpoint_p = stop_reply->stopped_by_watchpoint_p;
remote_watch_data_address = stop_reply->watch_data_address;
+ /* Update the core associated with a thread when we process stop
+ event in that thread. */
+ info = find_thread_ptid (ptid);
+ if (info && info->private)
+ {
+ info->private->core = stop_reply->core;
+ }
+ else
+ {
+ last_core = stop_reply->core;
+ thread_of_last_core = ptid;
+ }
+
remote_notice_new_inferior (ptid, 0);
}
@@ -7579,6 +7765,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
(ops, "osdata", annex, readbuf, offset, len,
&remote_protocol_packets[PACKET_qXfer_osdata]);
+ case TARGET_OBJECT_THREADS:
+ gdb_assert (annex == NULL);
+ return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
+ &remote_protocol_packets[PACKET_qXfer_threads]);
+
default:
return -1;
}
@@ -8895,6 +9086,17 @@ remote_supports_cond_tracepoints (void)
return rs->cond_tracepoints;
}
+static int
+remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+ struct thread_info *info = find_thread_ptid (ptid);
+ if (info && info->private)
+ return info->private->core;
+ else if (ptid_equal (ptid, thread_of_last_core))
+ return last_core;
+ return -1;
+}
+
static void
init_remote_ops (void)
{
@@ -8958,6 +9160,7 @@ Specify the serial device it is connected to\n\
remote_ops.to_terminal_ours = remote_terminal_ours;
remote_ops.to_supports_non_stop = remote_supports_non_stop;
remote_ops.to_supports_multi_process = remote_supports_multi_process;
+ remote_ops.to_core_of_thread = remote_core_of_thread;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -9317,6 +9520,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
"qXfer:osdata:read", "osdata", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
+ "qXfer:threads:read", "threads", 0);
+
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
"qXfer:siginfo:read", "read-siginfo-object", 0);
diff --git a/gdb/target.c b/gdb/target.c
index cd1614b..4fd82b9 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3024,6 +3024,26 @@ target_store_registers (struct regcache *regcache, int regno)
noprocess ();
}
+int
+target_core_of_thread (ptid_t ptid)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ {
+ if (t->to_core_of_thread != NULL)
+ {
+ int retval = t->to_core_of_thread (t, ptid);
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
+ PIDGET (ptid), retval);
+ return retval;
+ }
+ }
+
+ return -1;
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
diff --git a/gdb/target.h b/gdb/target.h
index ebe6056..55e0e84 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -255,6 +255,8 @@ enum target_object
/* Extra signal info. Usually the contents of `siginfo_t' on unix
platforms. */
TARGET_OBJECT_SIGNAL_INFO,
+ /* The list of threads that are being debugged. */
+ TARGET_OBJECT_THREADS,
/* Possible future objects: TARGET_OBJECT_FILE, ... */
};
@@ -597,6 +599,14 @@ struct target_ops
struct address_space *(*to_thread_address_space) (struct target_ops *,
ptid_t);
+ /* Return the core that thread PTID is on, or -1 if such information
+ is not available. For a stopped thread, this is supposed to return
+ the core the thread was last running on. For a running thread, it
+ should return one of the cores that the thread was running between
+ the call to this function and return -- and if it was running on
+ several cores, any other may be returned. */
+ int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
+
int to_magic;
/* Need sub-structure for target machine related rather than comm related?
*/
@@ -1246,6 +1256,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
(*current_target.to_log_command) (p); \
while (0)
+
+extern int target_core_of_thread (ptid_t ptid);
+
/* Routines for maintenance of the target structures...
add_target: Add a target to the list of all possible targets.
diff --git a/gdb/thread.c b/gdb/thread.c
index 689e9d5..fc2ee2a 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -468,8 +468,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
struct cleanup *cleanup_chain;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
@@ -748,8 +747,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
char *extra_info;
int current_thread = -1;
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
current_ptid = inferior_ptid;
/* We'll be switching threads temporarily. */
@@ -759,6 +757,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
for (tp = thread_list; tp; tp = tp->next)
{
struct cleanup *chain2;
+ int core;
if (requested_thread != -1 && tp->num != requested_thread)
continue;
@@ -817,6 +816,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
ui_out_field_string (uiout, "state", state);
}
+ core = target_core_of_thread (tp->ptid);
+ if (ui_out_is_mi_like_p (uiout) && core != -1)
+ ui_out_field_int (uiout, "core", core);
+
do_cleanups (chain2);
}
@@ -1058,8 +1061,7 @@ thread_apply_all_command (char *cmd, int from_tty)
if (cmd == NULL || *cmd == '\000')
error (_("Please specify a command following the thread ID list"));
- prune_threads ();
- target_find_new_threads ();
+ update_thread_list ();
old_chain = make_cleanup_restore_current_thread ();
@@ -1245,6 +1247,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
return GDB_RC_OK;
}
+void
+update_thread_list (void)
+{
+ prune_threads ();
+ target_find_new_threads ();
+}
+
/* Commands with a prefix of `thread'. */
struct cmd_list_element *thread_cmd_list = NULL;
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index 19a4644..6ecdd1f 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -44,7 +44,7 @@ struct ui_out_hdr
is always available. Stack/nested level 0 is reserved for the
top-level result. */
-enum { MAX_UI_OUT_LEVELS = 6 };
+enum { MAX_UI_OUT_LEVELS = 8 };
struct ui_out_level
{
diff --git a/gdb/utils.c b/gdb/utils.c
index 16ad084..c9aa612 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
return argv;
}
+int
+compare_positive_ints (const void *ap, const void *bp)
+{
+ /* Because we know we're comparing two ints which are positive,
+ there's no danger of overflow here. */
+ return * (int *) ap - * (int *) bp;
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
extern initialize_file_ftype _initialize_utils;
[-- Attachment #3: delta.diff --]
[-- Type: text/x-patch, Size: 39728 bytes --]
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index d1d5075..4a2f44e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -21825,8 +21825,8 @@ If all threads are stopped, the @var{stopped} field will have the
value of @code{"all"}. Otherwise, the value of the @var{stopped}
field will be a list of thread identifiers. Presently, this list will
always include a single thread, but frontend should be prepared to see
-several threads in the list. The @var{core} field reports on which
-processor core the stop event has happened. This field may be absent
+several threads in the list. The @var{core} field reports the
+processor core on which the stop event has happened. This field may be absent
if such information is not available.
@item =thread-group-created,id="@var{id}"
@@ -21907,12 +21907,12 @@ corresponds to the frame's code address. This field may be absent.
@node GDB/MI Thread Information
@subsection @sc{gdb/mi} Thread Information
-Whenver @value{GDBN} has to report an information about a thread, it
+Whenever @value{GDBN} has to report an information about a thread, it
uses a tuple with the following fields:
@table @code
@item id
-The numeric id assigned to the thread by @value{GDBN}. This field is
+The numeric id assigned to the thread by @value{GDBN}. This field is
always present.
@item target-id
@@ -21921,13 +21921,13 @@ Target-specific string identifying the thread. This field is always present.
@item details
Additional information about the thread provided by the target.
It is supposed to be human-readable and not interpreted by the
-frontend. This field is optional.
+frontend. This field is optional.
@item state
Either @samp{stopped} or @samp{running}, depending on whether the
thread is presently running. This field is always present.
-@item cores
+@item core
The value of this field is an integer number of the processor core the
thread was last seen on. This field is optional.
@end table
@@ -26240,8 +26240,8 @@ while the target is running.
-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
@end smallexample
-Lists thread groups (@pxref{Thread groups}). When a single thread
-group is passed as the argument, lists the children specified one.
+Lists thread groups (@pxref{Thread groups}). When a single thread
+group is passed as the argument, lists the children of that group.
When several thread group are passed, lists information about those
thread groups. Without any parameters, lists information about all
top-level thread groups.
@@ -26251,18 +26251,18 @@ With the @samp{--available} option, @value{GDBN} reports thread groups
available on the target.
The output of this command may have either a @samp{threads} result or
-a @samp{groups} result. The @samp{thread} result has a list of tuples
+a @samp{groups} result. The @samp{thread} result has a list of tuples
as value, with each tuple describing a thread (@pxref{GDB/MI Thread
-Information}). The @samp{groups} result has a list of tuples as value,
+Information}). The @samp{groups} result has a list of tuples as value,
each tuple describing a thread group. If top-level groups are
requested (that is, no parameter is passed), or when several groups
-are passed, the output always have a @samp{groups} result. The format
+are passed, the output always has a @samp{groups} result. The format
of the @samp{group} result is described below.
To reduce the number of roundtrips it's possible to list thread groups
together with their children, by passing the @samp{--recurse} option
-and the recursion depth. Presently, only recursion depth of 1 is
-permitted. If this option is present, then every reported thread group
+and the recursion depth. Presently, only recursion depth of 1 is
+permitted. If this option is present, then every reported thread group
will also include its children, either as @samp{group} or
@samp{threads} field.
@@ -26270,7 +26270,7 @@ In general, any combination of option and parameters is permitted, with
the following caveats:
@itemize @bullet
-@item
+@item
When a single thread group is passed, the output will typically
be the @samp{threads} result. Because threads may not contain
anything, the @samp{recurse} option will be ignored.
@@ -26293,7 +26293,7 @@ have the following fields:
Identifier of the thread group. This field is always present.
@item type
-The type of the thread group. At present, only @samp{process} is a
+The type of the thread group. At present, only @samp{process} is a
valid type.
@item pid
@@ -26306,7 +26306,8 @@ absent for an available thread group.
@item threads
This field has a list of tuples as value, each tuple describing a
-thread. It may be present
+thread. It may be present if the @samp{--recurse} option is
+specified, and it's actually possible to obtain the threads.
@item cores
This field is a list of integers, each identifying a core that one
@@ -31845,26 +31846,24 @@ The formal DTD for memory map format is given below:
@cindex thread list format
To efficiently update the list of threads and their attributes,
-@value{GDBN} gdb requests, using the @samp{qXfer:threads:read}
-package (@pxref{qXfer threads read}) and XML document with the
-format described below.
-
-The top-level structure of the document is shown below:
+@value{GDBN} issues the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}) and obtains the XML document with
+the following structure:
@smallexample
<?xml version="1.0"?>
<threads>
<thread id="id" core="0">
- some details
- </thread>
-</memory-map>
+ ... description ...
+ </thread>
+</threads>
@end smallexample
Each @samp{thread} element must have the @samp{id} attribute that
-identifies the thread (@pxref{thread-id syntax}). The
-@samp{core} attribute, if present, specifies which process core
+identifies the thread (@pxref{thread-id syntax}). The
+@samp{core} attribute, if present, specifies which processor core
the thread was last executing on. The content of the of @samp{thread}
-element is interpreted as hunam-readable auxilliary information.
+element is interpreted as human-readable auxilliary information.
@include agentexpr.texi
@@ -32437,9 +32436,9 @@ An example document is:
@end smallexample
The document has an @samp{osdata} element that contains a number of
-@samp{item} elements describing processes. Each process @samp{item}
+@samp{item} elements describing processes. Each process @samp{item}
may contain @samp{threads} element with items describing threads of
-that process. Both process and thread items contain a number of
+that process. Both process and thread items contain a number of
@samp{column} elements with various information.
Each process item should include a column whose name is @samp{pid}. The value
@@ -32451,7 +32450,7 @@ is running on.
Each thread item should include a column whose name is @samp{tid}.
The value of that is a thread identifier, specific to the operating
-system. The @samp{core} column, if present, is the integer number of
+system. The @samp{core} column, if present, is the integer number of
the core the thread is running on.
@include gpl.texi
diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd
index 67de837..a04506b 100644
--- a/gdb/features/osdata.dtd
+++ b/gdb/features/osdata.dtd
@@ -10,10 +10,7 @@
<!ATTLIST osdata version CDATA #FIXED "1.0">
<!ATTLIST osdata type CDATA #REQUIRED>
-<!ELEMENT item (column*, threads?)>
+<!ELEMENT item (column*)>
<!ELEMENT column (#PCDATA)>
<!ATTLIST column name CDATA #REQUIRED>
-
-<!ELEMENT threads (item*)>
-
diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
index e7c3a11..20308ee 100644
--- a/gdb/features/threads.dtd
+++ b/gdb/features/threads.dtd
@@ -11,4 +11,3 @@
<!ATTLIST thread id CDATA #REQUIRED>
<!ATTLIST thread core CDATA #IMPLIED>
-
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index d1d853d..f042338 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2798,10 +2798,27 @@ compare_ints (const void *xa, const void *xb)
return a - b;
}
-/* Given PID, print information about every thread in that process to BUFFER.
- Set *CORES to a string with a comma-separated list of cores that threads
- of PID execute on. The string must be freed by the caller. BUFFER must
- be initialized. If no cores are found, *CORES will be set to NULL. */
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
+/* Given PID, iterates over all threads in that process.
+
+ Information about each thread, in a format suitable for qXfer:osdata:thread
+ is printed to BUFFER, if it's not NULL. The buffer will not be either
+ initialized, or finished, or have '\0' written to it. Caller is responsible
+ for such things.
+
+ The list of cores that threads are running on is assigned to *CORES, if it
+ is not NULL. If not cores are found, *CORES will be set to NULL.
+*/
+
static void
list_threads (int pid, struct buffer *buffer, char **cores)
{
@@ -2820,78 +2837,121 @@ list_threads (int pid, struct buffer *buffer, char **cores)
if (!dir)
return;
- buffer_xml_printf (buffer, "<threads>");
while ((dp = readdir (dir)) != NULL)
{
unsigned long lwp = strtoul (dp->d_name, NULL, 10);
-
+
if (lwp != 0)
{
unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
if (core != -1)
{
- char s[11];
+ char s[sizeof ("4294967295") + 1];
sprintf (s, "%u", core);
- if (count == allocated)
- core_numbers = realloc (core_numbers,
- sizeof (int ) * (allocated *= 2));
+ if (count == allocated) {
+ allocated *= 2;
+ core_numbers = realloc (core_numbers,
+ sizeof (int ) * allocated);
+
+ }
core_numbers[count++] = core;
- buffer_xml_printf (buffer,
- "<item>"
- "<column name=\"tid\">%s</column>"
- "<column name=\"core\">%s</column>"
- "</item>", dp->d_name, s);
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "<column name=\"core\">%s</column>"
+ "</item>", pid, dp->d_name, s);
}
else
{
- buffer_xml_printf (buffer,
- "<item>"
- "<column name=\"tid\">%s</column>"
- "</item>", dp->d_name);
+ if (buffer)
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"tid\">%s</column>"
+ "</item>", pid, dp->d_name);
}
- }
+ }
}
- buffer_xml_printf (buffer, "</threads>");
- }
-
- *cores = NULL;
- if (count > 0)
- {
- struct buffer buffer2;
- int *b;
- int *e;
- qsort (core_numbers, count, sizeof (int), compare_ints);
-
- /* Remove duplicates. */
- b = core_numbers;
- e = core_numbers + count;
-
- while (b != e) {
- if ((b + 1 < e) && (*b == *(b+1)))
- {
- *(b + 1) = *(e - 1);
- --e;
- }
- else
- {
- ++b;
- }
- }
+ }
+
+ if (cores)
+ {
+ *cores = NULL;
+ if (count > 0)
+ {
+ struct buffer buffer2;
+ int *b;
+ int *e;
+ qsort (core_numbers, count, sizeof (int), compare_ints);
- buffer_init (&buffer2);
+ /* Remove duplicates. */
+ b = core_numbers;
+ e = unique (b, core_numbers + count);
- for (b = core_numbers; b != e; ++b) {
- char number[11];
- sprintf (number, "%u", *b);
- buffer_xml_printf (&buffer2, "%s%s",
- (b == core_numbers) ? "" : ",", number);
- }
- buffer_grow_str0 (&buffer2, "");
-
- *cores = buffer_finish (&buffer2);
- }
+ buffer_init (&buffer2);
+
+ for (b = core_numbers; b != e; ++b)
+ {
+ char number[sizeof ("4294967295") + 1];
+ sprintf (number, "%u", *b);
+ buffer_xml_printf (&buffer2, "%s%s",
+ (b == core_numbers) ? "" : ",", number);
+ }
+ buffer_grow_str0 (&buffer2, "");
+
+ *cores = buffer_finish (&buffer2);
+ }
+ }
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+ char pathname[128];
+ FILE *f;
+ char cmd[MAXPATHLEN + 1];
+
+ sprintf (pathname, "/proc/%d/cmdline", pid);
+
+ if ((f = fopen (pathname, "r")) != NULL)
+ {
+ size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+ if (len > 0)
+ {
+ char *cores = 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\">%d</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>",
+ pid,
+ username,
+ cmd);
+
+ /* This only collects core numbers, and does not print threads. */
+ list_threads (pid, NULL, &cores);
+
+ if (cores)
+ {
+ buffer_xml_printf (buffer,
+ "<column name=\"cores\">%s</column>", cores);
+ free (cores);
+ }
+
+ buffer_xml_printf (buffer, "</item>");
+ }
+ fclose (f);
+ }
}
static int
@@ -2904,10 +2964,16 @@ linux_qxfer_osdata (const char *annex,
static const char *buf;
static long len_avail = -1;
static struct buffer buffer;
+ int processes = 0;
+ int threads = 0;
DIR *dirp;
- if (strcmp (annex, "processes") != 0)
+ if (strcmp (annex, "processes") == 0)
+ processes = 1;
+ else if (strcmp (annex, "threads") == 0)
+ threads = 1;
+ else
return 0;
if (!readbuf || writebuf)
@@ -2920,7 +2986,10 @@ linux_qxfer_osdata (const char *annex,
len_avail = 0;
buf = NULL;
buffer_init (&buffer);
- buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ if (processes)
+ buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+ else if (threads)
+ buffer_grow_str (&buffer, "<osdata type=\"threads\">");
dirp = opendir ("/proc");
if (dirp)
@@ -2939,60 +3008,16 @@ linux_qxfer_osdata (const char *annex,
if (stat (procentry, &statbuf) == 0
&& S_ISDIR (statbuf.st_mode))
{
- char pathname[128];
- FILE *f;
- char cmd[MAXPATHLEN + 1];
- struct passwd *entry;
- int pid;
-
- sprintf (pathname, "/proc/%s/cmdline", dp->d_name);
- entry = getpwuid (statbuf.st_uid);
+ int pid = (int) strtoul (dp->d_name, NULL, 10);
- pid = (int)strtoul (dp->d_name, NULL, 10);
-
- if ((f = fopen (pathname, "r")) != NULL)
+ if (processes)
{
- size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
- if (len > 0)
- {
- struct buffer thread_buffer;
- char *cores = 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>",
- dp->d_name,
- entry ? entry->pw_name : "?",
- cmd);
-
- buffer_init (&thread_buffer);
- list_threads (pid, &thread_buffer, &cores);
-
- if (cores)
- {
- buffer_xml_printf (
- &buffer,
- "<column name=\"cores\">%s</column>", cores);
- free (cores);
- }
-
- buffer_grow (&buffer,
- thread_buffer.buffer,
- thread_buffer.used_size);
- free (buffer_finish (&thread_buffer));
-
-
- buffer_xml_printf (&buffer, "</item>");
- }
- fclose (f);
+ struct passwd *entry = getpwuid (statbuf.st_uid);
+ show_process (pid, entry ? entry->pw_name : "?", &buffer);
+ }
+ else if (threads)
+ {
+ list_threads (pid, &buffer, NULL);
}
}
}
@@ -3271,8 +3296,8 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
static int
linux_core_of_thread (ptid_t ptid)
{
- char filename[sizeof ("/proc//task//stat")
- + 2*20 /* decimal digits for 2 numbers, max 2^64 bit each */
+ char filename[sizeof ("/proc//task//stat")
+ + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
+ 1];
FILE *f;
char *content = NULL;
@@ -3281,10 +3306,12 @@ linux_core_of_thread (ptid_t ptid)
int content_read = 0;
int i;
int core;
-
- sprintf (filename, "/proc/%d/task/%ld/stat",
+
+ sprintf (filename, "/proc/%d/task/%ld/stat",
ptid_get_pid (ptid), ptid_get_lwp (ptid));
f = fopen (filename, "r");
+ if (!f)
+ return -1;
for (;;)
{
@@ -3296,19 +3323,19 @@ linux_core_of_thread (ptid_t ptid)
{
content[content_read] = '\0';
break;
- }
+ }
}
p = strchr (content, '(');
p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
-
+
p = strtok_r (p, " ", &ts);
for (i = 0; i != 36; ++i)
p = strtok_r (NULL, " ", &ts);
if (sscanf (p, "%d", &core) == 0)
core = -1;
-
+
free (content);
fclose (f);
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 97c076d..4e17ba1 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1616,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...)
prev = f + 1;
}
break;
+ case 'd':
+ {
+ int i = va_arg (ap, char *);
+ char b[sizeof ("4294967295") + 1];
+
+ buffer_grow (buffer, prev, f - prev - 1);
+ sprintf (b, "%d", i);
+ buffer_grow_str (buffer, b);
+ prev = f + 1;
+ }
}
percent = 0;
}
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 62e3e3d..56806e2 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -726,15 +726,15 @@ handle_threads_qxfer_proper (struct buffer *buffer)
if (the_target->core_for_thread)
core = (*the_target->core_for_thread) (ptid);
- if (core != -1)
+ if (core != -1)
{
sprintf (core_s, "%d", core);
- buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+ buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
ptid_s, core_s);
}
else
{
- buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+ buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
ptid_s);
}
}
@@ -757,19 +757,19 @@ handle_threads_qxfer (const char *annex,
{
struct buffer buffer;
/* When asked for data at offset 0, generate everything and store into
- 'result'. Successive reads will be served off 'result'. */
+ 'result'. Successive reads will be served off 'result'. */
if (result)
free (result);
-
+
buffer_init (&buffer);
handle_threads_qxfer_proper (&buffer);
-
+
result = buffer_finish (&buffer);
result_length = strlen (result);
- buffer_free (&buffer);
+ buffer_free (&buffer);
}
-
+
if (offset >= result_length)
{
/* We're out of data. */
@@ -778,10 +778,10 @@ handle_threads_qxfer (const char *annex,
result_length = 0;
return 0;
}
-
+
if (length > result_length - offset)
length = result_length - offset;
-
+
memcpy (readbuf, result + offset, length);
return length;
@@ -1227,7 +1227,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
*new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
free (data);
- return;
+ return;
}
/* Protocol features query. */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index b232caf..7c528f2 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -5440,6 +5440,9 @@ linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
back_to = make_cleanup (xfree, filename);
f = fopen (filename, "r");
+ if (!f)
+ return -1;
+
make_cleanup_fclose (f);
for (;;)
@@ -5461,7 +5464,7 @@ linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
/* If the first field after program name has index 0, then core number is
- the field with index 36. There's no constant for that anywhere. */
+ the field with index 36. There's no constant for that anywhere. */
p = strtok_r (p, " ", &ts);
for (i = 0; i != 36; ++i)
p = strtok_r (NULL, " ", &ts);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index b152d98..96bc39e 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -50,6 +50,7 @@
#include "valprint.h"
#include "inferior.h"
#include "osdata.h"
+#include "splay-tree.h"
#include <ctype.h>
#include <sys/time.h>
@@ -366,7 +367,8 @@ struct collect_cores_data
VEC (int) *cores;
};
-static int collect_cores (struct thread_info *ti, void *xdata)
+static int
+collect_cores (struct thread_info *ti, void *xdata)
{
struct collect_cores_data *data = xdata;
@@ -380,6 +382,16 @@ static int collect_cores (struct thread_info *ti, void *xdata)
return 0;
}
+static int *
+unique (int *b, int *e)
+{
+ int *d = b;
+ while (++b != e)
+ if (*d != *b)
+ *++d = *b;
+ return ++d;
+}
+
struct print_one_inferior_data
{
int recurse;
@@ -390,7 +402,7 @@ static int
print_one_inferior (struct inferior *inferior, void *xdata)
{
struct print_one_inferior_data *top_data = xdata;
-
+
if (VEC_length (int, top_data->inferiors) == 0
|| bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
VEC_length (int, top_data->inferiors), sizeof (int),
@@ -408,7 +420,7 @@ print_one_inferior (struct inferior *inferior, void *xdata)
data.cores = 0;
iterate_over_threads (collect_cores, &data);
- if (VEC_length (int, data.cores))
+ if (!VEC_empty (int, data.cores))
{
int elt;
int i;
@@ -422,28 +434,16 @@ print_one_inferior (struct inferior *inferior, void *xdata)
b = VEC_address (int, data.cores);
e = b + VEC_length (int, data.cores);
+ e = unique (b, e);
- /* Remove duplicates. */
- while (b != e) {
- if ((b + 1 < e) && (*b == *(b+1)))
- {
- *(b + 1) = *(e - 1);
- --e;
- }
- else
- {
- ++b;
- }
- }
-
- for (i = 0; VEC_iterate (int, data.cores, i, elt); ++i)
- ui_out_field_int (uiout, NULL, elt);
+ for (; b != e; ++b)
+ ui_out_field_int (uiout, NULL, *b);
do_cleanups (back_to_2);
}
if (top_data->recurse)
- print_thread_info (uiout, -1, inferior->pid);
+ print_thread_info (uiout, -1, inferior->pid);
do_cleanups (back_to);
}
@@ -451,25 +451,178 @@ print_one_inferior (struct inferior *inferior, void *xdata)
return 0;
}
-/* Output a field named 'cores' with a list as the value. The elements of
+/* Output a field named 'cores' with a list as the value. The elements of
the list are obtained by splitting 'cores' on comma. */
+
static void
output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
{
- struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+ struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
field_name);
char *cores = xstrdup (xcores);
char *p = cores;
- char *ts = 0;
make_cleanup (xfree, cores);
- for (p = strtok_r (p, ",", &ts); p; p = strtok_r (NULL, ",", &ts))
+ for (p = strtok (p, ","); p; p = strtok (NULL, ","))
ui_out_field_string (uiout, NULL, p);
-
+
do_cleanups (back_to);
}
+static void
+free_vector_of_ints (void *xvector)
+{
+ VEC (int) **vector = xvector;
+ VEC_free (int, *vector);
+}
+
+static void
+do_nothing (splay_tree_key k)
+{
+}
+
+static void
+free_vector_of_osdata_items (splay_tree_value xvalue)
+{
+ VEC (osdata_item_s) *value = (VEC (osdata_item_s) *) xvalue;
+ /* We don't free the items itself, it will be done separately. */
+ VEC_free (osdata_item_s, value);
+}
+
+static int
+splay_tree_int_comparator (splay_tree_key xa, splay_tree_key xb)
+{
+ int a = xa;
+ int b = xb;
+ return a - b;
+}
+
+static void
+free_splay_tree (void *xt)
+{
+ splay_tree t = xt;
+ splay_tree_delete (t);
+}
+
+static void
+list_available_thread_groups (VEC (int) *ids, int recurse)
+{
+ struct osdata *data;
+ struct osdata_item *item;
+ int ix_items;
+ /* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
+ The vector contains information about all threads for the given
+ pid. */
+ splay_tree tree;
+
+ /* get_osdata will throw if it cannot return data. */
+ data = get_osdata ("processes");
+ make_cleanup_osdata_free (data);
+
+ if (recurse)
+ {
+ struct osdata *threads = get_osdata ("threads");
+ make_cleanup_osdata_free (threads);
+
+ tree = splay_tree_new (splay_tree_int_comparator,
+ do_nothing,
+ free_vector_of_osdata_items);
+ make_cleanup (free_splay_tree, tree);
+
+ for (ix_items = 0;
+ VEC_iterate (osdata_item_s, threads->items,
+ ix_items, item);
+ ix_items++)
+ {
+ const char *pid = get_osdata_column (item, "pid");
+ int pid_i = strtoul (pid, NULL, 0);
+ VEC (osdata_item_s) *vec = 0;
+
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (!n)
+ {
+ VEC_safe_push (osdata_item_s, vec, item);
+ splay_tree_insert (tree, pid_i, (splay_tree_value)vec);
+ }
+ else
+ {
+ vec = (VEC (osdata_item_s) *) n->value;
+ VEC_safe_push (osdata_item_s, vec, item);
+ n->value = (splay_tree_value) vec;
+ }
+ }
+ }
+
+ 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;
+
+ const char *pid = get_osdata_column (item, "pid");
+ const char *cmd = get_osdata_column (item, "command");
+ const char *user = get_osdata_column (item, "user");
+ const char *cores = get_osdata_column (item, "cores");
+
+ int pid_i = strtoul (pid, NULL, 0);
+
+ /* At present, the target will return all available processes
+ and if information about specific ones was required, we filter
+ undesired processes here. */
+ if (ids && bsearch (&pid_i, VEC_address (int, ids),
+ VEC_length (int, ids),
+ sizeof (int), compare_positive_ints) == NULL)
+ continue;
+
+
+ back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ 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);
+ if (cores)
+ output_cores (uiout, "cores", cores);
+
+ if (recurse)
+ {
+ splay_tree_node n = splay_tree_lookup (tree, pid_i);
+ if (n)
+ {
+ VEC (osdata_item_s) *children = (VEC (osdata_item_s) *) n->value;
+ struct osdata_item *child;
+ int ix_child;
+
+ make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+ for (ix_child = 0;
+ VEC_iterate (osdata_item_s, children, ix_child, child);
+ ++ix_child)
+ {
+ struct cleanup *back_to_2 =
+ make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+ const char *tid = get_osdata_column (child, "tid");
+ const char *tcore = get_osdata_column (child, "core");
+ ui_out_field_string (uiout, "id", tid);
+ if (tcore)
+ ui_out_field_string (uiout, "core", tcore);
+
+ do_cleanups (back_to_2);
+ }
+ }
+ }
+
+ do_cleanups (back_to);
+ }
+}
+
void
mi_cmd_list_thread_groups (char *command, char **argv, int argc)
{
@@ -494,19 +647,22 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
while (1)
{
- int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+ int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
&optind, &optarg);
if (opt < 0)
break;
switch ((enum opt) opt)
{
- case AVAILABLE_OPT:
- available = 1;
+ case AVAILABLE_OPT:
+ available = 1;
break;
case RECURSE_OPT:
- if (strcmp (optarg, "1") != 0)
- error ("only '1' is permitted value for the '--recurse' option");
- recurse = 1;
+ if (strcmp (optarg, "0") == 0)
+ ;
+ else if (strcmp (optarg, "1") == 0)
+ recurse = 1;
+ else
+ error ("only '0' and '1' are valid values for the '--recurse' option");
break;
}
}
@@ -516,86 +672,18 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
char *end;
int inf = strtoul (argv[optind], &end, 0);
if (*end != '\0')
- error ("invalid group id '%s'", argv[optind]);
+ error ("invalid group id '%s'", argv[optind]);
VEC_safe_push (int, ids, inf);
}
- qsort (VEC_address (int, ids),
+ qsort (VEC_address (int, ids),
VEC_length (int, ids),
sizeof (int), compare_positive_ints);
- back_to = make_cleanup ((void (*)(void *))VEC_OP (int, free), &ids);
+ back_to = make_cleanup (free_vector_of_ints, &ids);
if (available)
{
- struct osdata *data;
- struct osdata_item *item;
- int ix_items;
-
- data = get_osdata ("processes");
- make_cleanup_osdata_free (data);
-
- 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;
-
- const char *pid = get_osdata_column (item, "pid");
- const char *cmd = get_osdata_column (item, "command");
- const char *user = get_osdata_column (item, "user");
- const char *cores = get_osdata_column (item, "cores");
-
- int pid_i = strtoul (pid, NULL, 0);
-
- /* At present, the target will return all available processes
- and if information about specific ones was required, we filter
- undesired processes here. */
- if (ids && bsearch (&pid_i, VEC_address (int, ids),
- VEC_length (int, ids),
- sizeof (int), compare_positive_ints) == NULL)
- continue;
-
-
- back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
-
- 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);
- if (cores)
- output_cores (uiout, "cores", cores);
-
- if (item->children && recurse)
- {
- struct osdata_item *child;
- int ix_child;
-
- make_cleanup_ui_out_list_begin_end (uiout, "threads");
-
- for (ix_child = 0; VEC_iterate (osdata_item_s,
- item->children->items,
- ix_child, child); ++ix_child)
- {
- struct cleanup *back_to_2 =
- make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
-
- const char *tid = get_osdata_column (child, "tid");
- const char *tcore = get_osdata_column (child, "core");
- ui_out_field_string (uiout, "id", tid);
- if (tcore)
- ui_out_field_string (uiout, "core", tcore);
-
- do_cleanups (back_to_2);
- }
- }
-
- do_cleanups (back_to);
- }
+ list_available_thread_groups (ids, recurse);
}
else if (VEC_length (int, ids) == 1)
{
@@ -603,7 +691,7 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
int pid = *VEC_address (int, ids);
if (!in_inferior_list (pid))
error ("Invalid thread group id '%d'", pid);
- print_thread_info (uiout, -1, pid);
+ print_thread_info (uiout, -1, pid);
}
else
{
@@ -611,15 +699,15 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
data.recurse = recurse;
data.inferiors = ids;
- /* Local thread groups. Either no explicit ids -- and we
- print everything, or several explicit ids. In both cases,
+ /* Local thread groups. Either no explicit ids -- and we
+ print everything, or several explicit ids. In both cases,
we print more than one group, and have to use 'groups'
as the top-level element. */
make_cleanup_ui_out_list_begin_end (uiout, "groups");
update_thread_list ();
iterate_over_inferiors (print_one_inferior, &data);
}
-
+
do_cleanups (back_to);
}
diff --git a/gdb/osdata.c b/gdb/osdata.c
index 394fc4d..3d2c23c 100644
--- a/gdb/osdata.c
+++ b/gdb/osdata.c
@@ -50,10 +50,8 @@ osdata_parse (const char *xml)
/* Internal parsing data passed to all XML callbacks. */
struct osdata_parsing_data
{
- struct osdata *top_osdata;
struct osdata *osdata;
char *property_name;
- struct osdata_item *current_item;
};
/* Handle the start of a <osdata> element. */
@@ -73,37 +71,9 @@ osdata_start_osdata (struct gdb_xml_parser *parser,
type = VEC_index (gdb_xml_value_s, attributes, 0)->value;
osdata = XZALLOC (struct osdata);
osdata->type = xstrdup (type);
- data->top_osdata = data->osdata = osdata;
-}
-
-/* Handle the start of a <threads> element. */
-
-static void
-osdata_start_threads (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;
-
- osdata = XZALLOC (struct osdata);
- osdata->type = xstrdup ("threads");
data->osdata = osdata;
}
-static void
-osdata_end_threads (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;
-
- data->current_item->children = data->osdata;
- data->osdata = data->top_osdata;
-}
-
-
/* Handle the start of a <item> element. */
static void
@@ -114,8 +84,6 @@ osdata_start_item (struct gdb_xml_parser *parser,
struct osdata_parsing_data *data = user_data;
struct osdata_item item = { NULL };
VEC_safe_push (osdata_item_s, data->osdata->items, &item);
- if (data->osdata == data->top_osdata)
- data->current_item = VEC_last (osdata_item_s, data->osdata->items);
}
/* Handle the start of a <column> element. */
@@ -169,15 +137,10 @@ const struct gdb_xml_attribute column_attributes[] = {
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
};
-extern const struct gdb_xml_element osdata_children[];
-
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 },
- { "threads", NULL, osdata_children,
- GDB_XML_EF_OPTIONAL, osdata_start_threads,
- osdata_end_threads },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
diff --git a/gdb/osdata.h b/gdb/osdata.h
index 485789e..4ada454 100644
--- a/gdb/osdata.h
+++ b/gdb/osdata.h
@@ -22,9 +22,6 @@
#include "vec.h"
-typedef struct osdata *osdata_p;
-DEF_VEC_P(osdata_p);
-
typedef struct osdata_column
{
char *name;
@@ -35,13 +32,6 @@ DEF_VEC_O(osdata_column_s);
typedef struct osdata_item
{
VEC(osdata_column_s) *columns;
-
- /* Nested items. Presently, a given item may only contain
- children of specific kind, and children->type gives
- the kind of children. In fact, only 'threads' may be
- the kind of children right now. */
- osdata_p children;
-
} osdata_item_s;
DEF_VEC_O(osdata_item_s);
@@ -51,7 +41,8 @@ struct osdata
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 *);
diff --git a/gdb/remote.c b/gdb/remote.c
index eb47713..e393e47 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -311,6 +311,12 @@ struct private_thread_info
int core;
};
+/* The core number that was last seed by process_stop_reply. */
+static int last_core = -1;
+
+/* The thread that corresponds to last_core. */
+static ptid_t thread_of_last_core;
+
/* Returns true if the multi-process extensions are in effect. */
static int
remote_multi_process_p (struct remote_state *rs)
@@ -2312,6 +2318,8 @@ remote_find_new_threads (void)
CRAZY_MAX_THREADS);
}
+#if defined(HAVE_LIBEXPAT)
+
typedef struct thread_item
{
ptid_t ptid;
@@ -2348,8 +2356,8 @@ start_thread (struct gdb_xml_parser *parser,
VEC_safe_push (thread_item_t, data->items, &item);
}
-static void
-end_thread (struct gdb_xml_parser *parser,
+static void
+end_thread (struct gdb_xml_parser *parser,
const struct gdb_xml_element *element,
void *user_data, const char *body_text)
{
@@ -2382,6 +2390,8 @@ const struct gdb_xml_element threads_elements[] = {
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
+#endif
+
/*
* Find all threads for info threads command.
* Uses new thread protocol contributed by Cisco.
@@ -2399,13 +2409,14 @@ remote_threads_info (struct target_ops *ops)
if (remote_desc == 0) /* paranoia */
error (_("Command can only be used when connected to the remote target."));
+#if defined(HAVE_LIBEXPAT)
if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
{
- char *xml = target_read_stralloc (¤t_target,
+ char *xml = target_read_stralloc (¤t_target,
TARGET_OBJECT_THREADS, NULL);
struct cleanup *back_to = make_cleanup (xfree, xml);
- if (xml && *xml)
+ if (xml && *xml)
{
struct gdb_xml_parser *parser;
struct threads_parsing_context context;
@@ -2417,7 +2428,7 @@ remote_threads_info (struct target_ops *ops)
&context);
gdb_xml_use_dtd (parser, "threads.dtd");
-
+
if (gdb_xml_parse (parser, xml) == 0)
{
int i;
@@ -2449,15 +2460,16 @@ remote_threads_info (struct target_ops *ops)
}
}
xfree (item->extra);
- }
+ }
}
-
- VEC_free (thread_item_t, context.items);
+
+ VEC_free (thread_item_t, context.items);
}
do_cleanups (back_to);
return;
}
+#endif
if (use_threadinfo_query)
{
@@ -4901,7 +4913,14 @@ process_stop_reply (struct stop_reply *stop_reply,
event in that thread. */
info = find_thread_ptid (ptid);
if (info && info->private)
- info->private->core = stop_reply->core;
+ {
+ info->private->core = stop_reply->core;
+ }
+ else
+ {
+ last_core = stop_reply->core;
+ thread_of_last_core = ptid;
+ }
remote_notice_new_inferior (ptid, 0);
}
@@ -9073,6 +9092,8 @@ remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
struct thread_info *info = find_thread_ptid (ptid);
if (info && info->private)
return info->private->core;
+ else if (ptid_equal (ptid, thread_of_last_core))
+ return last_core;
return -1;
}
diff --git a/gdb/target.h b/gdb/target.h
index 7435433..55e0e84 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -257,7 +257,7 @@ enum target_object
TARGET_OBJECT_SIGNAL_INFO,
/* The list of threads that are being debugged. */
TARGET_OBJECT_THREADS,
- /* Possible future objects: TARGET_OBJECT_FILE, ... */
+ /* Possible future objects: TARGET_OBJECT_FILE, ... */
};
/* Request that OPS transfer up to LEN 8-bit bytes of the target's
@@ -600,8 +600,8 @@ struct target_ops
ptid_t);
/* Return the core that thread PTID is on, or -1 if such information
- is not available. For a stopped thread, this is supposed to return
- the core the thread was last running on. For running threads, it
+ is not available. For a stopped thread, this is supposed to return
+ the core the thread was last running on. For a running thread, it
should return one of the cores that the thread was running between
the call to this function and return -- and if it was running on
several cores, any other may be returned. */
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [MI] core awareness
@ 2009-12-17 15:33 Pedro Alves
2009-12-21 14:48 ` Vladimir Prus
0 siblings, 1 reply; 17+ messages in thread
From: Pedro Alves @ 2009-12-17 15:33 UTC (permalink / raw)
To: gdb-patches; +Cc: Vladimir Prus
(resending in us-ascii)
On Wednesday 16 December 2009 20:52:38, Vladimir Prus wrote:
> The attached patch implements the core awareness, as discussed at:
>
> http://thread.gmane.org/gmane.comp.gdb.devel/27468/
>
> The most current spec can be found at:
>
> http://article.gmane.org/gmane.comp.gdb.devel/27548
>
> To summarize, with this patch, when debugging against gdbserver:
>
> * The -list-thread-groups command will report the cores that each
> process runs on (with or without --available)
> * It's possible to pass several thread groups to -list-thread-groups
> * The *stopped output includes core number
>
> For native debugging on Linux, roughly the same is also possible.
>
> This is mostly MI change, but includes some remote protocol tweaks,
> and documentation. I'd appreciate review of those parts.
More like 80% not-MI changes. :-)
>
> - Volodya
> core-awareness.diff
> commit 533abb7a66898cde158ea5556604ea20d56f2a47
> Author: Vladimir Prus <vladimir@codesourcery.com>
> Date: Wed Dec 16 23:39:19 2009 +0300
>
> Implement core awareness.
>
> gdb/
> * bcache.c (compare_ints): Remove
> (print_percentage): Use compare_positive_ints.
Fishy indentation.
>
> * defs.h (compare_positive_ints): Declare.
> * linux-nat.c (linux_nat_core_of_thread): New.
> (linux_nat_add_target): Register the above.
> * osdata.c (osdata_parsing_data): New fields
> top_osdata and current_item.
> (osdata_start_threads, osdata_end_threads): New.
> (osdata_start_osdata): Init top_osdata.
> (osdata_start_item): Record current item.
> (osdata_children): New.
> (item_children): Permit 'threads' child.
> * osdata.h (osdata_item): New field 'children'.
> * remote.c (struct private_thread_info): New.
> (PACKET_qXfer_threads, use_osdata_threads): New.
> (struct thread_item, threads_parsing_context
> (start_thread, end_thread, thread_attributes)
> (thread_children, threads_children, threads_elements): New.
> (remote_threads_info): Try qXfer:threads before anything
> else.
> (remote_protocol_packets): Register qXfer:threads.
> (remote_open_1): Init use_osdata_threads.
> (struct stop_reply): New field 'core'.
> (remote_parse_stop_reply): Parse core number.
> (process_stop_reply): Record core number.
> (remote_xfer_partial): Handle qXfer:threads.
> (remote_core_of_thread): New.
> (init_remote_ops): Register remote_core_of_thread.
> (_initialize_remote): Register qXfer:read.
> * target.c (target_core_of_thread): New
> * target.h (enum target_object): New value TARGET_OBJECT_THREADS.
> (struct target_ops): New field to_core_of_threads.
> (target_core_of_thread): Declare.
> * thread.c (print_thread_info): Report the core.
> * ui-out.c (MAX_UI_OUT_LEVELS): Increase.
> * utils.c (compare_positive_ints): New.
> * features/osdata.dtd: Allow nested threads.
> * features/threads.dtd: New.
> * mi/mi-interp.c (mi_on_normal_stop): Report the core.
> * mi/mi-main.c (struct collect_cores_data, collect_cores)
> (print_one_inferior_data): New.
> (print_one_inferior_data): Implementing printing of selected
> inferiors. Collect and print cores.
> (output_cores): New.
> (mi_cmd_list_thread_groups): Support --recurse. Permit specifying
> thread groups together with --available.
>
> gdbserver/
> * linux-low.c (linux_core_of_thread): New.
> (compare_ints, list_threads): New.
> (linux_qxfer_osdata): Report threads and cores.
> (linux_target_op): Register linux_core_of_thread.
> * remote-utils.c (prepare_resume_reply): Report the core.
> * server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
> New.
> (handle_query): Handle qXfer:threads. Announce availability
> thereof.
> * target.h (struct target_ops): New field core_for_threads.
>
> gdb/doc
> * gdb.texinfo (GDB/MI Thread Information): New.
> (GDB/MI Async Records): Document the core field in *stopped.
> (GDB/MI Miscellaneous Commands): Expand -list-thread-groups
> documentation
> (Process list): Document that osdata document may contain
> threads.
> (Remote Serial Protocol): Document qXfer:threads.
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 348d853..882be85 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -444,7 +444,8 @@ 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/osdata.dtd
> + $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
> + $(srcdir)/features/threads.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,
> diff --git a/gdb/bcache.c b/gdb/bcache.c
> index d56df07..e467c5b 100644
> --- a/gdb/bcache.c
> +++ b/gdb/bcache.c
> @@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
> \f
> /* Printing statistics. */
>
> -static int
> -compare_ints (const void *ap, const void *bp)
> -{
> - /* Because we know we're comparing two ints which are positive,
> - there's no danger of overflow here. */
> - return * (int *) ap - * (int *) bp;
> -}
> -
> -
> static void
> print_percentage (int portion, int total)
> {
> @@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
>
> /* To compute the median, we need the set of chain lengths sorted. */
> qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
> - compare_ints);
> + compare_positive_ints);
> qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
> - compare_ints);
> + compare_positive_ints);
>
> if (c->num_buckets > 0)
> {
> diff --git a/gdb/defs.h b/gdb/defs.h
> index b944ffb..396e9a8 100644
> --- a/gdb/defs.h
> +++ b/gdb/defs.h
> @@ -417,6 +417,8 @@ char *ldirname (const char *filename);
>
> char **gdb_buildargv (const char *);
>
> +int compare_positive_ints (const void *ap, const void *bp);
> +
> /* From demangle.c */
>
> extern void set_demangling_style (char *);
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index e880838..d1d5075 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -15402,6 +15402,10 @@ are:
> @tab @code{qXfer:siginfo:write}
> @tab @code{set $_siginfo}
>
> +@item @code{threads}
> +@tab @code{qXfer:threads:read}
> +@tab @code{info threads}
> +
> @item @code{get-thread-local-@*storage-address}
> @tab @code{qGetTLSAddr}
> @tab Displaying @code{__thread} variables
> @@ -21688,6 +21692,7 @@ follow development on @email{gdb@@sourceware.org} and
> * GDB/MI Stream Records::
> * GDB/MI Async Records::
> * GDB/MI Frame Information::
> +* GDB/MI Thread Information::
> @end menu
>
> @node GDB/MI Result Records
> @@ -21780,7 +21785,7 @@ several times, either for different threads, because it cannot resume
> all threads together, or even for a single thread, if the thread must
> be stepped though some code before letting it run freely.
>
> -@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
> +@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
> The target has stopped. The @var{reason} field can have one of the
> following values:
>
> @@ -21820,7 +21825,9 @@ If all threads are stopped, the @var{stopped} field will have the
> value of @code{"all"}. Otherwise, the value of the @var{stopped}
> field will be a list of thread identifiers. Presently, this list will
> always include a single thread, but frontend should be prepared to see
> -several threads in the list.
> +several threads in the list. The @var{core} field reports on which
> +processor core the stop event has happened. This field may be absent
> +if such information is not available.
>
> @item =thread-group-created,id="@var{id}"
> @itemx =thread-group-exited,id="@var{id}"
> @@ -21897,6 +21904,34 @@ corresponds to the frame's code address. This field may be absent.
>
> @end table
>
> +@node GDB/MI Thread Information
> +@subsection @sc{gdb/mi} Thread Information
> +
> +Whenver @value{GDBN} has to report an information about a thread, it
^ typo: Whenever
> +uses a tuple with the following fields:
> +
> +@table @code
> +@item id
> +The numeric id assigned to the thread by @value{GDBN}. This field is
> +always present.
> +
> +@item target-id
> +Target-specific string identifying the thread. This field is always present.
> +
> +@item details
> +Additional information about the thread provided by the target.
> +It is supposed to be human-readable and not interpreted by the
> +frontend. This field is optional.
> +
> +@item state
> +Either @samp{stopped} or @samp{running}, depending on whether the
> +thread is presently running. This field is always present.
> +
> +@item cores
Typo: "core"?
> +The value of this field is an integer number of the processor core the
> +thread was last seen on. This field is optional.
> +@end table
> +
>
> @c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
> @node GDB/MI Simple Examples
> @@ -26202,20 +26237,83 @@ while the target is running.
> @subheading Synopsis
>
> @smallexample
> --list-thread-groups [ --available ] [ @var{group} ]
> +-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
> @end smallexample
>
> -When used without the @var{group} parameter, lists top-level thread
> -groups that are being debugged. When used with the @var{group}
> -parameter, the children of the specified group are listed. The
> -children can be either threads, or other groups. At present,
> -@value{GDBN} will not report both threads and groups as children at
> -the same time, but it may change in future.
> +Lists thread groups (@pxref{Thread groups}). When a single thread
Double space after period. Here and elsewhere.
> +group is passed as the argument, lists the children specified one.
> +When several thread group are passed, lists information about those
> +thread groups. Without any parameters, lists information about all
> +top-level thread groups.
> +
> +Normally, thread groups that are being debugged are reported.
> +With the @samp{--available} option, @value{GDBN} reports thread groups
> +available on the target.
> +
> +The output of this command may have either a @samp{threads} result or
> +a @samp{groups} result. The @samp{thread} result has a list of tuples
> +as value, with each tuple describing a thread (@pxref{GDB/MI Thread
> +Information}). The @samp{groups} result has a list of tuples as value,
> +each tuple describing a thread group. If top-level groups are
> +requested (that is, no parameter is passed), or when several groups
> +are passed, the output always have a @samp{groups} result. The format
> +of the @samp{group} result is described below.
> +
> +To reduce the number of roundtrips it's possible to list thread groups
> +together with their children, by passing the @samp{--recurse} option
> +and the recursion depth. Presently, only recursion depth of 1 is
> +permitted. If this option is present, then every reported thread group
> +will also include its children, either as @samp{group} or
> +@samp{threads} field.
> +
> +In general, any combination of option and parameters is permitted, with
> +the following caveats:
> +
> +@itemize @bullet
> +@item
> +When a single thread group is passed, the output will typically
> +be the @samp{threads} result. Because threads may not contain
> +anything, the @samp{recurse} option will be ignored.
> +
> +@item
> +When the @samp{--available} option is passed, limited information may
> +be available. In particular, the list of threads of a process might
> +be inaccessible. Further, specifying specific thread groups might
> +not give any performance advantage over listing all thread groups.
> +The frontend should assume that @samp{-list-thread-groups --available}
> +is always an expensive operation and cache the results.
> +
> +@end itemize
> +
> +The @samp{groups} result is a list of tuples, where each tuple may
> +have the following fields:
> +
> +@table @code
> +@item id
> +Identifier of the thread group. This field is always present.
> +
> +@item type
> +The type of the thread group. At present, only @samp{process} is a
> +valid type.
> +
> +@item pid
> +The target-specific process identifier. This field is only present
> +for thread groups of type @samp{process}.
> +
> +@item num_children
> +The number of children this thread group has. This field may be
> +absent for an available thread group.
> +
> +@item threads
> +This field has a list of tuples as value, each tuple describing a
> +thread. It may be present
Looks like an unfinished sentence.
>
> -With the @samp{--available} option, instead of reporting groups that
> -are been debugged, GDB will report all thread groups available on the
> -target. Using the @samp{--available} option together with @var{group}
> -is not allowed.
> +@item cores
> +This field is a list of integers, each identifying a core that one
> +thread of the group is running on. This field may be absent if
> +such information is not available.
> +
> +@end table
>
> @subheading Example
>
> @@ -26229,6 +26327,16 @@ is not allowed.
> @{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
> frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
> file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
> +-list-thread-groups --available
> +^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
> +-list-thread-groups --available --recurse 1
> + ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
> + threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
> + @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
> +-list-thread-groups --available --recurse 1 17 18
> +^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
> + threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
> + @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
> @end smallexample
>
> @subheading The @code{-interpreter-exec} Command
> @@ -27978,6 +28086,7 @@ Show the current setting of the target wait timeout.
> * File-I/O Remote Protocol Extension::
> * Library List Format::
> * Memory Map Format::
> +* Thread List Format::
> @end menu
>
> @node Overview
> @@ -28878,6 +28987,10 @@ If @var{n} is @samp{thread}, then @var{r} is the @var{thread-id} of
> the stopped thread, as specified in @ref{thread-id syntax}.
>
> @item
> +If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
> +the core on which the stop event was detected.
> +
> +@item
> If @var{n} is a recognized @dfn{stop reason}, it describes a more
> specific event that stopped the target. The currently defined stop
> reasons are listed below. @var{aa} should be @samp{05}, the trap
> @@ -28911,8 +29024,6 @@ logged execution events, because it has reached the end (or the
> beginning when executing backward) of the log. The value of @var{r}
> will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
> for more information.
> -
> -
> @end table
>
> @item W @var{AA}
> @@ -29452,6 +29563,12 @@ These are the currently defined stub features and their properties:
> @tab @samp{-}
> @tab Yes
>
> +@item @samp{qXfer:threads:read}
> +@tab No
> +@tab @samp{-}
> +@tab Yes
> +
> +
> @item @samp{QNonStop}
> @tab No
> @tab @samp{-}
> @@ -29535,6 +29652,10 @@ The remote stub understands the @samp{qXfer:siginfo:read} packet
> The remote stub understands the @samp{qXfer:siginfo:write} packet
> (@pxref{qXfer siginfo write}).
>
> +@item qXfer:threads:read
> +The remote stub understands the @samp{qXfer:threads:read} packet
> +(@pxref{qXfer threads read}).
> +
> @item QNonStop
> The remote stub understands the @samp{QNonStop} packet
> (@pxref{QNonStop}).
> @@ -29727,6 +29848,15 @@ This packet is not probed by default; the remote stub must request it,
> by supplying an appropriate @samp{qSupported} response
> (@pxref{qSupported}).
>
> +@item qXfer:threads:read::@var{offset},@var{length}
> +@anchor{qXfer threads read}
> +Access the list of threads on target. @xref{Thread List Format}. The
> +annex part of the generic @samp{qXfer} packet must be empty
> +(@pxref{qXfer read}).
> +
> +This packet is not probed by default; the remote stub must request it,
What's the advantage of this, rather than probing it? I would
see it nice for GDB to know in advance that the target can
report core data, but that's not that reporting support
for this packet means, since the "core" field is optional.
> +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}.
> @@ -31710,6 +31840,32 @@ The formal DTD for memory map format is given below:
> <!ATTLIST property name CDATA #REQUIRED>
> @end smallexample
>
> +@node Thread List Format
> +@section Thread List Format
> +@cindex thread list format
> +
> +To efficiently update the list of threads and their attributes,
> +@value{GDBN} gdb requests, using the @samp{qXfer:threads:read}
> +package (@pxref{qXfer threads read}) and XML document with the
> +format described below.
> +
> +The top-level structure of the document is shown below:
> +
> +@smallexample
> +<?xml version="1.0"?>
> +<threads>
> + <thread id="id" core="0">
> + some details
> + </thread>
> +</memory-map>
s/memory-map/threads
"some details" ?
> +@end smallexample
> +
> +Each @samp{thread} element must have the @samp{id} attribute that
> +identifies the thread (@pxref{thread-id syntax}). The
> +@samp{core} attribute, if present, specifies which process core
s/process core/processor core/ ?
> +the thread was last executing on. The content of the of @samp{thread}
> +element is interpreted as hunam-readable auxilliary information.
typo: hunam
> +
> @include agentexpr.texi
>
> @node Target Descriptions
> @@ -32269,15 +32425,34 @@ An example document is:
> <column name="pid">1</column>
> <column name="user">root</column>
> <column name="command">/sbin/init</column>
> + <column name="cores">1,2,3</column>
> + <threads>
> + <item>
> + <column name="tid">12</column>
> + <column name="core">3</column>
> + </item>
> + </threads>
> </item>
> </osdata>
> @end smallexample
>
> -Each item should include a column whose name is @samp{pid}. The value
> +The document has an @samp{osdata} element that contains a number of
> +@samp{item} elements describing processes. Each process @samp{item}
> +may contain @samp{threads} element with items describing threads of
> +that process. Both process and thread items contain a number of
> +@samp{column} elements with various information.
> +
> +Each process 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.
> +displayed by @value{GDBN}. The @samp{cores} column, if present,
> +should contain a comma-separated list of cores that this process
> +is running on.
> +Each thread item should include a column whose name is @samp{tid}.
> +The value of that is a thread identifier, specific to the operating
> +system.
> The @samp{core} column, if present, is the integer number of
> +the core the thread is running on.
>
> @include gpl.texi
>
> diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd
> index a04506b..67de837 100644
> --- a/gdb/features/osdata.dtd
> +++ b/gdb/features/osdata.dtd
> @@ -10,7 +10,10 @@
> <!ATTLIST osdata version CDATA #FIXED "1.0">
> <!ATTLIST osdata type CDATA #REQUIRED>
>
> -<!ELEMENT item (column*)>
> +<!ELEMENT item (column*, threads?)>
>
> <!ELEMENT column (#PCDATA)>
> <!ATTLIST column name CDATA #REQUIRED>
> +
> +<!ELEMENT threads (item*)>
> +
This dtd change isn't right. "osdata" isn't specific to
processes, it is just a generic table. A "threads" element
doesn't fit in, it wouldn't make sense for anything other
than listing processes. Two alternative solutions:
- Allow multiple sub-table elements in the osdata schema, and
allow a "name" attribute on this element. In the listing
processes case, you'd have a sub-table whose type/name
attribute would be "threads". You should change
info_osdata_command to descend into subtables somehow.
- Don't nest "threads" within processes osdata. Treat it as
a different request / table (e.g., <osdata type="threads">).
That is, on the CLI, "info os processes" works as is, unmodified, and,
you'd add support for "info os threads". No schema changes
are necessary with this option. At the MI implementation level, you'd
do two requests to get the same data: get_osdata("processes"), and
get_osdata("threads").
> diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
> new file mode 100644
> index 0000000..e7c3a11
> --- /dev/null
> +++ b/gdb/features/threads.dtd
> @@ -0,0 +1,14 @@
> +<!-- Copyright (C) 2008, 2009 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. -->
> +
> +<!ELEMENT threads (thread*)>
> +<!ATTLIST threads version CDATA #FIXED "1.0">
> +
> +<!ELEMENT thread (#PCDATA)>
> +
> +<!ATTLIST thread id CDATA #REQUIRED>
> +<!ATTLIST thread core CDATA #IMPLIED>
> +
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index 45dd9f7..d1d853d 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -137,6 +137,7 @@ static void *add_lwp (ptid_t ptid);
> static int my_waitpid (int pid, int *status, int flags);
> static int linux_stopped_by_watchpoint (void);
> static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
> +static int linux_core_of_thread (ptid_t ptid);
>
> struct pending_signals
> {
> @@ -2789,6 +2790,111 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
> #endif
>
> static int
> +compare_ints (const void *xa, const void *xb)
> +{
> + int a = *(const int *)xa;
> + int b = *(const int *)xb;
> +
> + return a - b;
> +}
> +
> +/* Given PID, print information about every thread in that process to BUFFER.
> + Set *CORES to a string with a comma-separated list of cores that threads
> + of PID execute on. The string must be freed by the caller. BUFFER must
> + be initialized. If no cores are found, *CORES will be set to NULL. */
> +static void
> +list_threads (int pid, struct buffer *buffer, char **cores)
> +{
> + int count = 0;
> + int allocated = 10;
> + int *core_numbers = xmalloc (sizeof (int) * allocated);
> + char pathname[128];
> + DIR *dir;
> + struct dirent *dp;
> + struct stat statbuf;
> +
> + sprintf (pathname, "/proc/%d/task", pid);
> + if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
> + {
> + dir = opendir (pathname);
> + if (!dir)
> + return;
> +
> + buffer_xml_printf (buffer, "<threads>");
> + while ((dp = readdir (dir)) != NULL)
> + {
> + unsigned long lwp = strtoul (dp->d_name, NULL, 10);
> +
> + if (lwp != 0)
> + {
> + unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
> +
> + if (core != -1)
> + {
> + char s[11];
> + sprintf (s, "%u", core);
> +
> + if (count == allocated)
> + core_numbers = realloc (core_numbers,
> + sizeof (int ) * (allocated *= 2));
No space after int. Please move the 'allocated *= 2' in its own
statement. My GM hat tells me that I should point out that there are a
bunch of hardcoded buffer sizes in the patch, that the GNU conventions
tells us we should avoid.
> + core_numbers[count++] = core;
> + buffer_xml_printf (buffer,
> + "<item>"
> + "<column name=\"tid\">%s</column>"
> + "<column name=\"core\">%s</column>"
> + "</item>", dp->d_name, s);
> + }
> + else
> + {
> + buffer_xml_printf (buffer,
> + "<item>"
> + "<column name=\"tid\">%s</column>"
> + "</item>", dp->d_name);
> + }
> + }
> + }
> + buffer_xml_printf (buffer, "</threads>");
> + }
> +
> + *cores = NULL;
> + if (count > 0)
> + {
> + struct buffer buffer2;
> + int *b;
> + int *e;
> + qsort (core_numbers, count, sizeof (int), compare_ints);
> +
> + /* Remove duplicates. */
> + b = core_numbers;
> + e = core_numbers + count;
> +
> + while (b != e) {
> + if ((b + 1 < e) && (*b == *(b+1)))
> + {
> + *(b + 1) = *(e - 1);
> + --e;
> + }
> + else
> + {
> + ++b;
> + }
> + }
> +
> + buffer_init (&buffer2);
> +
> + for (b = core_numbers; b != e; ++b) {
'{' goes on its own line.
> + char number[11];
> + sprintf (number, "%u", *b);
> + buffer_xml_printf (&buffer2, "%s%s",
> + (b == core_numbers) ? "" : ",", number);
> + }
> + buffer_grow_str0 (&buffer2, "");
There's a bug here somewhere in the duplicates detection that
we talked about off-list, that I know you've fixed
already. :-)
> +
> + *cores = buffer_finish (&buffer2);
> + }
> +}
> +
> +static int
> linux_qxfer_osdata (const char *annex,
> unsigned char *readbuf, unsigned const char *writebuf,
> CORE_ADDR offset, int len)
> @@ -2837,15 +2943,20 @@ linux_qxfer_osdata (const char *annex,
> FILE *f;
> char cmd[MAXPATHLEN + 1];
> struct passwd *entry;
> + int pid;
>
> sprintf (pathname, "/proc/%s/cmdline", dp->d_name);
> entry = getpwuid (statbuf.st_uid);
>
> + pid = (int)strtoul (dp->d_name, NULL, 10);
Space after (int).
> +
> if ((f = fopen (pathname, "r")) != NULL)
> {
> size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
> if (len > 0)
> {
> + struct buffer thread_buffer;
> + char *cores = 0;
> int i;
> for (i = 0; i < len; i++)
> if (cmd[i] == '\0')
> @@ -2857,11 +2968,29 @@ linux_qxfer_osdata (const char *annex,
> "<item>"
> "<column name=\"pid\">%s</column>"
> "<column name=\"user\">%s</column>"
> - "<column name=\"command\">%s</column>"
> - "</item>",
> + "<column name=\"command\">%s</column>",
> dp->d_name,
> entry ? entry->pw_name : "?",
> cmd);
> +
> + buffer_init (&thread_buffer);
> + list_threads (pid, &thread_buffer, &cores);
> +
> + if (cores)
> + {
> + buffer_xml_printf (
> + &buffer,
> + "<column name=\"cores\">%s</column>", cores);
> + free (cores);
> + }
> +
> + buffer_grow (&buffer,
> + thread_buffer.buffer,
> + thread_buffer.used_size);
> + free (buffer_finish (&thread_buffer));
> +
> +
> + buffer_xml_printf (&buffer, "</item>");
Any chance linux-nat.c also gets support for this (however it
ends up looking like)?
> }
> fclose (f);
> }
> @@ -3139,6 +3268,53 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
> return ret;
> }
>
> +static int
> +linux_core_of_thread (ptid_t ptid)
> +{
> + char filename[sizeof ("/proc//task//stat")
s,//,/
> + + 2*20 /* decimal digits for 2 numbers, max 2^64 bit each */
Spaces around '*'.
> + + 1];
> + FILE *f;
> + char *content = NULL;
> + char *p;
> + char *ts = 0;
> + int content_read = 0;
> + int i;
> + int core;
> +
> + sprintf (filename, "/proc/%d/task/%ld/stat",
> + ptid_get_pid (ptid), ptid_get_lwp (ptid));
> + f = fopen (filename, "r");
> +
Should check if file opened successfully. Older
kernels don't have /proc/<pid>/task available --- see "man proc".
> + for (;;)
> + {
> + int n;
> + content = realloc (content, content_read + 1024);
> + n = fread (content + content_read, 1, 1024, f);
> + content_read += n;
> + if (n < 1024)
> + {
> + content[content_read] = '\0';
> + break;
> + }
> + }
> +
> + p = strchr (content, '(');
> + p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
> +
> + p = strtok_r (p, " ", &ts);
> + for (i = 0; i != 36; ++i)
> + p = strtok_r (NULL, " ", &ts);
> +
> + if (sscanf (p, "%d", &core) == 0)
> + core = -1;
> +
> + free (content);
> + fclose (f);
> +
> + return core;
> +}
> +
> static struct target_ops linux_target_ops = {
> linux_create_inferior,
> linux_attach,
> @@ -3178,10 +3354,11 @@ static struct target_ops linux_target_ops = {
> linux_start_non_stop,
> linux_supports_multi_process,
> #ifdef USE_THREAD_DB
> - thread_db_handle_monitor_command
> + thread_db_handle_monitor_command,
> #else
> - NULL
> + NULL,
> #endif
> + linux_core_of_thread
> };
>
> static void
> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
> index 158b653..97c076d 100644
> --- a/gdb/gdbserver/remote-utils.c
> +++ b/gdb/gdbserver/remote-utils.c
> @@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
> gdbserver to know what inferior_ptid is. */
> if (1 || !ptid_equal (general_thread, ptid))
> {
> + int core = -1;
> /* In non-stop, don't change the general thread behind
> GDB's back. */
> if (!non_stop)
> @@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
> buf = write_ptid (buf, ptid);
> strcat (buf, ";");
> buf += strlen (buf);
> +
> + if (the_target->core_for_thread)
> + core = (*the_target->core_for_thread) (ptid);
> + if (core != -1)
> + {
> + sprintf (buf, "core:");
> + buf += strlen (buf);
> + sprintf (buf, "%x", core);
> + strcat (buf, ";");
> + buf += strlen (buf);
> + }
> }
> }
>
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index 14bc7e7..62e3e3d 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
> }
> }
>
> +static void
> +handle_threads_qxfer_proper (struct buffer *buffer)
> +{
> + struct inferior_list_entry *thread;
> +
> + buffer_grow_str (buffer, "<threads>\n");
> +
> + for (thread = all_threads.head; thread; thread = thread->next)
> + {
> + ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
> + char ptid_s[100];
> + int core = -1;
> + char core_s[21];
> +
> + write_ptid (ptid_s, ptid);
> +
> + if (the_target->core_for_thread)
> + core = (*the_target->core_for_thread) (ptid);
> +
> + if (core != -1)
> + {
> + sprintf (core_s, "%d", core);
> + buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
> + ptid_s, core_s);
> + }
> + else
> + {
> + buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
> + ptid_s);
> + }
> + }
> +
> + buffer_grow_str0 (buffer, "</threads>\n");
> +}
> +
> +static int
> +handle_threads_qxfer (const char *annex,
> + unsigned char *readbuf,
> + CORE_ADDR offset, int length)
> +{
> + static char *result = 0;
> + static unsigned int result_length = 0;
> +
> + if (annex && strcmp (annex, "") != 0)
> + return 0;
> +
> + if (offset == 0)
> + {
> + struct buffer buffer;
> + /* When asked for data at offset 0, generate everything and store into
> + 'result'. Successive reads will be served off 'result'. */
> + if (result)
> + free (result);
> +
> + buffer_init (&buffer);
> +
> + handle_threads_qxfer_proper (&buffer);
> +
> + result = buffer_finish (&buffer);
> + result_length = strlen (result);
> + buffer_free (&buffer);
> + }
> +
> + if (offset >= result_length)
> + {
> + /* We're out of data. */
> + free (result);
> + result = NULL;
> + result_length = 0;
> + return 0;
> + }
> +
> + if (length > result_length - offset)
> + length = result_length - offset;
> +
> + memcpy (readbuf, result + offset, length);
> +
> + return length;
> +
> +}
> +
> /* Handle all of the extended 'q' packets. */
> void
> handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> @@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> return;
> }
>
> + if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
> + {
> + unsigned char *data;
> + int n;
> + CORE_ADDR ofs;
> + unsigned int len;
> + char *annex;
> +
> + require_running (own_buf);
> +
> + /* Reject any annex; grab the offset and length. */
> + if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
> + || annex[0] != '\0')
> + {
> + strcpy (own_buf, "E00");
> + return;
> + }
> +
> + /* Read one extra byte, as an indicator of whether there is
> + more. */
> + if (len > PBUFSIZ - 2)
> + len = PBUFSIZ - 2;
> + data = malloc (len + 1);
> + if (!data)
> + return;
> + n = handle_threads_qxfer (annex, data, ofs, len + 1);
> + if (n < 0)
> + write_enn (own_buf);
> + else if (n > len)
> + *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
> + else
> + *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
> +
> + free (data);
> + return;
> + }
> +
> /* Protocol features query. */
> if (strncmp ("qSupported", own_buf, 10) == 0
> && (own_buf[10] == ':' || own_buf[10] == '\0'))
> @@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> if (target_supports_non_stop ())
> strcat (own_buf, ";QNonStop+");
>
> + strcat (own_buf, ";qXfer:threads:read+");
> +
> return;
> }
>
> diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
> index 0e4b127..8e8778a 100644
> --- a/gdb/gdbserver/target.h
> +++ b/gdb/gdbserver/target.h
> @@ -283,6 +283,9 @@ struct target_ops
> /* If not NULL, target-specific routine to process monitor command.
> Returns 1 if handled, or 0 to perform default processing. */
> int (*handle_monitor_command) (char *);
> +
> + /* Returns the core given a thread, or -1 if not known. */
> + int (*core_for_thread) (ptid_t);
> };
>
> extern struct target_ops *the_target;
> diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
> index 2f388fb..bd0044b 100644
> --- a/gdb/gdbthread.h
> +++ b/gdb/gdbthread.h
> @@ -346,4 +346,6 @@ extern struct cleanup *make_cleanup_restore_current_thread (void);
> INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
> extern struct thread_info* inferior_thread (void);
>
> +extern void update_thread_list (void);
> +
> #endif /* GDBTHREAD_H */
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index c0afecd..b232caf 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -5421,6 +5421,59 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
> return inf->aspace;
> }
>
> +/* Return the core for a thread. */
> +static int
> +linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
> +{
> + struct cleanup *back_to;
> + char *filename;
> + FILE *f;
> + char *content = NULL;
> + char *p;
> + char *ts = 0;
> + int content_read = 0;
> + int i;
> + int core;
> +
> + filename = xstrprintf ("/proc/%d/task/%ld/stat",
> + GET_PID (ptid), GET_LWP (ptid));
> + back_to = make_cleanup (xfree, filename);
> +
> + f = fopen (filename, "r");
> + make_cleanup_fclose (f);
Should check if file opened successfully. Older
kernels don't have /proc/<pid>/task available --- see "man proc".
> +
> + for (;;)
> + {
> + int n;
> + content = xrealloc (content, content_read + 1024);
> + n = fread (content + content_read, 1, 1024, f);
> + content_read += n;
> + if (n < 1024)
> + {
> + content[content_read] = '\0';
> + break;
> + }
> + }
> +
> + make_cleanup (xfree, content);
> +
> + p = strchr (content, '(');
> + p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
> +
> + /* If the first field after program name has index 0, then core number is
> + the field with index 36. There's no constant for that anywhere. */
> + p = strtok_r (p, " ", &ts);
> + for (i = 0; i != 36; ++i)
> + p = strtok_r (NULL, " ", &ts);
> +
> + if (sscanf (p, "%d", &core) == 0)
> + core = -1;
> +
> + do_cleanups (back_to);
> +
> + return core;
> +}
> +
> void
> linux_nat_add_target (struct target_ops *t)
> {
> @@ -5461,6 +5514,8 @@ linux_nat_add_target (struct target_ops *t)
>
> t->to_supports_multi_process = linux_nat_supports_multi_process;
>
> + t->to_core_of_thread = linux_nat_core_of_thread;
> +
> /* We don't change the stratum; this target will sit at
> process_stratum and thread_db will set at thread_stratum. This
> is a little strange, since this is a multi-threaded-capable
> diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
> index 248cd66..af51bde 100644
> --- a/gdb/mi/mi-interp.c
> +++ b/gdb/mi/mi-interp.c
> @@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
>
> if (print_frame)
> {
> + int core;
> if (uiout != mi_uiout)
> {
> /* The normal_stop function has printed frame information into
> @@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
> }
> else
> ui_out_field_string (mi_uiout, "stopped-threads", "all");
> +
> + core = target_core_of_thread (inferior_ptid);
> + if (core != -1)
> + ui_out_field_int (mi_uiout, "core", core);
> }
>
> fputs_unfiltered ("*stopped", raw_stdout);
> diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
> index 2332752..b152d98 100644
> --- a/gdb/mi/mi-main.c
> +++ b/gdb/mi/mi-main.c
> @@ -359,11 +359,44 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
> print_thread_info (uiout, thread, -1);
> }
>
> +struct collect_cores_data
> +{
> + int pid;
> +
> + VEC (int) *cores;
> +};
> +
> +static int collect_cores (struct thread_info *ti, void *xdata)
Line break before function name, please.
> +{
> + struct collect_cores_data *data = xdata;
> +
> + if (ptid_get_pid (ti->ptid) == data->pid)
> + {
> + int core = target_core_of_thread (ti->ptid);
> + if (core != -1)
> + VEC_safe_push (int, data->cores, core);
> + }
> +
> + return 0;
> +}
> +
> +struct print_one_inferior_data
> +{
> + int recurse;
> + VEC (int) *inferiors;
> +};
> +
> static int
> -print_one_inferior (struct inferior *inferior, void *arg)
> +print_one_inferior (struct inferior *inferior, void *xdata)
> {
> - if (inferior->pid != 0)
> + struct print_one_inferior_data *top_data = xdata;
> +
> + if (VEC_length (int, top_data->inferiors) == 0
> + || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
> + VEC_length (int, top_data->inferiors), sizeof (int),
> + compare_positive_ints))
> {
> + struct collect_cores_data data;
> struct cleanup *back_to
> = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
>
> @@ -371,36 +404,128 @@ print_one_inferior (struct inferior *inferior, void *arg)
> ui_out_field_string (uiout, "type", "process");
> ui_out_field_int (uiout, "pid", inferior->pid);
>
> + data.pid = inferior->pid;
> + data.cores = 0;
> + iterate_over_threads (collect_cores, &data);
> +
> + if (VEC_length (int, data.cores))
Use instead:
if (!VEC_empty (int, data.cores))
> + {
> + int elt;
> + int i;
> + int *b, *e;
> + struct cleanup *back_to_2 =
> + make_cleanup_ui_out_list_begin_end (uiout, "cores");
> +
> + qsort (VEC_address (int, data.cores),
> + VEC_length (int, data.cores), sizeof (int),
> + compare_positive_ints);
> +
> + b = VEC_address (int, data.cores);
> + e = b + VEC_length (int, data.cores);
> +
> + /* Remove duplicates. */
> + while (b != e) {
> + if ((b + 1 < e) && (*b == *(b+1)))
Spaces around '+'.
> + {
> + *(b + 1) = *(e - 1);
> + --e;
> + }
> + else
> + {
> + ++b;
> + }
> + }
> +
> + for (i = 0; VEC_iterate (int, data.cores, i, elt); ++i)
> + ui_out_field_int (uiout, NULL, elt);
> +
> + do_cleanups (back_to_2);
> + }
> +
> + if (top_data->recurse)
> + print_thread_info (uiout, -1, inferior->pid);
> +
> do_cleanups (back_to);
> }
>
> return 0;
> }
>
> +/* Output a field named 'cores' with a list as the value. The elements of
> + the list are obtained by splitting 'cores' on comma. */
> +static void
Double space after period. Please add one empty line between describing
comment and the function.
> +output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
> +{
> + struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
> + field_name);
> + char *cores = xstrdup (xcores);
> + char *p = cores;
> + char *ts = 0;
> +
> + make_cleanup (xfree, cores);
> +
> + for (p = strtok_r (p, ",", &ts); p; p = strtok_r (NULL, ",", &ts))
> + ui_out_field_string (uiout, NULL, p);
strtok_r isn't portable (not available on mingw, for example).
You could pull it from gnulib, but I doubt it's worth the trouble.
> +
> + do_cleanups (back_to);
> +}
> +
> void
> mi_cmd_list_thread_groups (char *command, char **argv, int argc)
> {
> struct cleanup *back_to;
> int available = 0;
> - char *id = NULL;
> + int recurse = 0;
> + VEC (int) *ids = 0;
>
> - if (argc > 0 && strcmp (argv[0], "--available") == 0)
> + enum opt
> {
> - ++argv;
> - --argc;
> - available = 1;
> - }
> + AVAILABLE_OPT, RECURSE_OPT
> + };
> + static struct mi_opt opts[] =
> + {
> + {"-available", AVAILABLE_OPT, 0},
> + {"-recurse", RECURSE_OPT, 1},
> + { 0, 0, 0 }
> + };
>
> - if (argc > 0)
> - id = argv[0];
> + int optind = 0;
> + char *optarg;
>
> - back_to = make_cleanup (null_cleanup, NULL);
> + while (1)
> + {
> + int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
> + &optind, &optarg);
> + if (opt < 0)
> + break;
> + switch ((enum opt) opt)
> + {
> + case AVAILABLE_OPT:
> + available = 1;
> + break;
> + case RECURSE_OPT:
> + if (strcmp (optarg, "1") != 0)
> + error ("only '1' is permitted value for the '--recurse' option");
Seems strange not to allow '--recurse 0'. What future values
are you thinking of? Wouldn't it make more sense to call this
"--threads"? We may add other level-1 sublists to thread-groups
in the future, and _not_ want --recurse 1 to always show
them, no?
> + recurse = 1;
> + break;
> + }
> + }
>
> - if (available && id)
> + for (; optind < argc; ++optind)
> {
> - error (_("Can only report top-level available thread groups"));
> + char *end;
> + int inf = strtoul (argv[optind], &end, 0);
> + if (*end != '\0')
> + error ("invalid group id '%s'", argv[optind]);
Something fishy with the tabs vs spaces here.
> + VEC_safe_push (int, ids, inf);
> }
> - else if (available)
> + qsort (VEC_address (int, ids),
> + VEC_length (int, ids),
> + sizeof (int), compare_positive_ints);
> +
> + back_to = make_cleanup ((void (*)(void *))VEC_OP (int, free), &ids);
Please don't cast the function's type: instead write a
small function that has the proper cleanup prototype, and
call free there.
> +
> + if (available)
> {
> struct osdata *data;
> struct osdata_item *item;
> @@ -416,12 +541,25 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
> ix_items, item);
> ix_items++)
> {
> - struct cleanup *back_to =
> - make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
> + struct cleanup *back_to;
>
> const char *pid = get_osdata_column (item, "pid");
> const char *cmd = get_osdata_column (item, "command");
> const char *user = get_osdata_column (item, "user");
> + const char *cores = get_osdata_column (item, "cores");
> +
> + int pid_i = strtoul (pid, NULL, 0);
> +
> + /* At present, the target will return all available processes
> + and if information about specific ones was required, we filter
> + undesired processes here. */
> + if (ids && bsearch (&pid_i, VEC_address (int, ids),
> + VEC_length (int, ids),
> + sizeof (int), compare_positive_ints) == NULL)
> + continue;
> +
> +
> + back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
>
> ui_out_field_fmt (uiout, "id", "%s", pid);
> ui_out_field_string (uiout, "type", "process");
> @@ -429,21 +567,57 @@ mi_cmd_list_thread_groups (char *command, char **argv, int argc)
> ui_out_field_string (uiout, "description", cmd);
> if (user)
> ui_out_field_string (uiout, "user", user);
> + if (cores)
> + output_cores (uiout, "cores", cores);
> +
> + if (item->children && recurse)
> + {
> + struct osdata_item *child;
> + int ix_child;
> +
> + make_cleanup_ui_out_list_begin_end (uiout, "threads");
> +
> + for (ix_child = 0; VEC_iterate (osdata_item_s,
> + item->children->items,
> + ix_child, child); ++ix_child)
Please format as:
for (ix_child = 0;
VEC_iterate (osdata_item_s, item->children->items,
ix_child, child);
++ix_child)
(it took me a bit to locate the ++ix_child statement in your formatting)
> + {
> + struct cleanup *back_to_2 =
> + make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
> +
> + const char *tid = get_osdata_column (child, "tid");
> + const char *tcore = get_osdata_column (child, "core");
> + ui_out_field_string (uiout, "id", tid);
> + if (tcore)
> + ui_out_field_string (uiout, "core", tcore);
> +
> + do_cleanups (back_to_2);
> + }
> + }
>
> do_cleanups (back_to);
> }
> }
> - else if (id)
> + else if (VEC_length (int, ids) == 1)
> {
> - int pid = atoi (id);
> + /* Local thread groups, single id. */
> + int pid = *VEC_address (int, ids);
> if (!in_inferior_list (pid))
> - error ("Invalid thread group id '%s'", id);
> + error ("Invalid thread group id '%d'", pid);
> print_thread_info (uiout, -1, pid);
> }
> else
> {
> + struct print_one_inferior_data data;
> + data.recurse = recurse;
> + data.inferiors = ids;
> +
> + /* Local thread groups. Either no explicit ids -- and we
> + print everything, or several explicit ids. In both cases,
> + we print more than one group, and have to use 'groups'
> + as the top-level element. */
> make_cleanup_ui_out_list_begin_end (uiout, "groups");
> - iterate_over_inferiors (print_one_inferior, NULL);
> + update_thread_list ();
> + iterate_over_inferiors (print_one_inferior, &data);
> }
>
> do_cleanups (back_to);
> diff --git a/gdb/osdata.c b/gdb/osdata.c
> index 3d2c23c..394fc4d 100644
> --- a/gdb/osdata.c
> +++ b/gdb/osdata.c
> @@ -50,8 +50,10 @@ osdata_parse (const char *xml)
> /* Internal parsing data passed to all XML callbacks. */
> struct osdata_parsing_data
> {
> + struct osdata *top_osdata;
> struct osdata *osdata;
> char *property_name;
> + struct osdata_item *current_item;
> };
>
> /* Handle the start of a <osdata> element. */
> @@ -71,9 +73,37 @@ osdata_start_osdata (struct gdb_xml_parser *parser,
> type = VEC_index (gdb_xml_value_s, attributes, 0)->value;
> osdata = XZALLOC (struct osdata);
> osdata->type = xstrdup (type);
> + data->top_osdata = data->osdata = osdata;
> +}
> +
> +/* Handle the start of a <threads> element. */
> +
> +static void
> +osdata_start_threads (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;
> +
> + osdata = XZALLOC (struct osdata);
> + osdata->type = xstrdup ("threads");
> data->osdata = osdata;
> }
>
> +static void
> +osdata_end_threads (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;
> +
> + data->current_item->children = data->osdata;
> + data->osdata = data->top_osdata;
> +}
> +
> +
> /* Handle the start of a <item> element. */
>
> static void
> @@ -84,6 +114,8 @@ osdata_start_item (struct gdb_xml_parser *parser,
> struct osdata_parsing_data *data = user_data;
> struct osdata_item item = { NULL };
> VEC_safe_push (osdata_item_s, data->osdata->items, &item);
> + if (data->osdata == data->top_osdata)
> + data->current_item = VEC_last (osdata_item_s, data->osdata->items);
> }
>
> /* Handle the start of a <column> element. */
> @@ -137,10 +169,15 @@ const struct gdb_xml_attribute column_attributes[] = {
> { NULL, GDB_XML_AF_NONE, NULL, NULL }
> };
>
> +extern const struct gdb_xml_element osdata_children[];
> +
> 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 },
> + { "threads", NULL, osdata_children,
> + GDB_XML_EF_OPTIONAL, osdata_start_threads,
> + osdata_end_threads },
> { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
> };
>
> diff --git a/gdb/osdata.h b/gdb/osdata.h
> index 4ada454..485789e 100644
> --- a/gdb/osdata.h
> +++ b/gdb/osdata.h
> @@ -22,6 +22,9 @@
>
> #include "vec.h"
>
> +typedef struct osdata *osdata_p;
> +DEF_VEC_P(osdata_p);
> +
> typedef struct osdata_column
> {
> char *name;
> @@ -32,6 +35,13 @@ DEF_VEC_O(osdata_column_s);
> typedef struct osdata_item
> {
> VEC(osdata_column_s) *columns;
> +
> + /* Nested items. Presently, a given item may only contain
> + children of specific kind, and children->type gives
> + the kind of children. In fact, only 'threads' may be
> + the kind of children right now. */
> + osdata_p children;
> +
> } osdata_item_s;
> DEF_VEC_O(osdata_item_s);
>
> @@ -41,8 +51,7 @@ struct osdata
>
> 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 *);
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 9fa92fb..eb47713 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -60,6 +60,7 @@
> #include "remote-fileio.h"
> #include "gdb/fileio.h"
> #include "gdb_stat.h"
> +#include "xml-support.h"
>
> #include "memory-map.h"
>
> @@ -303,6 +304,13 @@ struct remote_state
> int ctrlc_pending_p;
> };
>
> +/* Private data that we'll store in (struct thread_info)->private. */
> +struct private_thread_info
> +{
> + char *extra;
> + int core;
> +};
> +
> /* Returns true if the multi-process extensions are in effect. */
> static int
> remote_multi_process_p (struct remote_state *rs)
> @@ -1054,6 +1062,7 @@ enum {
> PACKET_qXfer_spu_read,
> PACKET_qXfer_spu_write,
> PACKET_qXfer_osdata,
> + PACKET_qXfer_threads,
> PACKET_qGetTLSAddr,
> PACKET_qSupported,
> PACKET_QPassSignals,
> @@ -2303,6 +2312,76 @@ remote_find_new_threads (void)
> CRAZY_MAX_THREADS);
> }
>
> +typedef struct thread_item
> +{
> + ptid_t ptid;
> + char *extra;
> + int core;
> +} thread_item_t;
> +DEF_VEC_O(thread_item_t);
> +
> +struct threads_parsing_context
> +{
> + VEC (thread_item_t) *items;
> +};
> +
> +static void
> +start_thread (struct gdb_xml_parser *parser,
> + const struct gdb_xml_element *element,
> + void *user_data, VEC(gdb_xml_value_s) *attributes)
libexpat is an optional dependency. This code should be
conditionalized on HAVE_LIBEXPAT. After fixing, please confirm
GDB still builds and works against gdbserver when configured
with "--with-expat=no".
> +{
> + struct threads_parsing_context *data = user_data;
> +
> + struct thread_item item;
> + char *id;
> +
> + id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
> + item.ptid = read_ptid (id, NULL);
> +
> + if (VEC_length (gdb_xml_value_s, attributes) > 1)
> + item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
> + else
> + item.core = -1;
> +
> + item.extra = 0;
> +
> + VEC_safe_push (thread_item_t, data->items, &item);
> +}
> +
> +static void
> +end_thread (struct gdb_xml_parser *parser,
> + const struct gdb_xml_element *element,
> + void *user_data, const char *body_text)
> +{
> + struct threads_parsing_context *data = user_data;
> +
> + if (body_text && *body_text)
> + VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
> +}
> +
> +const struct gdb_xml_attribute thread_attributes[] = {
> + { "id", GDB_XML_AF_NONE, NULL, NULL },
> + { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
> + { NULL, GDB_XML_AF_NONE, NULL, NULL }
> +};
> +
> +const struct gdb_xml_element thread_children[] = {
> + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
> +};
> +
> +const struct gdb_xml_element threads_children[] = {
> + { "thread", thread_attributes, thread_children,
> + GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
> + start_thread, end_thread },
> + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
> +};
> +
> +const struct gdb_xml_element threads_elements[] = {
> + { "threads", NULL, threads_children,
> + GDB_XML_EF_NONE, NULL, NULL },
> + { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
> +};
> +
> /*
> * Find all threads for info threads command.
> * Uses new thread protocol contributed by Cisco.
> @@ -2320,6 +2399,66 @@ remote_threads_info (struct target_ops *ops)
> if (remote_desc == 0) /* paranoia */
> error (_("Command can only be used when connected to the remote target."));
>
> + if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
> + {
> + char *xml = target_read_stralloc (¤t_target,
> + TARGET_OBJECT_THREADS, NULL);
> +
> + struct cleanup *back_to = make_cleanup (xfree, xml);
> + if (xml && *xml)
> + {
> + struct gdb_xml_parser *parser;
> + struct threads_parsing_context context;
> + struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
> +
> + context.items = 0;
> + parser = gdb_xml_create_parser_and_cleanup (_("threads"),
> + threads_elements,
> + &context);
> +
> + gdb_xml_use_dtd (parser, "threads.dtd");
> +
> + if (gdb_xml_parse (parser, xml) == 0)
> + {
> + int i;
> + struct thread_item *item;
> +
> + for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
> + {
> + if (!ptid_equal (item->ptid, null_ptid))
> + {
> + struct thread_info *info;
> + /* In non-stop mode, we assume new found threads
> + are running until proven otherwise with a
> + stop reply. In all-stop, we can only get
> + here if all threads are stopped. */
> + int running = non_stop ? 1 : 0;
> +
> + remote_notice_new_inferior (item->ptid, running);
> +
> + info = find_thread_ptid (item->ptid);
> + if (info)
> + {
> + if (!info->private)
> + info->private = (struct private_thread_info *)
> + xmalloc (sizeof (struct private_thread_info));
> +
> + info->private->extra = item->extra;
> + item->extra = 0;
> + info->private->core = item->core;
> + }
> + }
> + xfree (item->extra);
> + }
> + }
> +
> + VEC_free (thread_item_t, context.items);
> + }
> +
> + do_cleanups (back_to);
> + return;
> + }
> +
> if (use_threadinfo_query)
> {
> putpkt ("qfThreadInfo");
> @@ -2392,6 +2531,15 @@ remote_threads_extra_info (struct thread_info *tp)
> server doesn't know about it. */
> return NULL;
>
> + if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
> + {
> + struct thread_info *info = find_thread_ptid (tp->ptid);
> + if (info && info->private)
> + return info->private->extra;
> + else
> + return NULL;
> + }
> +
> if (use_threadextra_query)
> {
> char *b = rs->buf;
> @@ -3152,6 +3300,8 @@ static struct protocol_feature remote_protocol_features[] = {
> PACKET_qXfer_spu_write },
> { "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
> PACKET_qXfer_osdata },
> + { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
> + PACKET_qXfer_threads },
> { "QPassSignals", PACKET_DISABLE, remote_supported_packet,
> PACKET_QPassSignals },
> { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
> @@ -4262,6 +4412,8 @@ struct stop_reply
>
> int solibs_changed;
> int replay_event;
> +
> + int core;
> };
>
> /* The list of already fetched and acknowledged stop events. */
> @@ -4425,6 +4577,7 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
> event->replay_event = 0;
> event->stopped_by_watchpoint_p = 0;
> event->regcache = NULL;
> + event->core = -1;
>
> switch (buf[0])
> {
> @@ -4451,7 +4604,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
> /* If this packet is an awatch packet, don't parse the 'a'
> as a register number. */
>
> - if (strncmp (p, "awatch", strlen("awatch")) != 0)
> + if (strncmp (p, "awatch", strlen("awatch")) != 0
> + && strncmp (p, "core", strlen ("core") != 0))
> {
> /* Read the ``P'' register number. */
> pnum = strtol (p, &p_temp, 16);
> @@ -4497,6 +4651,12 @@ Packet: '%s'\n"),
> if (p_temp)
> p = p_temp;
> }
> + else if (strncmp (p, "core", p1 - p) == 0)
> + {
> + ULONGEST c;
> + p = unpack_varlen_hex (++p1, &c);
> + event->core = c;
> + }
> else
> {
> /* Silently skip unknown optional info. */
> @@ -4706,6 +4866,7 @@ process_stop_reply (struct stop_reply *stop_reply,
> struct target_waitstatus *status)
> {
> ptid_t ptid;
> + struct thread_info *info;
>
> *status = stop_reply->ws;
> ptid = stop_reply->ptid;
> @@ -4736,6 +4897,12 @@ process_stop_reply (struct stop_reply *stop_reply,
> remote_stopped_by_watchpoint_p = stop_reply->stopped_by_watchpoint_p;
> remote_watch_data_address = stop_reply->watch_data_address;
>
> + /* Update the core associated with a thread when we process stop
> + event in that thread. */
> + info = find_thread_ptid (ptid);
> + if (info && info->private)
> + info->private->core = stop_reply->core;
Consider all-stop, multi-threaded applications:
What about the core of the other threads?
Shouldn't they all be invalidated?
Won't then be stale?
(see comment below)
> +
> remote_notice_new_inferior (ptid, 0);
> }
>
> @@ -7579,6 +7746,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
> (ops, "osdata", annex, readbuf, offset, len,
> &remote_protocol_packets[PACKET_qXfer_osdata]);
>
> + case TARGET_OBJECT_THREADS:
> + gdb_assert (annex == NULL);
> + return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
> + &remote_protocol_packets[PACKET_qXfer_threads]);
> +
> default:
> return -1;
> }
> @@ -8895,6 +9067,15 @@ remote_supports_cond_tracepoints (void)
> return rs->cond_tracepoints;
> }
>
> +static int
> +remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
> +{
> + struct thread_info *info = find_thread_ptid (ptid);
> + if (info && info->private)
> + return info->private->core;
> + return -1;
> +}
(see comment above) I think this returns stale core data
for all other threads other than the one that reported
the stop, no? How is that handled?
> +
> static void
> init_remote_ops (void)
> {
> @@ -8958,6 +9139,7 @@ Specify the serial device it is connected to\n\
> remote_ops.to_terminal_ours = remote_terminal_ours;
> remote_ops.to_supports_non_stop = remote_supports_non_stop;
> remote_ops.to_supports_multi_process = remote_supports_multi_process;
> + remote_ops.to_core_of_thread = remote_core_of_thread;
> }
>
> /* Set up the extended remote vector by making a copy of the standard
> @@ -9317,6 +9499,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
> add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
> "qXfer:osdata:read", "osdata", 0);
>
> + add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
> + "qXfer:threads:read", "threads", 0);
> +
> add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
> "qXfer:siginfo:read", "read-siginfo-object", 0);
>
> diff --git a/gdb/target.c b/gdb/target.c
> index cd1614b..4fd82b9 100644
> --- a/gdb/target.c
> +++ b/gdb/target.c
> @@ -3024,6 +3024,26 @@ target_store_registers (struct regcache *regcache, int regno)
> noprocess ();
> }
>
> +int
> +target_core_of_thread (ptid_t ptid)
> +{
> + struct target_ops *t;
> +
> + for (t = current_target.beneath; t != NULL; t = t->beneath)
> + {
> + if (t->to_core_of_thread != NULL)
> + {
> + int retval = t->to_core_of_thread (t, ptid);
> + if (targetdebug)
> + fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
> + PIDGET (ptid), retval);
> + return retval;
> + }
> + }
> +
> + return -1;
> +}
> +
> static void
> debug_to_prepare_to_store (struct regcache *regcache)
> {
> diff --git a/gdb/target.h b/gdb/target.h
> index ebe6056..7435433 100644
> --- a/gdb/target.h
> +++ b/gdb/target.h
> @@ -255,7 +255,9 @@ enum target_object
> /* Extra signal info. Usually the contents of `siginfo_t' on unix
> platforms. */
> TARGET_OBJECT_SIGNAL_INFO,
> - /* Possible future objects: TARGET_OBJECT_FILE, ... */
> + /* The list of threads that are being debugged. */
> + TARGET_OBJECT_THREADS,
> + /* Possible future objects: TARGET_OBJECT_FILE, ... */
> };
>
> /* Request that OPS transfer up to LEN 8-bit bytes of the target's
> @@ -597,6 +599,14 @@ struct target_ops
> struct address_space *(*to_thread_address_space) (struct target_ops *,
> ptid_t);
>
> + /* Return the core that thread PTID is on, or -1 if such information
> + is not available. For a stopped thread, this is supposed to return
> + the core the thread was last running on. For running threads, it
> + should return one of the cores that the thread was running between
> + the call to this function and return -- and if it was running on
> + several cores, any other may be returned. */
> + int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
I guess that on some targets, it will be impossible to know which core
a running thread was running on. Should it be simply documented as
undefined instead?
> +
> int to_magic;
> /* Need sub-structure for target machine related rather than comm related?
> */
> @@ -1246,6 +1256,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
> (*current_target.to_log_command) (p); \
> while (0)
>
> +
> +extern int target_core_of_thread (ptid_t ptid);
> +
> /* Routines for maintenance of the target structures...
>
> add_target: Add a target to the list of all possible targets.
> diff --git a/gdb/thread.c b/gdb/thread.c
> index 689e9d5..fc2ee2a 100644
> --- a/gdb/thread.c
> +++ b/gdb/thread.c
> @@ -468,8 +468,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
> struct cleanup *cleanup_chain;
> int current_thread = -1;
>
> - prune_threads ();
> - target_find_new_threads ();
> + update_thread_list ();
>
> cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
>
> @@ -748,8 +747,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
> char *extra_info;
> int current_thread = -1;
>
> - prune_threads ();
> - target_find_new_threads ();
> + update_thread_list ();
> current_ptid = inferior_ptid;
>
> /* We'll be switching threads temporarily. */
> @@ -759,6 +757,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
> for (tp = thread_list; tp; tp = tp->next)
> {
> struct cleanup *chain2;
> + int core;
>
> if (requested_thread != -1 && tp->num != requested_thread)
> continue;
> @@ -817,6 +816,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
> ui_out_field_string (uiout, "state", state);
> }
>
> + core = target_core_of_thread (tp->ptid);
> + if (ui_out_is_mi_like_p (uiout) && core != -1)
> + ui_out_field_int (uiout, "core", core);
> +
> do_cleanups (chain2);
> }
>
> @@ -1058,8 +1061,7 @@ thread_apply_all_command (char *cmd, int from_tty)
> if (cmd == NULL || *cmd == '\000')
> error (_("Please specify a command following the thread ID list"));
>
> - prune_threads ();
> - target_find_new_threads ();
> + update_thread_list ();
>
> old_chain = make_cleanup_restore_current_thread ();
>
> @@ -1245,6 +1247,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
> return GDB_RC_OK;
> }
>
> +void
> +update_thread_list (void)
> +{
> + prune_threads ();
> + target_find_new_threads ();
> +}
> +
> /* Commands with a prefix of `thread'. */
> struct cmd_list_element *thread_cmd_list = NULL;
>
> diff --git a/gdb/ui-out.c b/gdb/ui-out.c
> index 19a4644..6ecdd1f 100644
> --- a/gdb/ui-out.c
> +++ b/gdb/ui-out.c
> @@ -44,7 +44,7 @@ struct ui_out_hdr
> is always available. Stack/nested level 0 is reserved for the
> top-level result. */
>
> -enum { MAX_UI_OUT_LEVELS = 6 };
> +enum { MAX_UI_OUT_LEVELS = 8 };
>
> struct ui_out_level
> {
> diff --git a/gdb/utils.c b/gdb/utils.c
> index 16ad084..c9aa612 100644
> --- a/gdb/utils.c
> +++ b/gdb/utils.c
> @@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
> return argv;
> }
>
> +int
> +compare_positive_ints (const void *ap, const void *bp)
> +{
> + /* Because we know we're comparing two ints which are positive,
> + there's no danger of overflow here. */
> + return * (int *) ap - * (int *) bp;
> +}
> +
> /* Provide a prototype to silence -Wmissing-prototypes. */
> extern initialize_file_ftype _initialize_utils;
>
Other:
- did you consider being able to list the available
cores? A candidate for "info os cores", I guess.
- your patch adds a bunch of trailing whitespace, would it
be possible to get rid of those please?
>quilt refresh
Warning: trailing whitespace in lines 26273,26309,31859 of gdb/doc/gdb.texinfo
Warning: trailing whitespace in line 260 of gdb/target.h
Warning: trailing whitespace in lines 2351,2352,2404,2408,2420,2452,2454,2455 of gdb/remote.c
Warning: trailing whitespace in line 101 of gdb/osdata.c
Warning: trailing whitespace in line 44 of gdb/osdata.h
Warning: trailing whitespace in lines 729,732,737,763,767,770,772,781,784,1230 of gdb/gdbserver/server.c
Warning: trailing whitespace in lines 2827,2837,2838,2854,2875,2880,2888,2892,2894,2959,2982,2986,2987,2988,2992,3274,3284,3285,3299,3304,3311 of gdb/gdbserver/linux-low.c
Warning: trailing whitespace in lines 393,411,438,446,459,469,497,503,504,522,556,557,576,580,581,584,594 of gdb/mi/mi-main.c
Refreshed patch core-awareness.diff
(quilt refresh --strip-trailing-whitespace does it automaticaly, and I
guess git has something similar)
- Don't forget to go over the patch and add
double-space-after-period everywhere. Sorry for the pickyness.
--
Pedro Alves
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2010-01-13 5:21 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-12-16 20:53 [MI] core awareness Vladimir Prus
2009-12-17 15:31 ` Pedro Alves
2009-12-18 11:41 ` Eli Zaretskii
2009-12-17 15:33 Pedro Alves
2009-12-21 14:48 ` Vladimir Prus
2009-12-31 11:51 ` Pedro Alves
2010-01-04 15:11 ` Vladimir Prus
2010-01-08 20:30 ` Pedro Alves
2010-01-09 12:41 ` Vladimir Prus
2010-01-09 13:48 ` Eli Zaretskii
2010-01-11 14:17 ` Pedro Alves
2010-01-11 21:01 ` Vladimir Prus
2010-01-11 21:22 ` Pedro Alves
2010-01-12 21:44 ` Vladimir Prus
2010-01-12 22:56 ` Doug Evans
2010-01-12 23:08 ` Doug Evans
2010-01-13 5:21 ` Vladimir Prus
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox