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:
parent
837504c42d
commit
dc146f7c09
25 changed files with 1334 additions and 133 deletions
|
@ -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).
|
||||
|
|
|
@ -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,
|
||||
|
|
13
gdb/bcache.c
13
gdb/bcache.c
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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);
|
||||
|
|
367
gdb/mi/mi-main.c
367
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
218
gdb/remote.c
218
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,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 (¤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_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);
|
||||
|
||||
|
|
20
gdb/target.c
20
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)
|
||||
{
|
||||
|
|
13
gdb/target.h
13
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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
30
gdb/thread.c
30
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;
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue