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.
This commit is contained in:
Vladimir Prus 2010-01-12 21:40:25 +00:00
parent 837504c42d
commit dc146f7c09
25 changed files with 1334 additions and 133 deletions

View File

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

View File

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

View File

@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
/* 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)
{

View File

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

View File

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

View File

@ -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.
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.
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 wait timeout.
* File-I/O Remote Protocol Extension::
* Library List Format::
* Memory Map Format::
* Thread List Format::
@end menu
@node Overview
@ -29024,6 +29134,10 @@ two-digit hex number.
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
@ -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

View File

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

View File

@ -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
{
@ -2801,6 +2802,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,
@ -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;
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)
{
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
static int
print_one_inferior (struct inferior *inferior, void *arg)
struct collect_cores_data
{
if (inferior->pid != 0)
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 *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 *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;
}
/* 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;
make_cleanup (xfree, cores);
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)
{
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);
if (available && id)
while (1)
{
error (_("Can only report top-level available thread groups"));
}
else 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++)
int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
&optind, &optarg);
if (opt < 0)
break;
switch ((enum opt) opt)
{
struct cleanup *back_to =
make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
const char *pid = get_osdata_column (item, "pid");
const char *cmd = get_osdata_column (item, "command");
const char *user = get_osdata_column (item, "user");
ui_out_field_fmt (uiout, "id", "%s", pid);
ui_out_field_string (uiout, "type", "process");
if (cmd)
ui_out_field_string (uiout, "description", cmd);
if (user)
ui_out_field_string (uiout, "user", user);
do_cleanups (back_to);
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);
}

View File

@ -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,26 @@ 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;
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 *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 (&current_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_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 +3413,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 +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, 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 +4721,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 +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 *stop_reply,
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 *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 +7858,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 +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 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 +10130,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);

View File

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

View File

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

View File

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

View File

@ -1877,7 +1877,7 @@ proc mi_check_thread_states { xstates test } {
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

View File

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

View File

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

View File

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