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>
|
2010-01-12 Jan Kratochvil <jan.kratochvil@redhat.com>
|
||||||
|
|
||||||
* configure: Regenerate (for _STRUCTURED_PROC).
|
* configure: Regenerate (for _STRUCTURED_PROC).
|
||||||
|
|
|
@ -444,7 +444,8 @@ RUNTESTFLAGS=
|
||||||
|
|
||||||
# XML files to build in to GDB.
|
# XML files to build in to GDB.
|
||||||
XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd \
|
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
|
# 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,
|
# 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. */
|
/* 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
|
static void
|
||||||
print_percentage (int portion, int total)
|
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. */
|
/* To compute the median, we need the set of chain lengths sorted. */
|
||||||
qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
|
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]),
|
qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
|
||||||
compare_ints);
|
compare_positive_ints);
|
||||||
|
|
||||||
if (c->num_buckets > 0)
|
if (c->num_buckets > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -417,6 +417,8 @@ char *ldirname (const char *filename);
|
||||||
|
|
||||||
char **gdb_buildargv (const char *);
|
char **gdb_buildargv (const char *);
|
||||||
|
|
||||||
|
int compare_positive_ints (const void *ap, const void *bp);
|
||||||
|
|
||||||
/* From demangle.c */
|
/* From demangle.c */
|
||||||
|
|
||||||
extern void set_demangling_style (char *);
|
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>
|
2010-01-06 Stan Shebs <stan@codesourcery.com>
|
||||||
|
|
||||||
* gdb.texinfo (Starting and Stopping Trace Experiments): Document
|
* gdb.texinfo (Starting and Stopping Trace Experiments): Document
|
||||||
|
|
|
@ -15542,6 +15542,10 @@ are:
|
||||||
@tab @code{qXfer:siginfo:write}
|
@tab @code{qXfer:siginfo:write}
|
||||||
@tab @code{set $_siginfo}
|
@tab @code{set $_siginfo}
|
||||||
|
|
||||||
|
@item @code{threads}
|
||||||
|
@tab @code{qXfer:threads:read}
|
||||||
|
@tab @code{info threads}
|
||||||
|
|
||||||
@item @code{get-thread-local-@*storage-address}
|
@item @code{get-thread-local-@*storage-address}
|
||||||
@tab @code{qGetTLSAddr}
|
@tab @code{qGetTLSAddr}
|
||||||
@tab Displaying @code{__thread} variables
|
@tab Displaying @code{__thread} variables
|
||||||
|
@ -21828,6 +21832,7 @@ follow development on @email{gdb@@sourceware.org} and
|
||||||
* GDB/MI Stream Records::
|
* GDB/MI Stream Records::
|
||||||
* GDB/MI Async Records::
|
* GDB/MI Async Records::
|
||||||
* GDB/MI Frame Information::
|
* GDB/MI Frame Information::
|
||||||
|
* GDB/MI Thread Information::
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@node GDB/MI Result Records
|
@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
|
all threads together, or even for a single thread, if the thread must
|
||||||
be stepped though some code before letting it run freely.
|
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
|
The target has stopped. The @var{reason} field can have one of the
|
||||||
following values:
|
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}
|
value of @code{"all"}. Otherwise, the value of the @var{stopped}
|
||||||
field will be a list of thread identifiers. Presently, this list will
|
field will be a list of thread identifiers. Presently, this list will
|
||||||
always include a single thread, but frontend should be prepared to see
|
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}"
|
@item =thread-group-created,id="@var{id}"
|
||||||
@itemx =thread-group-exited,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
|
@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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
@node GDB/MI Simple Examples
|
@node GDB/MI Simple Examples
|
||||||
|
@ -26349,20 +26384,84 @@ while the target is running.
|
||||||
@subheading Synopsis
|
@subheading Synopsis
|
||||||
|
|
||||||
@smallexample
|
@smallexample
|
||||||
-list-thread-groups [ --available ] [ @var{group} ]
|
-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
When used without the @var{group} parameter, lists top-level thread
|
Lists thread groups (@pxref{Thread groups}). When a single thread
|
||||||
groups that are being debugged. When used with the @var{group}
|
group is passed as the argument, lists the children of that group.
|
||||||
parameter, the children of the specified group are listed. The
|
When several thread group are passed, lists information about those
|
||||||
children can be either threads, or other groups. At present,
|
thread groups. Without any parameters, lists information about all
|
||||||
@value{GDBN} will not report both threads and groups as children at
|
top-level thread groups.
|
||||||
the same time, but it may change in future.
|
|
||||||
|
|
||||||
With the @samp{--available} option, instead of reporting groups that
|
Normally, thread groups that are being debugged are reported.
|
||||||
are been debugged, GDB will report all thread groups available on the
|
With the @samp{--available} option, @value{GDBN} reports thread groups
|
||||||
target. Using the @samp{--available} option together with @var{group}
|
available on the target.
|
||||||
is not allowed.
|
|
||||||
|
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
|
@subheading Example
|
||||||
|
|
||||||
|
@ -26376,6 +26475,16 @@ is not allowed.
|
||||||
@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
|
@{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
|
||||||
frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
|
frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
|
||||||
file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
|
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
|
@end smallexample
|
||||||
|
|
||||||
@subheading The @code{-interpreter-exec} Command
|
@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::
|
* File-I/O Remote Protocol Extension::
|
||||||
* Library List Format::
|
* Library List Format::
|
||||||
* Memory Map Format::
|
* Memory Map Format::
|
||||||
|
* Thread List Format::
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
@node Overview
|
@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
|
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}.
|
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
|
@item
|
||||||
If @var{n} is a recognized @dfn{stop reason}, it describes a more
|
If @var{n} is a recognized @dfn{stop reason}, it describes a more
|
||||||
specific event that stopped the target. The currently defined stop
|
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}
|
beginning when executing backward) of the log. The value of @var{r}
|
||||||
will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
|
will be either @samp{begin} or @samp{end}. @xref{Reverse Execution},
|
||||||
for more information.
|
for more information.
|
||||||
|
|
||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@item W @var{AA}
|
@item W @var{AA}
|
||||||
|
@ -29599,6 +29711,12 @@ These are the currently defined stub features and their properties:
|
||||||
@tab @samp{-}
|
@tab @samp{-}
|
||||||
@tab Yes
|
@tab Yes
|
||||||
|
|
||||||
|
@item @samp{qXfer:threads:read}
|
||||||
|
@tab No
|
||||||
|
@tab @samp{-}
|
||||||
|
@tab Yes
|
||||||
|
|
||||||
|
|
||||||
@item @samp{QNonStop}
|
@item @samp{QNonStop}
|
||||||
@tab No
|
@tab No
|
||||||
@tab @samp{-}
|
@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
|
The remote stub understands the @samp{qXfer:siginfo:write} packet
|
||||||
(@pxref{qXfer siginfo write}).
|
(@pxref{qXfer siginfo write}).
|
||||||
|
|
||||||
|
@item qXfer:threads:read
|
||||||
|
The remote stub understands the @samp{qXfer:threads:read} packet
|
||||||
|
(@pxref{qXfer threads read}).
|
||||||
|
|
||||||
@item QNonStop
|
@item QNonStop
|
||||||
The remote stub understands the @samp{QNonStop} packet
|
The remote stub understands the @samp{QNonStop} packet
|
||||||
(@pxref{QNonStop}).
|
(@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
|
by supplying an appropriate @samp{qSupported} response
|
||||||
(@pxref{qSupported}).
|
(@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}
|
@item qXfer:osdata:read::@var{offset},@var{length}
|
||||||
@anchor{qXfer osdata read}
|
@anchor{qXfer osdata read}
|
||||||
Access the target's @dfn{operating system information}.
|
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>
|
<!ATTLIST property name CDATA #REQUIRED>
|
||||||
@end smallexample
|
@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
|
@include agentexpr.texi
|
||||||
|
|
||||||
@node Target Descriptions
|
@node Target Descriptions
|
||||||
|
@ -32468,6 +32623,7 @@ An example document is:
|
||||||
<column name="pid">1</column>
|
<column name="pid">1</column>
|
||||||
<column name="user">root</column>
|
<column name="user">root</column>
|
||||||
<column name="command">/sbin/init</column>
|
<column name="command">/sbin/init</column>
|
||||||
|
<column name="cores">1,2,3</column>
|
||||||
</item>
|
</item>
|
||||||
</osdata>
|
</osdata>
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
@ -32475,7 +32631,9 @@ An example document is:
|
||||||
Each item should include a column whose name is @samp{pid}. The value
|
Each item should include a column whose name is @samp{pid}. The value
|
||||||
of that column should identify the process on the target. The
|
of that column should identify the process on the target. The
|
||||||
@samp{user} and @samp{command} columns are optional, and will be
|
@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.
|
which @value{GDBN} currently ignores.
|
||||||
|
|
||||||
@include gpl.texi
|
@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>
|
2010-01-04 Ulrich Weigand <uweigand@de.ibm.com>
|
||||||
|
|
||||||
* Makefile.in (clean): Remove new generated files.
|
* 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 void *add_lwp (ptid_t ptid);
|
||||||
static int linux_stopped_by_watchpoint (void);
|
static int linux_stopped_by_watchpoint (void);
|
||||||
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
|
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
|
||||||
|
static int linux_core_of_thread (ptid_t ptid);
|
||||||
|
|
||||||
struct pending_signals
|
struct pending_signals
|
||||||
{
|
{
|
||||||
|
@ -2801,6 +2802,175 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
static int
|
||||||
linux_qxfer_osdata (const char *annex,
|
linux_qxfer_osdata (const char *annex,
|
||||||
unsigned char *readbuf, unsigned const char *writebuf,
|
unsigned char *readbuf, unsigned const char *writebuf,
|
||||||
|
@ -2811,10 +2981,16 @@ linux_qxfer_osdata (const char *annex,
|
||||||
static const char *buf;
|
static const char *buf;
|
||||||
static long len_avail = -1;
|
static long len_avail = -1;
|
||||||
static struct buffer buffer;
|
static struct buffer buffer;
|
||||||
|
int processes = 0;
|
||||||
|
int threads = 0;
|
||||||
|
|
||||||
DIR *dirp;
|
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;
|
return 0;
|
||||||
|
|
||||||
if (!readbuf || writebuf)
|
if (!readbuf || writebuf)
|
||||||
|
@ -2827,7 +3003,10 @@ linux_qxfer_osdata (const char *annex,
|
||||||
len_avail = 0;
|
len_avail = 0;
|
||||||
buf = NULL;
|
buf = NULL;
|
||||||
buffer_init (&buffer);
|
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");
|
dirp = opendir ("/proc");
|
||||||
if (dirp)
|
if (dirp)
|
||||||
|
@ -2846,37 +3025,16 @@ linux_qxfer_osdata (const char *annex,
|
||||||
if (stat (procentry, &statbuf) == 0
|
if (stat (procentry, &statbuf) == 0
|
||||||
&& S_ISDIR (statbuf.st_mode))
|
&& S_ISDIR (statbuf.st_mode))
|
||||||
{
|
{
|
||||||
char pathname[128];
|
int pid = (int) strtoul (dp->d_name, NULL, 10);
|
||||||
FILE *f;
|
|
||||||
char cmd[MAXPATHLEN + 1];
|
|
||||||
struct passwd *entry;
|
|
||||||
|
|
||||||
sprintf (pathname, "/proc/%s/cmdline", dp->d_name);
|
if (processes)
|
||||||
entry = getpwuid (statbuf.st_uid);
|
|
||||||
|
|
||||||
if ((f = fopen (pathname, "r")) != NULL)
|
|
||||||
{
|
{
|
||||||
size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
|
struct passwd *entry = getpwuid (statbuf.st_uid);
|
||||||
if (len > 0)
|
show_process (pid, entry ? entry->pw_name : "?", &buffer);
|
||||||
{
|
}
|
||||||
int i;
|
else if (threads)
|
||||||
for (i = 0; i < len; i++)
|
{
|
||||||
if (cmd[i] == '\0')
|
list_threads (pid, &buffer, NULL);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3152,6 +3310,55 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
|
||||||
return ret;
|
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 = {
|
static struct target_ops linux_target_ops = {
|
||||||
linux_create_inferior,
|
linux_create_inferior,
|
||||||
linux_attach,
|
linux_attach,
|
||||||
|
@ -3191,10 +3398,11 @@ static struct target_ops linux_target_ops = {
|
||||||
linux_start_non_stop,
|
linux_start_non_stop,
|
||||||
linux_supports_multi_process,
|
linux_supports_multi_process,
|
||||||
#ifdef USE_THREAD_DB
|
#ifdef USE_THREAD_DB
|
||||||
thread_db_handle_monitor_command
|
thread_db_handle_monitor_command,
|
||||||
#else
|
#else
|
||||||
NULL
|
NULL,
|
||||||
#endif
|
#endif
|
||||||
|
linux_core_of_thread
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
|
||||||
gdbserver to know what inferior_ptid is. */
|
gdbserver to know what inferior_ptid is. */
|
||||||
if (1 || !ptid_equal (general_thread, ptid))
|
if (1 || !ptid_equal (general_thread, ptid))
|
||||||
{
|
{
|
||||||
|
int core = -1;
|
||||||
/* In non-stop, don't change the general thread behind
|
/* In non-stop, don't change the general thread behind
|
||||||
GDB's back. */
|
GDB's back. */
|
||||||
if (!non_stop)
|
if (!non_stop)
|
||||||
|
@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
|
||||||
buf = write_ptid (buf, ptid);
|
buf = write_ptid (buf, ptid);
|
||||||
strcat (buf, ";");
|
strcat (buf, ";");
|
||||||
buf += strlen (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;
|
prev = f + 1;
|
||||||
}
|
}
|
||||||
break;
|
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;
|
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. */
|
/* Handle all of the extended 'q' packets. */
|
||||||
void
|
void
|
||||||
handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
|
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;
|
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. */
|
/* Protocol features query. */
|
||||||
if (strncmp ("qSupported", own_buf, 10) == 0
|
if (strncmp ("qSupported", own_buf, 10) == 0
|
||||||
&& (own_buf[10] == ':' || 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 ())
|
if (target_supports_non_stop ())
|
||||||
strcat (own_buf, ";QNonStop+");
|
strcat (own_buf, ";QNonStop+");
|
||||||
|
|
||||||
|
strcat (own_buf, ";qXfer:threads:read+");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -283,6 +283,9 @@ struct target_ops
|
||||||
/* If not NULL, target-specific routine to process monitor command.
|
/* If not NULL, target-specific routine to process monitor command.
|
||||||
Returns 1 if handled, or 0 to perform default processing. */
|
Returns 1 if handled, or 0 to perform default processing. */
|
||||||
int (*handle_monitor_command) (char *);
|
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;
|
extern struct target_ops *the_target;
|
||||||
|
|
|
@ -187,6 +187,10 @@ struct thread_info
|
||||||
|
|
||||||
/* Private data used by the target vector implementation. */
|
/* Private data used by the target vector implementation. */
|
||||||
struct private_thread_info *private;
|
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. */
|
/* 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. */
|
INFERIOR_PTID. INFERIOR_PTID *must* be in the thread list. */
|
||||||
extern struct thread_info* inferior_thread (void);
|
extern struct thread_info* inferior_thread (void);
|
||||||
|
|
||||||
|
extern void update_thread_list (void);
|
||||||
|
|
||||||
#endif /* GDBTHREAD_H */
|
#endif /* GDBTHREAD_H */
|
||||||
|
|
|
@ -1184,6 +1184,7 @@ add_lwp (ptid_t ptid)
|
||||||
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
|
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
|
||||||
|
|
||||||
lp->ptid = ptid;
|
lp->ptid = ptid;
|
||||||
|
lp->core = -1;
|
||||||
|
|
||||||
lp->next = lwp_list;
|
lp->next = lwp_list;
|
||||||
lwp_list = lp;
|
lwp_list = lp;
|
||||||
|
@ -3642,6 +3643,7 @@ retry:
|
||||||
fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
|
fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
|
||||||
|
|
||||||
restore_child_signals_mask (&prev_mask);
|
restore_child_signals_mask (&prev_mask);
|
||||||
|
lp->core = linux_nat_core_of_thread_1 (lp->ptid);
|
||||||
return lp->ptid;
|
return lp->ptid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5423,6 +5425,75 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
|
||||||
return inf->aspace;
|
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
|
void
|
||||||
linux_nat_add_target (struct target_ops *t)
|
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_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
|
/* We don't change the stratum; this target will sit at
|
||||||
process_stratum and thread_db will set at thread_stratum. This
|
process_stratum and thread_db will set at thread_stratum. This
|
||||||
is a little strange, since this is a multi-threaded-capable
|
is a little strange, since this is a multi-threaded-capable
|
||||||
|
|
|
@ -89,6 +89,9 @@ struct lwp_info
|
||||||
- TARGET_WAITKIND_SYSCALL_RETURN */
|
- TARGET_WAITKIND_SYSCALL_RETURN */
|
||||||
int syscall_state;
|
int syscall_state;
|
||||||
|
|
||||||
|
/* The processor core this LWP was last seen on. */
|
||||||
|
int core;
|
||||||
|
|
||||||
/* Next LWP in list. */
|
/* Next LWP in list. */
|
||||||
struct lwp_info *next;
|
struct lwp_info *next;
|
||||||
};
|
};
|
||||||
|
@ -163,3 +166,6 @@ void linux_nat_switch_fork (ptid_t new_ptid);
|
||||||
|
|
||||||
/* Return the saved siginfo associated with PTID. */
|
/* Return the saved siginfo associated with PTID. */
|
||||||
struct siginfo *linux_nat_get_siginfo (ptid_t 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);
|
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
|
static void
|
||||||
thread_db_find_new_threads (struct target_ops *ops)
|
thread_db_find_new_threads (struct target_ops *ops)
|
||||||
|
@ -1466,6 +1472,9 @@ thread_db_find_new_threads (struct target_ops *ops)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
thread_db_find_new_threads_1 (inferior_ptid);
|
thread_db_find_new_threads_1 (inferior_ptid);
|
||||||
|
|
||||||
|
iterate_over_lwps (minus_one_ptid /* iterate over all */,
|
||||||
|
update_thread_core, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
|
|
|
@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
|
||||||
|
|
||||||
if (print_frame)
|
if (print_frame)
|
||||||
{
|
{
|
||||||
|
int core;
|
||||||
if (uiout != mi_uiout)
|
if (uiout != mi_uiout)
|
||||||
{
|
{
|
||||||
/* The normal_stop function has printed frame information into
|
/* The normal_stop function has printed frame information into
|
||||||
|
@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
ui_out_field_string (mi_uiout, "stopped-threads", "all");
|
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);
|
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 "valprint.h"
|
||||||
#include "inferior.h"
|
#include "inferior.h"
|
||||||
#include "osdata.h"
|
#include "osdata.h"
|
||||||
|
#include "splay-tree.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <sys/time.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);
|
print_thread_info (uiout, thread, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
struct collect_cores_data
|
||||||
print_one_inferior (struct inferior *inferior, void *arg)
|
|
||||||
{
|
{
|
||||||
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
|
struct cleanup *back_to
|
||||||
= make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
|
= 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_string (uiout, "type", "process");
|
||||||
ui_out_field_int (uiout, "pid", inferior->pid);
|
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);
|
do_cleanups (back_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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
|
void
|
||||||
mi_cmd_list_thread_groups (char *command, char **argv, int argc)
|
mi_cmd_list_thread_groups (char *command, char **argv, int argc)
|
||||||
{
|
{
|
||||||
struct cleanup *back_to;
|
struct cleanup *back_to;
|
||||||
int available = 0;
|
int available = 0;
|
||||||
char *id = NULL;
|
int recurse = 0;
|
||||||
|
VEC (int) *ids = 0;
|
||||||
|
|
||||||
if (argc > 0 && strcmp (argv[0], "--available") == 0)
|
enum opt
|
||||||
{
|
{
|
||||||
++argv;
|
AVAILABLE_OPT, RECURSE_OPT
|
||||||
--argc;
|
};
|
||||||
available = 1;
|
static struct mi_opt opts[] =
|
||||||
}
|
{
|
||||||
|
{"-available", AVAILABLE_OPT, 0},
|
||||||
|
{"-recurse", RECURSE_OPT, 1},
|
||||||
|
{ 0, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
if (argc > 0)
|
int optind = 0;
|
||||||
id = argv[0];
|
char *optarg;
|
||||||
|
|
||||||
back_to = make_cleanup (null_cleanup, NULL);
|
while (1)
|
||||||
|
|
||||||
if (available && id)
|
|
||||||
{
|
{
|
||||||
error (_("Can only report top-level available thread groups"));
|
int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
|
||||||
}
|
&optind, &optarg);
|
||||||
else if (available)
|
if (opt < 0)
|
||||||
{
|
break;
|
||||||
struct osdata *data;
|
switch ((enum opt) opt)
|
||||||
struct osdata_item *item;
|
|
||||||
int ix_items;
|
|
||||||
|
|
||||||
data = get_osdata ("processes");
|
|
||||||
make_cleanup_osdata_free (data);
|
|
||||||
|
|
||||||
make_cleanup_ui_out_list_begin_end (uiout, "groups");
|
|
||||||
|
|
||||||
for (ix_items = 0;
|
|
||||||
VEC_iterate (osdata_item_s, data->items,
|
|
||||||
ix_items, item);
|
|
||||||
ix_items++)
|
|
||||||
{
|
{
|
||||||
struct cleanup *back_to =
|
case AVAILABLE_OPT:
|
||||||
make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
|
available = 1;
|
||||||
|
break;
|
||||||
const char *pid = get_osdata_column (item, "pid");
|
case RECURSE_OPT:
|
||||||
const char *cmd = get_osdata_column (item, "command");
|
if (strcmp (optarg, "0") == 0)
|
||||||
const char *user = get_osdata_column (item, "user");
|
;
|
||||||
|
else if (strcmp (optarg, "1") == 0)
|
||||||
ui_out_field_fmt (uiout, "id", "%s", pid);
|
recurse = 1;
|
||||||
ui_out_field_string (uiout, "type", "process");
|
else
|
||||||
if (cmd)
|
error ("only '0' and '1' are valid values for the '--recurse' option");
|
||||||
ui_out_field_string (uiout, "description", cmd);
|
break;
|
||||||
if (user)
|
|
||||||
ui_out_field_string (uiout, "user", user);
|
|
||||||
|
|
||||||
do_cleanups (back_to);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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))
|
if (!in_inferior_list (pid))
|
||||||
error ("Invalid thread group id '%s'", id);
|
error ("Invalid thread group id '%d'", pid);
|
||||||
print_thread_info (uiout, -1, pid);
|
print_thread_info (uiout, -1, pid);
|
||||||
}
|
}
|
||||||
else
|
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");
|
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);
|
do_cleanups (back_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
218
gdb/remote.c
218
gdb/remote.c
|
@ -60,6 +60,7 @@
|
||||||
#include "remote-fileio.h"
|
#include "remote-fileio.h"
|
||||||
#include "gdb/fileio.h"
|
#include "gdb/fileio.h"
|
||||||
#include "gdb_stat.h"
|
#include "gdb_stat.h"
|
||||||
|
#include "xml-support.h"
|
||||||
|
|
||||||
#include "memory-map.h"
|
#include "memory-map.h"
|
||||||
|
|
||||||
|
@ -324,6 +325,20 @@ struct remote_state
|
||||||
int ctrlc_pending_p;
|
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. */
|
/* Returns true if the multi-process extensions are in effect. */
|
||||||
static int
|
static int
|
||||||
remote_multi_process_p (struct remote_state *rs)
|
remote_multi_process_p (struct remote_state *rs)
|
||||||
|
@ -1121,6 +1136,7 @@ enum {
|
||||||
PACKET_qXfer_spu_read,
|
PACKET_qXfer_spu_read,
|
||||||
PACKET_qXfer_spu_write,
|
PACKET_qXfer_spu_write,
|
||||||
PACKET_qXfer_osdata,
|
PACKET_qXfer_osdata,
|
||||||
|
PACKET_qXfer_threads,
|
||||||
PACKET_qGetTLSAddr,
|
PACKET_qGetTLSAddr,
|
||||||
PACKET_qSupported,
|
PACKET_qSupported,
|
||||||
PACKET_QPassSignals,
|
PACKET_QPassSignals,
|
||||||
|
@ -1395,7 +1411,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
|
||||||
remote_add_thread (currthread, running);
|
remote_add_thread (currthread, running);
|
||||||
inferior_ptid = currthread;
|
inferior_ptid = currthread;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ptid_equal (magic_null_ptid, inferior_ptid))
|
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
|
doesn't support qC. This is the first stop reported
|
||||||
after an attach, so this is the main thread. Update the
|
after an attach, so this is the main thread. Update the
|
||||||
ptid in the thread list. */
|
ptid in the thread list. */
|
||||||
thread_change_ptid (inferior_ptid, currthread);
|
thread_change_ptid (inferior_ptid, currthread);
|
||||||
return;
|
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
|
/* Call this function as a result of
|
||||||
1) A halt indication (T packet) containing a thread id
|
1) A halt indication (T packet) containing a thread id
|
||||||
2) A direct query of currthread
|
2) A direct query of currthread
|
||||||
|
@ -1437,12 +1473,6 @@ static void
|
||||||
record_currthread (ptid_t currthread)
|
record_currthread (ptid_t currthread)
|
||||||
{
|
{
|
||||||
general_thread = 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;
|
static char *last_pass_packet;
|
||||||
|
@ -2371,6 +2401,80 @@ remote_find_new_threads (void)
|
||||||
CRAZY_MAX_THREADS);
|
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.
|
* Find all threads for info threads command.
|
||||||
* Uses new thread protocol contributed by Cisco.
|
* Uses new thread protocol contributed by Cisco.
|
||||||
|
@ -2388,6 +2492,61 @@ remote_threads_info (struct target_ops *ops)
|
||||||
if (remote_desc == 0) /* paranoia */
|
if (remote_desc == 0) /* paranoia */
|
||||||
error (_("Command can only be used when connected to the remote target."));
|
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)
|
if (use_threadinfo_query)
|
||||||
{
|
{
|
||||||
putpkt ("qfThreadInfo");
|
putpkt ("qfThreadInfo");
|
||||||
|
@ -2460,6 +2619,15 @@ remote_threads_extra_info (struct thread_info *tp)
|
||||||
server doesn't know about it. */
|
server doesn't know about it. */
|
||||||
return NULL;
|
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)
|
if (use_threadextra_query)
|
||||||
{
|
{
|
||||||
char *b = rs->buf;
|
char *b = rs->buf;
|
||||||
|
@ -3245,6 +3413,8 @@ static struct protocol_feature remote_protocol_features[] = {
|
||||||
PACKET_qXfer_spu_write },
|
PACKET_qXfer_spu_write },
|
||||||
{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
|
{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
|
||||||
PACKET_qXfer_osdata },
|
PACKET_qXfer_osdata },
|
||||||
|
{ "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
|
||||||
|
PACKET_qXfer_threads },
|
||||||
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
|
{ "QPassSignals", PACKET_DISABLE, remote_supported_packet,
|
||||||
PACKET_QPassSignals },
|
PACKET_QPassSignals },
|
||||||
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
|
{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
|
||||||
|
@ -4359,6 +4529,8 @@ struct stop_reply
|
||||||
|
|
||||||
int solibs_changed;
|
int solibs_changed;
|
||||||
int replay_event;
|
int replay_event;
|
||||||
|
|
||||||
|
int core;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The list of already fetched and acknowledged stop events. */
|
/* 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->replay_event = 0;
|
||||||
event->stopped_by_watchpoint_p = 0;
|
event->stopped_by_watchpoint_p = 0;
|
||||||
event->regcache = NULL;
|
event->regcache = NULL;
|
||||||
|
event->core = -1;
|
||||||
|
|
||||||
switch (buf[0])
|
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'
|
/* If this packet is an awatch packet, don't parse the 'a'
|
||||||
as a register number. */
|
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. */
|
/* Read the ``P'' register number. */
|
||||||
pnum = strtol (p, &p_temp, 16);
|
pnum = strtol (p, &p_temp, 16);
|
||||||
|
@ -4594,6 +4768,12 @@ Packet: '%s'\n"),
|
||||||
if (p_temp)
|
if (p_temp)
|
||||||
p = 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
|
else
|
||||||
{
|
{
|
||||||
/* Silently skip unknown optional info. */
|
/* Silently skip unknown optional info. */
|
||||||
|
@ -4803,6 +4983,7 @@ process_stop_reply (struct stop_reply *stop_reply,
|
||||||
struct target_waitstatus *status)
|
struct target_waitstatus *status)
|
||||||
{
|
{
|
||||||
ptid_t ptid;
|
ptid_t ptid;
|
||||||
|
struct thread_info *info;
|
||||||
|
|
||||||
*status = stop_reply->ws;
|
*status = stop_reply->ws;
|
||||||
ptid = stop_reply->ptid;
|
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_watch_data_address = stop_reply->watch_data_address;
|
||||||
|
|
||||||
remote_notice_new_inferior (ptid, 0);
|
remote_notice_new_inferior (ptid, 0);
|
||||||
|
demand_private_info (ptid)->core = stop_reply->core;
|
||||||
}
|
}
|
||||||
|
|
||||||
stop_reply_xfree (stop_reply);
|
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,
|
(ops, "osdata", annex, readbuf, offset, len,
|
||||||
&remote_protocol_packets[PACKET_qXfer_osdata]);
|
&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:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -9324,6 +9511,15 @@ remote_set_disconnected_tracing (int val)
|
||||||
error (_("Target does not support this command."));
|
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
|
static void
|
||||||
init_remote_ops (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_trace_find = remote_trace_find;
|
||||||
remote_ops.to_get_trace_state_variable_value = remote_get_trace_state_variable_value;
|
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_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
|
/* 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],
|
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
|
||||||
"qXfer:osdata:read", "osdata", 0);
|
"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],
|
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
|
||||||
"qXfer:siginfo:read", "read-siginfo-object", 0);
|
"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 ();
|
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
|
static void
|
||||||
debug_to_prepare_to_store (struct regcache *regcache)
|
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
|
/* Extra signal info. Usually the contents of `siginfo_t' on unix
|
||||||
platforms. */
|
platforms. */
|
||||||
TARGET_OBJECT_SIGNAL_INFO,
|
TARGET_OBJECT_SIGNAL_INFO,
|
||||||
|
/* The list of threads that are being debugged. */
|
||||||
|
TARGET_OBJECT_THREADS,
|
||||||
/* Possible future objects: TARGET_OBJECT_FILE, ... */
|
/* Possible future objects: TARGET_OBJECT_FILE, ... */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -651,6 +653,14 @@ struct target_ops
|
||||||
disconnection - set VAL to 1 to keep tracing, 0 to stop. */
|
disconnection - set VAL to 1 to keep tracing, 0 to stop. */
|
||||||
void (*to_set_disconnected_tracing) (int val);
|
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;
|
int to_magic;
|
||||||
/* Need sub-structure for target machine related rather than comm related?
|
/* 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); \
|
(*current_target.to_log_command) (p); \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
|
|
||||||
|
extern int target_core_of_thread (ptid_t ptid);
|
||||||
|
|
||||||
/* Routines for maintenance of the target structures...
|
/* Routines for maintenance of the target structures...
|
||||||
|
|
||||||
add_target: Add a target to the list of all possible targets.
|
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>
|
2010-01-12 Joel Brobecker <brobecker@adacore.com>
|
||||||
|
|
||||||
* gdb.base/maint.exp: Adjust the expected output for the
|
* gdb.base/maint.exp: Adjust the expected output for the
|
||||||
|
|
|
@ -1877,7 +1877,7 @@ proc mi_check_thread_states { xstates test } {
|
||||||
foreach s $states {
|
foreach s $states {
|
||||||
set pattern "${pattern}(.*)state=\"$s\""
|
set pattern "${pattern}(.*)state=\"$s\""
|
||||||
}
|
}
|
||||||
set pattern "$pattern\\\}\\\].*"
|
set pattern "${pattern}(,core=\"\[0-9\]*\")?\\\}\\\].*"
|
||||||
|
|
||||||
verbose -log "expecting: $pattern"
|
verbose -log "expecting: $pattern"
|
||||||
mi_gdb_test "-thread-info" $pattern $test
|
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);
|
clear_thread_inferior_resources (tp);
|
||||||
|
|
||||||
/* FIXME: do I ever need to call the back-end to give it a
|
|
||||||
chance at this private data before deleting the thread? */
|
|
||||||
if (tp->private)
|
if (tp->private)
|
||||||
xfree (tp->private);
|
{
|
||||||
|
if (tp->private_dtor)
|
||||||
|
tp->private_dtor (tp->private);
|
||||||
|
else
|
||||||
|
xfree (tp->private);
|
||||||
|
}
|
||||||
|
|
||||||
xfree (tp);
|
xfree (tp);
|
||||||
}
|
}
|
||||||
|
@ -468,8 +471,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
|
||||||
struct cleanup *cleanup_chain;
|
struct cleanup *cleanup_chain;
|
||||||
int current_thread = -1;
|
int current_thread = -1;
|
||||||
|
|
||||||
prune_threads ();
|
update_thread_list ();
|
||||||
target_find_new_threads ();
|
|
||||||
|
|
||||||
cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
|
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;
|
char *extra_info;
|
||||||
int current_thread = -1;
|
int current_thread = -1;
|
||||||
|
|
||||||
prune_threads ();
|
update_thread_list ();
|
||||||
target_find_new_threads ();
|
|
||||||
current_ptid = inferior_ptid;
|
current_ptid = inferior_ptid;
|
||||||
|
|
||||||
/* We'll be switching threads temporarily. */
|
/* 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)
|
for (tp = thread_list; tp; tp = tp->next)
|
||||||
{
|
{
|
||||||
struct cleanup *chain2;
|
struct cleanup *chain2;
|
||||||
|
int core;
|
||||||
|
|
||||||
if (requested_thread != -1 && tp->num != requested_thread)
|
if (requested_thread != -1 && tp->num != requested_thread)
|
||||||
continue;
|
continue;
|
||||||
|
@ -817,6 +819,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
|
||||||
ui_out_field_string (uiout, "state", state);
|
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);
|
do_cleanups (chain2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1058,8 +1064,7 @@ thread_apply_all_command (char *cmd, int from_tty)
|
||||||
if (cmd == NULL || *cmd == '\000')
|
if (cmd == NULL || *cmd == '\000')
|
||||||
error (_("Please specify a command following the thread ID list"));
|
error (_("Please specify a command following the thread ID list"));
|
||||||
|
|
||||||
prune_threads ();
|
update_thread_list ();
|
||||||
target_find_new_threads ();
|
|
||||||
|
|
||||||
old_chain = make_cleanup_restore_current_thread ();
|
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;
|
return GDB_RC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
update_thread_list (void)
|
||||||
|
{
|
||||||
|
prune_threads ();
|
||||||
|
target_find_new_threads ();
|
||||||
|
}
|
||||||
|
|
||||||
/* Commands with a prefix of `thread'. */
|
/* Commands with a prefix of `thread'. */
|
||||||
struct cmd_list_element *thread_cmd_list = NULL;
|
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
|
is always available. Stack/nested level 0 is reserved for the
|
||||||
top-level result. */
|
top-level result. */
|
||||||
|
|
||||||
enum { MAX_UI_OUT_LEVELS = 6 };
|
enum { MAX_UI_OUT_LEVELS = 8 };
|
||||||
|
|
||||||
struct ui_out_level
|
struct ui_out_level
|
||||||
{
|
{
|
||||||
|
|
|
@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
|
||||||
return argv;
|
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. */
|
/* Provide a prototype to silence -Wmissing-prototypes. */
|
||||||
extern initialize_file_ftype _initialize_utils;
|
extern initialize_file_ftype _initialize_utils;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue