From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 21707 invoked by alias); 17 Dec 2009 15:31:30 -0000 Received: (qmail 21281 invoked by uid 22791); 17 Dec 2009 15:31:22 -0000 X-SWARE-Spam-Status: No, hits=-0.5 required=5.0 tests=AWL,BAYES_50,SPF_PASS,ZMIde_GENERICSPAM1 X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (38.113.113.100) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 17 Dec 2009 15:31:13 +0000 Received: (qmail 15363 invoked from network); 17 Dec 2009 15:31:07 -0000 Received: from unknown (HELO orlando) (pedro@127.0.0.2) by mail.codesourcery.com with ESMTPA; 17 Dec 2009 15:31:07 -0000 From: Pedro Alves To: gdb-patches@sourceware.org Subject: Re: [MI] core awareness Date: Thu, 17 Dec 2009 15:31:00 -0000 User-Agent: KMail/1.9.10 Cc: Vladimir Prus References: <200912162352.38585.vladimir@codesourcery.com> In-Reply-To: <200912162352.38585.vladimir@codesourcery.com> MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Message-Id: <200912171531.06949.pedro@codesourcery.com> X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-12/txt/msg00231.txt.bz2 On Wednesday 16 December 2009 20:52:38, Vladimir Prus wrote: > The attached patch implements the core awareness, as discussed at: >=20 > =A0=A0=A0=A0=A0=A0=A0=A0http://thread.gmane.org/gmane.comp.gdb.devel/2746= 8/ >=20 > The most current spec can be found at: >=20 > =A0=A0=A0=A0=A0=A0=A0=A0http://article.gmane.org/gmane.comp.gdb.devel/275= 48 >=20 > To summarize, with this patch, when debugging against gdbserver: >=20 > * The -list-thread-groups command will report the cores that each > process runs on (with or without --available) > * It's possible to pass several thread groups to -list-thread-groups > * The *stopped output includes core number >=20 > For native debugging on Linux, roughly the same is also possible. >=20 > This is mostly MI change, but includes some remote protocol tweaks, > and documentation. I'd appreciate review of those parts. More like 80% not-MI changes. :-) >=20 > - Volodya > core-awareness.diff > commit 533abb7a66898cde158ea5556604ea20d56f2a47 > Author: Vladimir Prus > Date: =A0 Wed Dec 16 23:39:19 2009 +0300 >=20 > =A0 =A0 =A0=A0=A0=A0Implement core awareness. > =A0 =A0=20 > =A0 =A0 =A0=A0=A0=A0gdb/ > =A0 =A0 =A0 =A0 =A0 =A0 * bcache.c (compare_ints): Remove > =A0 =A0 =A0=A0=A0=A0(print_percentage): Use compare_positive_ints. Fishy indentation. > =A0 =A0=20 > =A0 =A0 =A0=A0=A0=A0* defs.h (compare_positive_ints): Declare. > =A0 =A0 =A0=A0=A0=A0* linux-nat.c (linux_nat_core_of_thread): New. > =A0 =A0 =A0=A0=A0=A0(linux_nat_add_target): Register the above. > =A0 =A0 =A0=A0=A0=A0* osdata.c (osdata_parsing_data): New fields > =A0 =A0 =A0=A0=A0=A0top_osdata and current_item. > =A0 =A0 =A0=A0=A0=A0(osdata_start_threads, osdata_end_threads): New. > =A0 =A0 =A0=A0=A0=A0(osdata_start_osdata): Init top_osdata. > =A0 =A0 =A0=A0=A0=A0(osdata_start_item): Record current item. > =A0 =A0 =A0=A0=A0=A0(osdata_children): New. > =A0 =A0 =A0=A0=A0=A0(item_children): Permit 'threads' child. > =A0 =A0 =A0=A0=A0=A0* osdata.h (osdata_item): New field 'children'. > =A0 =A0 =A0=A0=A0=A0* remote.c (struct private_thread_info): New. > =A0 =A0 =A0=A0=A0=A0(PACKET_qXfer_threads, use_osdata_threads): New. > =A0 =A0 =A0=A0=A0=A0(struct thread_item, threads_parsing_context > =A0 =A0 =A0=A0=A0=A0(start_thread, end_thread, thread_attributes) > =A0 =A0 =A0=A0=A0=A0(thread_children, threads_children, threads_elements)= : New. > =A0 =A0 =A0=A0=A0=A0(remote_threads_info): Try qXfer:threads before anyth= ing > =A0 =A0 =A0=A0=A0=A0else. > =A0 =A0 =A0=A0=A0=A0(remote_protocol_packets): Register qXfer:threads. > =A0 =A0 =A0=A0=A0=A0(remote_open_1): Init use_osdata_threads. > =A0 =A0 =A0=A0=A0=A0(struct stop_reply): New field 'core'. > =A0 =A0 =A0=A0=A0=A0(remote_parse_stop_reply): Parse core number. > =A0 =A0 =A0=A0=A0=A0(process_stop_reply): Record core number. > =A0 =A0 =A0=A0=A0=A0(remote_xfer_partial): Handle qXfer:threads. > =A0 =A0 =A0=A0=A0=A0(remote_core_of_thread): New. > =A0 =A0 =A0=A0=A0=A0(init_remote_ops): Register remote_core_of_thread. > =A0 =A0 =A0=A0=A0=A0(_initialize_remote): Register qXfer:read. > =A0 =A0 =A0=A0=A0=A0* target.c (target_core_of_thread): New > =A0 =A0 =A0=A0=A0=A0* target.h (enum target_object): New value TARGET_OBJ= ECT_THREADS. > =A0 =A0 =A0=A0=A0=A0(struct target_ops): New field to_core_of_threads. > =A0 =A0 =A0=A0=A0=A0(target_core_of_thread): Declare. > =A0 =A0 =A0=A0=A0=A0* thread.c (print_thread_info): Report the core. > =A0 =A0 =A0=A0=A0=A0* ui-out.c (MAX_UI_OUT_LEVELS): Increase. > =A0 =A0 =A0=A0=A0=A0* utils.c (compare_positive_ints): New. > =A0 =A0 =A0=A0=A0=A0* features/osdata.dtd: Allow nested threads. > =A0 =A0 =A0=A0=A0=A0* features/threads.dtd: New. > =A0 =A0 =A0=A0=A0=A0* mi/mi-interp.c (mi_on_normal_stop): Report the core. > =A0 =A0 =A0=A0=A0=A0* mi/mi-main.c (struct collect_cores_data, collect_co= res) > =A0 =A0 =A0=A0=A0=A0(print_one_inferior_data): New. > =A0 =A0 =A0=A0=A0=A0(print_one_inferior_data): Implementing printing of s= elected > =A0 =A0 =A0=A0=A0=A0inferiors. =A0Collect and print cores. > =A0 =A0 =A0=A0=A0=A0(output_cores): New. > =A0 =A0 =A0=A0=A0=A0(mi_cmd_list_thread_groups): Support --recurse. Permi= t specifying > =A0 =A0 =A0=A0=A0=A0thread groups together with --available. > =A0 =A0=20 > =A0 =A0 =A0=A0=A0=A0gdbserver/ > =A0 =A0 =A0=A0=A0=A0* linux-low.c (linux_core_of_thread): New. > =A0 =A0 =A0=A0=A0=A0(compare_ints, list_threads): New. > =A0 =A0 =A0=A0=A0=A0(linux_qxfer_osdata): Report threads and cores. > =A0 =A0 =A0=A0=A0=A0(linux_target_op): Register linux_core_of_thread. > =A0 =A0 =A0=A0=A0=A0* remote-utils.c (prepare_resume_reply): Report the c= ore. > =A0 =A0 =A0=A0=A0=A0* server.c (handle_threads_qxfer_proper, handle_threa= ds_qxfer): > =A0 =A0 =A0=A0=A0=A0New. > =A0 =A0 =A0=A0=A0=A0(handle_query): Handle qXfer:threads. =A0Announce ava= ilability > =A0 =A0 =A0=A0=A0=A0thereof. > =A0 =A0 =A0=A0=A0=A0* target.h (struct target_ops): New field core_for_th= reads. > =A0 =A0=20 > =A0 =A0 =A0=A0=A0=A0gdb/doc > =A0 =A0 =A0=A0=A0=A0* gdb.texinfo (GDB/MI Thread Information): New. > =A0 =A0 =A0=A0=A0=A0(GDB/MI Async Records): Document the core field in *s= topped. > =A0 =A0 =A0=A0=A0=A0(GDB/MI Miscellaneous Commands): Expand -list-thread-= groups > =A0 =A0 =A0=A0=A0=A0documentation > =A0 =A0 =A0=A0=A0=A0(Process list): Document that osdata document may con= tain > =A0 =A0 =A0=A0=A0=A0threads. > =A0 =A0 =A0=A0=A0=A0(Remote Serial Protocol): Document qXfer:threads. >=20 > diff --git a/gdb/Makefile.in b/gdb/Makefile.in > index 348d853..882be85 100644 > --- a/gdb/Makefile.in > +++ b/gdb/Makefile.in > @@ -444,7 +444,8 @@ RUNTESTFLAGS=3D > =A0 > =A0# XML files to build in to GDB. > =A0XMLFILES =3D $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinc= lude.dtd \ > -=A0=A0=A0=A0=A0=A0=A0$(srcdir)/features/library-list.dtd $(srcdir)/featu= res/osdata.dtd > +=A0=A0=A0=A0=A0=A0=A0$(srcdir)/features/library-list.dtd $(srcdir)/featu= res/osdata.dtd \ > +=A0=A0=A0=A0=A0=A0=A0$(srcdir)/features/threads.dtd > =A0 > =A0# This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX > =A0# interface to the serial port. =A0Hopefully if get ported to OS/2, VM= S, > diff --git a/gdb/bcache.c b/gdb/bcache.c > index d56df07..e467c5b 100644 > --- a/gdb/bcache.c > +++ b/gdb/bcache.c > @@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache) > =A0=0C > =A0/* Printing statistics. =A0*/ > =A0 > -static int > -compare_ints (const void *ap, const void *bp) > -{ > - =A0/* Because we know we're comparing two ints which are positive, > - =A0 =A0 there's no danger of overflow here. =A0*/ > - =A0return * (int *) ap - * (int *) bp; > -} > - > - > =A0static void > =A0print_percentage (int portion, int total) > =A0{ > @@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type) > =A0 > =A0 =A0 =A0/* To compute the median, we need the set of chain lengths sor= ted. =A0*/ > =A0 =A0 =A0qsort (chain_length, c->num_buckets, sizeof (chain_length[0]), > -=A0=A0=A0=A0=A0=A0=A0 =A0 compare_ints); > +=A0=A0=A0=A0=A0=A0=A0 =A0 compare_positive_ints); > =A0 =A0 =A0qsort (entry_size, c->unique_count, sizeof (entry_size[0]), > -=A0=A0=A0=A0=A0=A0=A0 =A0 compare_ints); > +=A0=A0=A0=A0=A0=A0=A0 =A0 compare_positive_ints); > =A0 > =A0 =A0 =A0if (c->num_buckets > 0) > =A0 =A0 =A0 =A0{ > diff --git a/gdb/defs.h b/gdb/defs.h > index b944ffb..396e9a8 100644 > --- a/gdb/defs.h > +++ b/gdb/defs.h > @@ -417,6 +417,8 @@ char *ldirname (const char *filename); > =A0 > =A0char **gdb_buildargv (const char *); > =A0 > +int compare_positive_ints (const void *ap, const void *bp); > + > =A0/* From demangle.c */ > =A0 > =A0extern void set_demangling_style (char *); > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo > index e880838..d1d5075 100644 > --- a/gdb/doc/gdb.texinfo > +++ b/gdb/doc/gdb.texinfo > @@ -15402,6 +15402,10 @@ are: > =A0@tab @code{qXfer:siginfo:write} > =A0@tab @code{set $_siginfo} > =A0 > +@item @code{threads} > +@tab @code{qXfer:threads:read} > +@tab @code{info threads} > + > =A0@item @code{get-thread-local-@*storage-address} > =A0@tab @code{qGetTLSAddr} > =A0@tab Displaying @code{__thread} variables > @@ -21688,6 +21692,7 @@ follow development on @email{gdb@@sourceware.org}= and > =A0* GDB/MI Stream Records:: > =A0* GDB/MI Async Records:: > =A0* GDB/MI Frame Information:: > +* GDB/MI Thread Information:: > =A0@end menu > =A0 > =A0@node GDB/MI Result Records > @@ -21780,7 +21785,7 @@ several times, either for different threads, beca= use it cannot resume > =A0all threads together, or even for a single thread, if the thread must > =A0be stepped though some code before letting it run freely. > =A0 > -@item *stopped,reason=3D"@var{reason}",thread-id=3D"@var{id}",stopped-th= reads=3D"@var{stopped}" > +@item *stopped,reason=3D"@var{reason}",thread-id=3D"@var{id}",stopped-th= reads=3D"@var{stopped}",core=3D"@var{core}" > =A0The target has stopped. =A0The @var{reason} field can have one of the > =A0following values: > =A0 > @@ -21820,7 +21825,9 @@ If all threads are stopped, the @var{stopped} fie= ld will have the > =A0value of @code{"all"}. =A0Otherwise, the value of the @var{stopped} > =A0field will be a list of thread identifiers. =A0Presently, this list wi= ll > =A0always include a single thread, but frontend should be prepared to see > -several threads in the list. > +several threads in the list. =A0The @var{core} field reports on which > +processor core the stop event has happened. =A0This field may be absent > +if such information is not available. > =A0 > =A0@item =3Dthread-group-created,id=3D"@var{id}" > =A0@itemx =3Dthread-group-exited,id=3D"@var{id}" > @@ -21897,6 +21904,34 @@ corresponds to the frame's code address. =A0This= field may be absent. > =A0 > =A0@end table > =A0 > +@node GDB/MI Thread Information > +@subsection @sc{gdb/mi} Thread Information > + > +Whenver @value{GDBN} has to report an information about a thread, it ^ typo: Whenever > +uses a tuple with the following fields: > + > +@table @code > +@item id > +The numeric id assigned to the thread by @value{GDBN}. This field is > +always present. > + > +@item target-id > +Target-specific string identifying the thread. =A0This field is always p= resent. > + > +@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. =A0This field is always present. > + > +@item cores Typo: "core"? > +The value of this field is an integer number of the processor core the > +thread was last seen on. =A0This field is optional. > +@end table > + > =A0 > =A0@c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%= %%%% > =A0@node GDB/MI Simple Examples > @@ -26202,20 +26237,83 @@ while the target is running. > =A0@subheading Synopsis > =A0 > =A0@smallexample > --list-thread-groups [ --available ] [ @var{group} ] > +-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ] > =A0@end smallexample > =A0 > -When used without the @var{group} parameter, lists top-level thread > -groups that are being debugged. =A0When used with the @var{group} > -parameter, the children of the specified group are listed. =A0The > -children can be either threads, or other groups. =A0At present, > -@value{GDBN} will not report both threads and groups as children at > -the same time, but it may change in future. > +Lists thread groups (@pxref{Thread groups}). When a single thread Double space after period. Here and elsewhere. > +group is passed as the argument, lists the children specified one. > +When several thread group are passed, lists information about those > +thread groups. =A0Without any parameters, lists information about all > +top-level thread groups. > + > +Normally, thread groups that are being debugged are reported. > +With the @samp{--available} option, @value{GDBN} reports thread groups > +available on the target. > + > +The output of this command may have either a @samp{threads} result or > +a @samp{groups} result. The @samp{thread} result has a list of tuples > +as value, with each tuple describing a thread (@pxref{GDB/MI Thread > +Information}). The @samp{groups} result has a list of tuples as value, > +each tuple describing a thread group. =A0If top-level groups are > +requested (that is, no parameter is passed), or when several groups > +are passed, the output always have a @samp{groups} result. The format > +of the @samp{group} result is described below. > + > +To reduce the number of roundtrips it's possible to list thread groups > +together with their children, by passing the @samp{--recurse} option > +and the recursion depth. Presently, only recursion depth of 1 is > +permitted. If this option is present, then every reported thread group > +will also include its children, either as @samp{group} or > +@samp{threads} field. > + > +In general, any combination of option and parameters is permitted, with > +the following caveats: > + > +@itemize @bullet > +@item=20 > +When a single thread group is passed, the output will typically > +be the @samp{threads} result. =A0Because 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. =A0In particular, the list of threads of a process might > +be inaccessible. =A0Further, 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. =A0This 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. =A0This field is only present > +for thread groups of type @samp{process}. > + > +@item num_children > +The number of children this thread group has. =A0This 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. =A0It may be present=20 Looks like an unfinished sentence. > =A0 > -With the @samp{--available} option, instead of reporting groups that > -are been debugged, GDB will report all thread groups available on the > -target. =A0Using the @samp{--available} option together with @var{group} > -is not allowed. > +@item cores > +This field is a list of integers, each identifying a core that one > +thread of the group is running on. =A0This field may be absent if > +such information is not available. > + > +@end table > =A0 > =A0@subheading Example > =A0 > @@ -26229,6 +26327,16 @@ is not allowed. > =A0@{id=3D"1",target-id=3D"Thread 0xb7e156b0 (LWP 21254)", > =A0 =A0 frame=3D@{level=3D"0",addr=3D"0x0804891f",func=3D"foo",args=3D[@{= name=3D"i",value=3D"10"@}], > =A0 =A0 =A0 =A0 =A0 =A0 file=3D"/tmp/a.c",fullname=3D"/tmp/a.c",line=3D"1= 58"@},state=3D"running"@}]] > +-list-thread-groups --available > +^done,groups=3D[@{id=3D"17",type=3D"process",pid=3D"yyy",num_children=3D= "2",cores=3D[1,2]@}] > +-list-thread-groups --available --recurse 1 > + ^done,groups=3D[@{id=3D"17", types=3D"process",pid=3D"yyy",num_children= =3D"2",cores=3D[1,2], > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0threads=3D[@{id=3D"1",target-id=3D"Threa= d 0xb7e14b90",cores=3D[1]@}, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 @{id=3D"2",target-id=3D= "Thread 0xb7e14b90",cores=3D[2]@}]@},..] > +-list-thread-groups --available --recurse 1 17 18 > +^done,groups=3D[@{id=3D"17", types=3D"process",pid=3D"yyy",num_children= =3D"2",cores=3D[1,2], > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 threads=3D[@{id=3D"1",target-id=3D"Thread 0= xb7e14b90",cores=3D[1]@}, > + =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0@{id=3D"2",target-id=3D"= Thread 0xb7e14b90",cores=3D[2]@}]@},...] > =A0@end smallexample > =A0 > =A0@subheading The @code{-interpreter-exec} Command > @@ -27978,6 +28086,7 @@ Show the current setting of the target wait timeo= ut. > =A0* File-I/O Remote Protocol Extension:: > =A0* Library List Format:: > =A0* Memory Map Format:: > +* Thread List Format:: > =A0@end menu > =A0 > =A0@node Overview > @@ -28878,6 +28987,10 @@ If @var{n} is @samp{thread}, then @var{r} is the= @var{thread-id} of > =A0the stopped thread, as specified in @ref{thread-id syntax}. > =A0 > =A0@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 > =A0If @var{n} is a recognized @dfn{stop reason}, it describes a more > =A0specific event that stopped the target. =A0The currently defined stop > =A0reasons are listed below. =A0@var{aa} should be @samp{05}, the trap > @@ -28911,8 +29024,6 @@ logged execution events, because it has reached t= he end (or the > =A0beginning when executing backward) of the log. =A0The value of @var{r} > =A0will be either @samp{begin} or @samp{end}. =A0@xref{Reverse Execution}= ,=20 > =A0for more information. > - > - > =A0@end table > =A0 > =A0@item W @var{AA} > @@ -29452,6 +29563,12 @@ These are the currently defined stub features an= d their properties: > =A0@tab @samp{-} > =A0@tab Yes > =A0 > +@item @samp{qXfer:threads:read} > +@tab No > +@tab @samp{-} > +@tab Yes > + > + > =A0@item @samp{QNonStop} > =A0@tab No > =A0@tab @samp{-} > @@ -29535,6 +29652,10 @@ The remote stub understands the @samp{qXfer:sigi= nfo:read} packet > =A0The remote stub understands the @samp{qXfer:siginfo:write} packet > =A0(@pxref{qXfer siginfo write}). > =A0 > +@item qXfer:threads:read > +The remote stub understands the @samp{qXfer:threads:read} packet > +(@pxref{qXfer threads read}). > + > =A0@item QNonStop > =A0The remote stub understands the @samp{QNonStop} packet > =A0(@pxref{QNonStop}). > @@ -29727,6 +29848,15 @@ This packet is not probed by default; the remote= stub must request it, > =A0by supplying an appropriate @samp{qSupported} response > =A0(@pxref{qSupported}). > =A0 > +@item qXfer:threads:read::@var{offset},@var{length} > +@anchor{qXfer threads read} > +Access the list of threads on target. =A0@xref{Thread List Format}. =A0T= he > +annex part of the generic @samp{qXfer} packet must be empty > +(@pxref{qXfer read}). > + > +This packet is not probed by default; the remote stub must request it, What's the advantage of this, rather than probing it? I would see it nice for GDB to know in advance that the target can report core data, but that's not that reporting support for this packet means, since the "core" field is optional. > +by supplying an appropriate @samp{qSupported} response (@pxref{qSupporte= d}). > + > =A0@item qXfer:osdata:read::@var{offset},@var{length} > =A0@anchor{qXfer osdata read} > =A0Access the target's @dfn{operating system information}. =A0 > @@ -31710,6 +31840,32 @@ The formal DTD for memory map format is given be= low: > =A0 > =A0@end smallexample > =A0 > +@node Thread List Format > +@section Thread List Format > +@cindex thread list format > + > +To efficiently update the list of threads and their attributes, > +@value{GDBN} gdb requests, using the @samp{qXfer:threads:read} > +package (@pxref{qXfer threads read}) and XML document with the > +format described below. > + > +The top-level structure of the document is shown below: > + > +@smallexample > + > + > + =A0 =A0 > + =A0 =A0some details > + =A0 =A0 =A0 =A0=20 > + s/memory-map/threads "some details" ? > +@end smallexample > + > +Each @samp{thread} element must have the @samp{id} attribute that > +identifies the thread (@pxref{thread-id syntax}). The > +@samp{core} attribute, if present, specifies which process core s/process core/processor core/ ? > +the thread was last executing on. =A0The content of the of @samp{thread} > +element is interpreted as hunam-readable auxilliary information. typo: hunam > + > =A0@include agentexpr.texi > =A0 > =A0@node Target Descriptions > @@ -32269,15 +32425,34 @@ An example document is: > =A0 =A0 =A01 > =A0 =A0 =A0root > =A0 =A0 =A0/sbin/init > + =A0 =A01,2,3 > + =A0 =A0 > + =A0 =A0 =A0 > + =A0 =A0 =A012 > + =A0 =A0 =A03 > + =A0 =A0 =A0 > + =A0 =A0 > =A0 =A0 > =A0 > =A0@end smallexample > =A0 > -Each item should include a column whose name is @samp{pid}. =A0The value > +The document has an @samp{osdata} element that contains a number of > +@samp{item} elements describing processes. Each process @samp{item} > +may contain @samp{threads} element with items describing threads of > +that process. Both process and thread items contain a number of > +@samp{column} elements with various information. > + > +Each process item should include a column whose name is @samp{pid}. =A0T= he value > =A0of that column should identify the process on the target. =A0The > =A0@samp{user} and @samp{command} columns are optional, and will be > -displayed by @value{GDBN}. =A0Target may provide additional columns, > -which @value{GDBN} currently ignores. > +displayed by @value{GDBN}. =A0The @samp{cores} column, if present, > +should contain a comma-separated list of cores that this process > +is running on. > +Each thread item should include a column whose name is @samp{tid}. > +The value of that is a thread identifier, specific to the operating > +system. > The @samp{core} column, if present, is the integer number of=20 > +the core the thread is running on. > =A0 > =A0@include gpl.texi > =A0 > diff --git a/gdb/features/osdata.dtd b/gdb/features/osdata.dtd > index a04506b..67de837 100644 > --- a/gdb/features/osdata.dtd > +++ b/gdb/features/osdata.dtd > @@ -10,7 +10,10 @@ > =A0 > =A0 > =A0 > - > + > =A0 > =A0 > =A0 > + > + > + This dtd change isn't right. "osdata" isn't specific to processes, it is just a generic table. A "threads" element doesn't fit in, it wouldn't make sense for anything other than listing processes. Two alternative solutions: - Allow multiple sub-table elements in the osdata schema, and allow a "name" attribute on this element. In the listing processes case, you'd have a sub-table whose type/name attribute would be "threads". You should change info_osdata_command to descend into subtables somehow. - Don't nest "threads" within processes osdata. Treat it as a different request / table (e.g., ). That is, on the CLI, "info os processes" works as is, unmodified, and, you'd add support for "info os threads". No schema changes are necessary with this option. At the MI implementation level, you'd do two requests to get the same data: get_osdata("processes"), and get_osdata("threads"). > diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd > new file mode 100644 > index 0000000..e7c3a11 > --- /dev/null > +++ b/gdb/features/threads.dtd > @@ -0,0 +1,14 @@ > + > + > + > + > + > + > + > + > + > + > diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c > index 45dd9f7..d1d853d 100644 > --- a/gdb/gdbserver/linux-low.c > +++ b/gdb/gdbserver/linux-low.c > @@ -137,6 +137,7 @@ static void *add_lwp (ptid_t ptid); > =A0static int my_waitpid (int pid, int *status, int flags); > =A0static int linux_stopped_by_watchpoint (void); > =A0static void mark_lwp_dead (struct lwp_info *lwp, int wstat); > +static int linux_core_of_thread (ptid_t ptid); > =A0 > =A0struct pending_signals > =A0{ > @@ -2789,6 +2790,111 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR = *data_p) > =A0#endif > =A0 > =A0static int > +compare_ints (const void *xa, const void *xb) > +{ > + =A0int a =3D *(const int *)xa; > + =A0int b =3D *(const int *)xb; > + > + =A0return a - b; > +} > + > +/* Given PID, print information about every thread in that process to BU= FFER. > + =A0 Set *CORES to a string with a comma-separated list of cores that th= reads > + =A0 of PID execute on. =A0The string must be freed by the caller. =A0BU= FFER must > + =A0 be initialized. If no cores are found, *CORES will be set to NULL. = */ > +static void > +list_threads (int pid, struct buffer *buffer, char **cores) > +{ > + =A0int count =3D 0; > + =A0int allocated =3D 10; > + =A0int *core_numbers =3D xmalloc (sizeof (int) * allocated); > + =A0char pathname[128]; > + =A0DIR *dir; > + =A0struct dirent *dp; > + =A0struct stat statbuf; > + > + =A0sprintf (pathname, "/proc/%d/task", pid); > + =A0if (stat (pathname, &statbuf) =3D=3D 0 && S_ISDIR (statbuf.st_mode)) > + =A0 =A0{ > + =A0 =A0 =A0dir =3D opendir (pathname); > + =A0 =A0 =A0if (!dir) > +=A0=A0=A0=A0=A0=A0=A0return; > + > + =A0 =A0 =A0buffer_xml_printf (buffer, ""); > + =A0 =A0 =A0while ((dp =3D readdir (dir)) !=3D NULL) > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0unsigned long lwp =3D strtoul (dp->d_name, NULL= , 10); > +=A0=A0=A0=A0=A0=A0=A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0 =A0if (lwp !=3D 0) > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0unsigned core =3D linux_core_of_thread = (ptid_build (pid, lwp, 0)); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0if (core !=3D -1) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0char s[11]; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0sprintf (s, "%u", core); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0if (count =3D=3D alloca= ted)=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0core_numbers =3D re= alloc (core_numbers,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0sizeof (int ) * (alloc= ated *=3D 2)); No space after int. Please move the 'allocated *=3D 2' in its own statement. My GM hat tells me that I should point out that there are a bunch of hardcoded buffer sizes in the patch, that the GNU conventions tells us we should avoid. > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0core_numbers[count++] = =3D core; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0buffer_xml_printf (buff= er, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 "" > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 "%s" > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 "%s" > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 "", dp->d_name, s); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0} > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0else > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0buffer_xml_printf (buff= er, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 "" > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 "%s" > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 "", dp->d_name); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0} > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0}=A0=A0=A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0} > + =A0 =A0 =A0buffer_xml_printf (buffer, ""); > + =A0 =A0} > + > + =A0*cores =3D NULL; > + =A0if (count > 0) > + =A0 =A0{ > + =A0 =A0 =A0struct buffer buffer2; > + =A0 =A0 =A0int *b; > + =A0 =A0 =A0int *e; > + =A0 =A0 =A0qsort (core_numbers, count, sizeof (int), compare_ints); > + > + =A0 =A0 =A0/* Remove duplicates. */ > + =A0 =A0 =A0b =3D core_numbers; > + =A0 =A0 =A0e =3D core_numbers + count; > + > + =A0 =A0 =A0while (b !=3D e) { > +=A0=A0=A0=A0=A0=A0=A0if ((b + 1 < e) && (*b =3D=3D *(b+1))) > +=A0=A0=A0=A0=A0=A0=A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0*(b + 1) =3D *(e - 1); > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0--e;=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0 =A0} > +=A0=A0=A0=A0=A0=A0=A0else > +=A0=A0=A0=A0=A0=A0=A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0++b; > +=A0=A0=A0=A0=A0=A0=A0 =A0}=A0=A0=A0=A0=A0 =A0 > + =A0 =A0 =A0} > + > + =A0 =A0 =A0buffer_init (&buffer2); > + > + =A0 =A0 =A0for (b =3D core_numbers; b !=3D e; ++b) { '{' goes on its own line. > +=A0=A0=A0=A0=A0=A0=A0char number[11]; > +=A0=A0=A0=A0=A0=A0=A0sprintf (number, "%u", *b); > +=A0=A0=A0=A0=A0=A0=A0buffer_xml_printf (&buffer2, "%s%s",=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 (b =3D=3D core_numbers) ? "" : ",", number); > + =A0 =A0 =A0} > + =A0 =A0 =A0buffer_grow_str0 (&buffer2, ""); There's a bug here somewhere in the duplicates detection that we talked about off-list, that I know you've fixed already. :-) > + =A0 =A0 =A0 > + =A0 =A0 =A0*cores =3D buffer_finish (&buffer2); > + =A0 =A0} =A0 > +} > + > +static int > =A0linux_qxfer_osdata (const char *annex, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0unsigned char *re= adbuf, unsigned const char *writebuf, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0CORE_ADDR offset,= int len) > @@ -2837,15 +2943,20 @@ linux_qxfer_osdata (const char *annex, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 FILE *f; > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 char cmd[MAXPATHLEN + 1]; > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 struct passwd *entry; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 int pid; > =A0 > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 sprintf (pathname, "/pro= c/%s/cmdline", dp->d_name); > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 entry =3D getpwuid (stat= buf.st_uid); > =A0 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 pid =3D (int)strtoul (dp->= d_name, NULL, 10); Space after (int). > + > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 if ((f =3D fopen (pathna= me, "r")) !=3D NULL) > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 { > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 size_t len =3D f= read (cmd, 1, sizeof (cmd) - 1, f); > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 if (len > 0) > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0 { > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 st= ruct buffer thread_buffer; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 ch= ar *cores =3D 0;=A0=A0=A0=A0=A0=A0=A0=20 > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = int i; > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = for (i =3D 0; i < len; i++) > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 if (cmd[i] =3D=3D '\0') > @@ -2857,11 +2968,29 @@ linux_qxfer_osdata (const char *annex, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 "" > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 "%s" > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 "%s" > -=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 "%s" > -=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 "", > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 "%s", > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 dp->d_name, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 entry ? entry->pw_name : "?", > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 cmd); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 bu= ffer_init (&thread_buffer); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 li= st_threads (pid, &thread_buffer, &cores); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 if= (cores) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 { > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 buffer_xml_printf ( > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 =A0 =A0 &buffer,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 "%s", cores); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 free (cores); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 } > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 bu= ffer_grow (&buffer,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0thread_buffer.buffer,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0thread_buffer.used_size); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 fr= ee (buffer_finish (&thread_buffer)); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 bu= ffer_xml_printf (&buffer, ""); Any chance linux-nat.c also gets support for this (however it ends up looking like)? > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0 } > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 fclose (f); > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 } > @@ -3139,6 +3268,53 @@ linux_qxfer_spu (const char *annex, unsigned char = *readbuf, > =A0 =A0return ret; > =A0} > =A0 > +static int > +linux_core_of_thread (ptid_t ptid) > +{ > + =A0char filename[sizeof ("/proc//task//stat")=20 s,//,/ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 + 2*20 /* decimal digits f= or 2 numbers, max 2^64 bit each */ Spaces around '*'. > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 + 1]; > + =A0FILE *f; > + =A0char *content =3D NULL; > + =A0char *p; > + =A0char *ts =3D 0; > + =A0int content_read =3D 0; > + =A0int i; > + =A0int core; > + =A0 > + =A0sprintf (filename, "/proc/%d/task/%ld/stat",=20 > +=A0=A0=A0=A0=A0=A0=A0 =A0 ptid_get_pid (ptid), ptid_get_lwp (ptid)); > + =A0f =3D fopen (filename, "r"); > + Should check if file opened successfully. Older kernels don't have /proc//task available --- see "man proc". > + =A0for (;;) > + =A0 =A0{ > + =A0 =A0 =A0int n; > + =A0 =A0 =A0content =3D realloc (content, content_read + 1024); > + =A0 =A0 =A0n =3D fread (content + content_read, 1, 1024, f); > + =A0 =A0 =A0content_read +=3D n; > + =A0 =A0 =A0if (n < 1024) > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0content[content_read] =3D '\0'; > +=A0=A0=A0=A0=A0=A0=A0 =A0break; > +=A0=A0=A0=A0=A0=A0=A0}=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0 > + =A0 =A0} > + > + =A0p =3D strchr (content, '('); > + =A0p =3D strchr (p, ')') + 2; /* skip ")" and a whitespace. */ > + =A0 =A0 > + =A0p =3D strtok_r (p, " ", &ts); > + =A0for (i =3D 0; i !=3D 36; ++i) > + =A0 =A0p =3D strtok_r (NULL, " ", &ts); > + > + =A0if (sscanf (p, "%d", &core) =3D=3D 0) > + =A0 =A0core =3D -1; > + =A0 > + =A0free (content); > + =A0fclose (f); > + > + =A0return core; > +} > + > =A0static struct target_ops linux_target_ops =3D { > =A0 =A0linux_create_inferior, > =A0 =A0linux_attach, > @@ -3178,10 +3354,11 @@ static struct target_ops linux_target_ops =3D { > =A0 =A0linux_start_non_stop, > =A0 =A0linux_supports_multi_process, > =A0#ifdef USE_THREAD_DB > - =A0thread_db_handle_monitor_command > + =A0thread_db_handle_monitor_command, > =A0#else > - =A0NULL > + =A0NULL, > =A0#endif > + =A0linux_core_of_thread > =A0}; > =A0 > =A0static void > diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c > index 158b653..97c076d 100644 > --- a/gdb/gdbserver/remote-utils.c > +++ b/gdb/gdbserver/remote-utils.c > @@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid, > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0 gdbserver to know what inferior_ptid= is. =A0*/ > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0if (1 || !ptid_equal (general_thread, pti= d)) > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0int core =3D -1; > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0/* In non-stop, don't cha= nge the general thread behind > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 GDB's back. =A0*/ > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0if (!non_stop) > @@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0buf =3D write_ptid (buf, = ptid); > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0strcat (buf, ";"); > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0buf +=3D strlen (buf); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0if (the_target->core_for_th= read) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0core =3D (*the_target->= core_for_thread) (ptid); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0if (core !=3D -1) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0sprintf (buf, "core= :"); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0buf +=3D strlen (bu= f); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0sprintf (buf, "%x",= core); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0strcat (buf, ";"); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0buf +=3D strlen (bu= f); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0} > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0} > =A0=A0=A0=A0=A0=A0=A0=A0 =A0} > =A0 > diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c > index 14bc7e7..62e3e3d 100644 > --- a/gdb/gdbserver/server.c > +++ b/gdb/gdbserver/server.c > @@ -707,6 +707,87 @@ handle_monitor_command (char *mon) > =A0 =A0 =A0} > =A0} > =A0 > +static void > +handle_threads_qxfer_proper (struct buffer *buffer) > +{ > + =A0struct inferior_list_entry *thread; > + > + =A0buffer_grow_str (buffer, "\n"); > + > + =A0for (thread =3D all_threads.head; thread; thread =3D thread->next) > + =A0 =A0{ > + =A0 =A0 =A0ptid_t ptid =3D thread_to_gdb_id ((struct thread_info *)thre= ad); > + =A0 =A0 =A0char ptid_s[100]; > + =A0 =A0 =A0int core =3D -1; > + =A0 =A0 =A0char core_s[21]; > + > + =A0 =A0 =A0write_ptid (ptid_s, ptid); > + > + =A0 =A0 =A0if (the_target->core_for_thread) > +=A0=A0=A0=A0=A0=A0=A0core =3D (*the_target->core_for_thread) (ptid); > + > + =A0 =A0 =A0if (core !=3D -1)=20 > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0sprintf (core_s, "%d", core); > +=A0=A0=A0=A0=A0=A0=A0 =A0buffer_xml_printf (buffer, "\n",=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 ptid_s, core_s); > +=A0=A0=A0=A0=A0=A0=A0} > + =A0 =A0 =A0else > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0buffer_xml_printf (buffer, "\n", =A0 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 ptid_s); > +=A0=A0=A0=A0=A0=A0=A0} > + =A0 =A0} > + > + =A0buffer_grow_str0 (buffer, "\n"); > +} > + > +static int > +handle_threads_qxfer (const char *annex, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0unsigned char *= readbuf, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0CORE_ADDR offse= t, int length) > +{ > + =A0static char *result =3D 0; > + =A0static unsigned int result_length =3D 0; > + > + =A0if (annex && strcmp (annex, "") !=3D 0) > + =A0 =A0return 0; > + > + =A0if (offset =3D=3D 0) > + =A0 =A0{ > + =A0 =A0 =A0struct buffer buffer; > + =A0 =A0 =A0/* When asked for data at offset 0, generate everything and = store into > +=A0=A0=A0=A0=A0=A0=A0 'result'. Successive reads will be served off 'res= ult'. =A0*/ > + =A0 =A0 =A0if (result) > +=A0=A0=A0=A0=A0=A0=A0free (result); > + =A0 =A0 =A0 > + =A0 =A0 =A0buffer_init (&buffer); > + > + =A0 =A0 =A0handle_threads_qxfer_proper (&buffer); > + =A0 =A0 =A0 > + =A0 =A0 =A0result =3D buffer_finish (&buffer); > + =A0 =A0 =A0result_length =3D strlen (result); > + =A0 =A0 =A0buffer_free (&buffer);=A0=A0=A0 =A0 > + =A0 =A0} > + =A0 > + =A0if (offset >=3D result_length) > + =A0 =A0{ > + =A0 =A0 =A0/* We're out of data. =A0*/ > + =A0 =A0 =A0free (result); > + =A0 =A0 =A0result =3D NULL; > + =A0 =A0 =A0result_length =3D 0; > + =A0 =A0 =A0return 0; > + =A0 =A0} > + =A0 > + =A0if (length > result_length - offset) > + =A0 =A0length =3D result_length - offset; > + =A0 > + =A0memcpy (readbuf, result + offset, length); > + > + =A0return length; > + > +} > + > =A0/* Handle all of the extended 'q' packets. =A0*/ > =A0void > =A0handle_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) > =A0 =A0 =A0 =A0return; > =A0 =A0 =A0} > =A0 > + =A0if (strncmp ("qXfer:threads:read:", own_buf, 19) =3D=3D 0) > + =A0 =A0{ > + =A0 =A0 =A0unsigned char *data; > + =A0 =A0 =A0int n; > + =A0 =A0 =A0CORE_ADDR ofs; > + =A0 =A0 =A0unsigned int len; > + =A0 =A0 =A0char *annex; > + > + =A0 =A0 =A0require_running (own_buf); > + > + =A0 =A0 =A0/* Reject any annex; grab the offset and length. =A0*/ > + =A0 =A0 =A0if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0 > +=A0=A0=A0=A0=A0=A0=A0 =A0|| annex[0] !=3D '\0') > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0strcpy (own_buf, "E00"); > +=A0=A0=A0=A0=A0=A0=A0 =A0return; > +=A0=A0=A0=A0=A0=A0=A0} > + > + =A0 =A0 =A0/* Read one extra byte, as an indicator of whether there is > +=A0=A0=A0=A0=A0=A0=A0 more. =A0*/ > + =A0 =A0 =A0if (len > PBUFSIZ - 2) > +=A0=A0=A0=A0=A0=A0=A0len =3D PBUFSIZ - 2; > + =A0 =A0 =A0data =3D malloc (len + 1); > + =A0 =A0 =A0if (!data) > +=A0=A0=A0=A0=A0=A0=A0return; > + =A0 =A0 =A0n =3D handle_threads_qxfer (annex, data, ofs, len + 1); > + =A0 =A0 =A0if (n < 0) > +=A0=A0=A0=A0=A0=A0=A0write_enn (own_buf); > + =A0 =A0 =A0else if (n > len) > +=A0=A0=A0=A0=A0=A0=A0*new_packet_len_p =3D write_qxfer_response (own_buf= , data, len, 1); > + =A0 =A0 =A0else > +=A0=A0=A0=A0=A0=A0=A0*new_packet_len_p =3D write_qxfer_response (own_buf= , data, n, 0); > + > + =A0 =A0 =A0free (data); > + =A0 =A0 =A0return; =A0 =A0=20 > + =A0 =A0} > + > =A0 =A0/* Protocol features query. =A0*/ > =A0 =A0if (strncmp ("qSupported", own_buf, 10) =3D=3D 0 > =A0 =A0 =A0 =A0&& (own_buf[10] =3D=3D ':' || own_buf[10] =3D=3D '\0')) > @@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *n= ew_packet_len_p) > =A0 =A0 =A0 =A0if (target_supports_non_stop ()) > =A0=A0=A0=A0=A0=A0=A0=A0strcat (own_buf, ";QNonStop+"); > =A0 > + =A0 =A0 =A0strcat (own_buf, ";qXfer:threads:read+"); > + > =A0 =A0 =A0 =A0return; > =A0 =A0 =A0} > =A0 > diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h > index 0e4b127..8e8778a 100644 > --- a/gdb/gdbserver/target.h > +++ b/gdb/gdbserver/target.h > @@ -283,6 +283,9 @@ struct target_ops > =A0 =A0/* If not NULL, target-specific routine to process monitor command. > =A0 =A0 =A0 Returns 1 if handled, or 0 to perform default processing. =A0= */ > =A0 =A0int (*handle_monitor_command) (char *); > + > + =A0/* Returns the core given a thread, or -1 if not known. =A0*/ > + =A0int (*core_for_thread) (ptid_t); > =A0}; > =A0 > =A0extern struct target_ops *the_target; > diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h > index 2f388fb..bd0044b 100644 > --- a/gdb/gdbthread.h > +++ b/gdb/gdbthread.h > @@ -346,4 +346,6 @@ extern struct cleanup *make_cleanup_restore_current_t= hread (void); > =A0 =A0 INFERIOR_PTID. =A0INFERIOR_PTID *must* be in the thread list. =A0= */ > =A0extern struct thread_info* inferior_thread (void); > =A0 > +extern void update_thread_list (void); > + > =A0#endif /* GDBTHREAD_H */ > diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c > index c0afecd..b232caf 100644 > --- a/gdb/linux-nat.c > +++ b/gdb/linux-nat.c > @@ -5421,6 +5421,59 @@ linux_nat_thread_address_space (struct target_ops = *t, ptid_t ptid) > =A0 =A0return inf->aspace; > =A0} > =A0 > +/* Return the core for a thread. =A0*/ > +static int > +linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid) > +{ > + =A0struct cleanup *back_to; > + =A0char *filename; > + =A0FILE *f; > + =A0char *content =3D NULL; > + =A0char *p; > + =A0char *ts =3D 0; > + =A0int content_read =3D 0; > + =A0int i; > + =A0int core; > + > + =A0filename =3D xstrprintf ("/proc/%d/task/%ld/stat", > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 GE= T_PID (ptid), GET_LWP (ptid)); > + =A0back_to =3D make_cleanup (xfree, filename); > + > + =A0f =3D fopen (filename, "r"); > + =A0make_cleanup_fclose (f); Should check if file opened successfully. Older kernels don't have /proc//task available --- see "man proc". > + > + =A0for (;;) > + =A0 =A0{ > + =A0 =A0 =A0int n; > + =A0 =A0 =A0content =3D xrealloc (content, content_read + 1024); > + =A0 =A0 =A0n =3D fread (content + content_read, 1, 1024, f); > + =A0 =A0 =A0content_read +=3D n; > + =A0 =A0 =A0if (n < 1024) > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0content[content_read] =3D '\0'; > +=A0=A0=A0=A0=A0=A0=A0 =A0break; > +=A0=A0=A0=A0=A0=A0=A0} > + =A0 =A0} > + > + =A0make_cleanup (xfree, content); > + > + =A0p =3D strchr (content, '('); > + =A0p =3D strchr (p, ')') + 2; /* skip ")" and a whitespace. */ > + > + =A0/* If the first field after program name has index 0, then core numb= er is > + =A0 =A0 the field with index 36. There's no constant for that anywhere.= =A0*/ > + =A0p =3D strtok_r (p, " ", &ts); > + =A0for (i =3D 0; i !=3D 36; ++i) > + =A0 =A0p =3D strtok_r (NULL, " ", &ts); > + > + =A0if (sscanf (p, "%d", &core) =3D=3D 0) > + =A0 =A0core =3D -1; > + > + =A0do_cleanups (back_to); > + > + =A0return core; > +} > + > =A0void > =A0linux_nat_add_target (struct target_ops *t) > =A0{ > @@ -5461,6 +5514,8 @@ linux_nat_add_target (struct target_ops *t) > =A0 > =A0 =A0t->to_supports_multi_process =3D linux_nat_supports_multi_process; > =A0 > + =A0t->to_core_of_thread =3D linux_nat_core_of_thread; > + > =A0 =A0/* We don't change the stratum; this target will sit at > =A0 =A0 =A0 process_stratum and thread_db will set at thread_stratum. =A0= This > =A0 =A0 =A0 is a little strange, since this is a multi-threaded-capable > diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c > index 248cd66..af51bde 100644 > --- a/gdb/mi/mi-interp.c > +++ b/gdb/mi/mi-interp.c > @@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_fram= e) > =A0 > =A0 =A0if (print_frame) > =A0 =A0 =A0{ > + =A0 =A0 =A0int core; > =A0 =A0 =A0 =A0if (uiout !=3D mi_uiout) > =A0=A0=A0=A0=A0=A0=A0=A0{ > =A0=A0=A0=A0=A0=A0=A0=A0 =A0/* The normal_stop function has printed frame= information into=20 > @@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_fra= me) > =A0=A0=A0=A0=A0=A0=A0=A0} > =A0 =A0 =A0 =A0else > =A0=A0=A0=A0=A0=A0=A0=A0ui_out_field_string (mi_uiout, "stopped-threads",= "all"); > + > + =A0 =A0 =A0core =3D target_core_of_thread (inferior_ptid); > + =A0 =A0 =A0if (core !=3D -1) > +=A0=A0=A0=A0=A0=A0=A0ui_out_field_int (mi_uiout, "core", core); > =A0 =A0 =A0} > =A0 =A0 > =A0 =A0fputs_unfiltered ("*stopped", raw_stdout); > diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c > index 2332752..b152d98 100644 > --- a/gdb/mi/mi-main.c > +++ b/gdb/mi/mi-main.c > @@ -359,11 +359,44 @@ mi_cmd_thread_info (char *command, char **argv, int= argc) > =A0 =A0print_thread_info (uiout, thread, -1); > =A0} > =A0 > +struct collect_cores_data > +{ > + =A0int pid; > + > + =A0VEC (int) *cores; > +}; > + > +static int collect_cores (struct thread_info *ti, void *xdata) Line break before function name, please. > +{ > + =A0struct collect_cores_data *data =3D xdata; > + > + =A0if (ptid_get_pid (ti->ptid) =3D=3D data->pid) > + =A0 =A0{ > + =A0 =A0 =A0int core =3D target_core_of_thread (ti->ptid); > + =A0 =A0 =A0if (core !=3D -1) > +=A0=A0=A0=A0=A0=A0=A0VEC_safe_push (int, data->cores, core); > + =A0 =A0} > + > + =A0return 0; > +} > + > +struct print_one_inferior_data > +{ > + =A0int recurse; > + =A0VEC (int) *inferiors; > +}; > + > =A0static int > -print_one_inferior (struct inferior *inferior, void *arg) > +print_one_inferior (struct inferior *inferior, void *xdata) > =A0{ > - =A0if (inferior->pid !=3D 0) > + =A0struct print_one_inferior_data *top_data =3D xdata; > + =A0 > + =A0if (VEC_length (int, top_data->inferiors) =3D=3D 0 > + =A0 =A0 =A0|| bsearch (&(inferior->pid), VEC_address (int, top_data->in= feriors), > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0VEC_length (int, top_da= ta->inferiors), sizeof (int), > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0compare_positive_ints)) > =A0 =A0 =A0{ > + =A0 =A0 =A0struct collect_cores_data data; > =A0 =A0 =A0 =A0struct cleanup *back_to > =A0=A0=A0=A0=A0=A0=A0=A0=3D make_cleanup_ui_out_tuple_begin_end (uiout, N= ULL); > =A0 > @@ -371,36 +404,128 @@ print_one_inferior (struct inferior *inferior, voi= d *arg) > =A0 =A0 =A0 =A0ui_out_field_string (uiout, "type", "process"); > =A0 =A0 =A0 =A0ui_out_field_int (uiout, "pid", inferior->pid); > =A0 > + =A0 =A0 =A0data.pid =3D inferior->pid; > + =A0 =A0 =A0data.cores =3D 0; > + =A0 =A0 =A0iterate_over_threads (collect_cores, &data); > + > + =A0 =A0 =A0if (VEC_length (int, data.cores))=20 Use instead: if (!VEC_empty (int, data.cores)) > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0int elt; > +=A0=A0=A0=A0=A0=A0=A0 =A0int i; > +=A0=A0=A0=A0=A0=A0=A0 =A0int *b, *e; > +=A0=A0=A0=A0=A0=A0=A0 =A0struct cleanup *back_to_2 =3D > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0make_cleanup_ui_out_list_begin_end (uiout, = "cores"); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0qsort (VEC_address (int, data.cores), > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 VEC_length (int, data.core= s), sizeof (int), > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 compare_positive_ints); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0b =3D VEC_address (int, data.cores); > +=A0=A0=A0=A0=A0=A0=A0 =A0e =3D b + VEC_length (int, data.cores); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0/* Remove duplicates. =A0*/ > +=A0=A0=A0=A0=A0=A0=A0 =A0while (b !=3D e) { > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0if ((b + 1 < e) && (*b =3D=3D *(b+1))) Spaces around '+'. > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0*(b + 1) =3D *(e - 1); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0--e; > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0} > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0else > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0++b; > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0} > +=A0=A0=A0=A0=A0=A0=A0 =A0} > +=A0=A0=A0=A0=A0=A0=A0 =A0=A0=A0=A0=A0=A0=A0 =A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0 =A0for (i =3D 0; VEC_iterate (int, data.cores, i, = elt); ++i) > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0ui_out_field_int (uiout, NULL, elt); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0do_cleanups (back_to_2); > +=A0=A0=A0=A0=A0=A0=A0} > + > + =A0 =A0 =A0if (top_data->recurse) > +=A0=A0=A0=A0=A0=A0=A0print_thread_info (uiout, -1, inferior->pid); =A0 = =A0 > + > =A0 =A0 =A0 =A0do_cleanups (back_to); > =A0 =A0 =A0} > =A0 > =A0 =A0return 0; > =A0} > =A0 > +/* Output a field named 'cores' with a list as the value. The elements of > + =A0 the list are obtained by splitting 'cores' on comma. =A0*/ > +static void Double space after period. Please add one empty line between describing comment and the function. > +output_cores (struct ui_out *uiout, const char *field_name, const char *= xcores) > +{ > + =A0struct cleanup *back_to =3D make_cleanup_ui_out_list_begin_end (uiou= t,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0field_name); > + =A0char *cores =3D xstrdup (xcores); > + =A0char *p =3D cores; > + =A0char *ts =3D 0; > + > + =A0make_cleanup (xfree, cores); > + > + =A0for (p =3D strtok_r (p, ",", &ts); p; =A0p =3D strtok_r (NULL, ",", = &ts)) > + =A0 =A0ui_out_field_string (uiout, NULL, p); strtok_r isn't portable (not available on mingw, for example). You could pull it from gnulib, but I doubt it's worth the trouble. > +=20 > + =A0do_cleanups (back_to); > +} > + > =A0void > =A0mi_cmd_list_thread_groups (char *command, char **argv, int argc) > =A0{ > =A0 =A0struct cleanup *back_to; > =A0 =A0int available =3D 0; > - =A0char *id =3D NULL; > + =A0int recurse =3D 0; > + =A0VEC (int) *ids =3D 0; > =A0 > - =A0if (argc > 0 && strcmp (argv[0], "--available") =3D=3D 0) > + =A0enum opt > =A0 =A0 =A0{ > - =A0 =A0 =A0++argv; > - =A0 =A0 =A0--argc; > - =A0 =A0 =A0available =3D 1; > - =A0 =A0} > + =A0 =A0 =A0AVAILABLE_OPT, RECURSE_OPT > + =A0 =A0}; > + =A0static struct mi_opt opts[] =3D > + =A0{ > + =A0 =A0{"-available", AVAILABLE_OPT, 0}, > + =A0 =A0{"-recurse", RECURSE_OPT, 1}, > + =A0 =A0{ 0, 0, 0 } > + =A0}; > =A0 > - =A0if (argc > 0) > - =A0 =A0id =3D argv[0]; > + =A0int optind =3D 0; > + =A0char *optarg; > =A0 > - =A0back_to =3D make_cleanup (null_cleanup, NULL); > + =A0while (1) > + =A0 =A0{ > + =A0 =A0 =A0int opt =3D mi_getopt ("-list-thread-groups", argc, argv, op= ts,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 &optind, &optarg); > + =A0 =A0 =A0if (opt < 0) > +=A0=A0=A0=A0=A0=A0=A0break; > + =A0 =A0 =A0switch ((enum opt) opt) > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0case AVAILABLE_OPT:=20 > +=A0=A0=A0=A0=A0=A0=A0 =A0available =3D 1;=20 > +=A0=A0=A0=A0=A0=A0=A0 =A0break; > +=A0=A0=A0=A0=A0=A0=A0case RECURSE_OPT: > +=A0=A0=A0=A0=A0=A0=A0 =A0if (strcmp (optarg, "1") !=3D 0) > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0error ("only '1' is permitted value for the= '--recurse' option"); Seems strange not to allow '--recurse 0'. What future values are you thinking of? Wouldn't it make more sense to call this "--threads"? We may add other level-1 sublists to thread-groups in the future, and _not_ want --recurse 1 to always show them, no? > +=A0=A0=A0=A0=A0=A0=A0 =A0recurse =3D 1; > +=A0=A0=A0=A0=A0=A0=A0 =A0break; > +=A0=A0=A0=A0=A0=A0=A0} > + =A0 =A0} > =A0 > - =A0if (available && id) > + =A0for (; optind < argc; ++optind) > =A0 =A0 =A0{ > - =A0 =A0 =A0error (_("Can only report top-level available thread groups"= )); > + =A0 =A0 =A0char *end; > + =A0 =A0 =A0int inf =3D strtoul (argv[optind], &end, 0); > + =A0 =A0 =A0if (*end !=3D '\0') > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0error ("invalid group id '%s'", argv[optind= ]); Something fishy with the tabs vs spaces here. > + =A0 =A0 =A0VEC_safe_push (int, ids, inf); > =A0 =A0 =A0} > - =A0else if (available) > + =A0qsort (VEC_address (int, ids),=20 > +=A0=A0=A0=A0=A0=A0=A0 VEC_length (int, ids), > +=A0=A0=A0=A0=A0=A0=A0 sizeof (int), compare_positive_ints); > + > + =A0back_to =3D make_cleanup ((void (*)(void *))VEC_OP (int, free), &ids= ); Please don't cast the function's type: instead write a small function that has the proper cleanup prototype, and call free there. > + > + =A0if (available) > =A0 =A0 =A0{ > =A0 =A0 =A0 =A0struct osdata *data; > =A0 =A0 =A0 =A0struct osdata_item *item; > @@ -416,12 +541,25 @@ mi_cmd_list_thread_groups (char *command, char **ar= gv, int argc) > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0i= x_items, item); > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 ix_items++) > =A0=A0=A0=A0=A0=A0=A0=A0{ > -=A0=A0=A0=A0=A0=A0=A0 =A0struct cleanup *back_to =3D > -=A0=A0=A0=A0=A0=A0=A0 =A0 =A0make_cleanup_ui_out_tuple_begin_end (uiout,= NULL); > +=A0=A0=A0=A0=A0=A0=A0 =A0struct cleanup *back_to; > =A0 > =A0=A0=A0=A0=A0=A0=A0=A0 =A0const char *pid =3D get_osdata_column (item, = "pid"); > =A0=A0=A0=A0=A0=A0=A0=A0 =A0const char *cmd =3D get_osdata_column (item, = "command"); > =A0=A0=A0=A0=A0=A0=A0=A0 =A0const char *user =3D get_osdata_column (item,= "user"); > +=A0=A0=A0=A0=A0=A0=A0 =A0const char *cores =3D get_osdata_column (item, = "cores"); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0int pid_i =3D strtoul (pid, NULL, 0); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0/* At present, the target will return all avail= able processes > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 and if information about specific ones was= required, we filter > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 undesired processes here. =A0*/ > +=A0=A0=A0=A0=A0=A0=A0 =A0if (ids && bsearch (&pid_i, VEC_address (int, i= ds),=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 =A0VEC_length (int, ids),=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 =A0sizeof (int), compare_positive_ints) =3D=3D NULL) > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0continue; > + > + > +=A0=A0=A0=A0=A0=A0=A0 =A0back_to =3D make_cleanup_ui_out_tuple_begin_end= (uiout, NULL); > =A0 > =A0=A0=A0=A0=A0=A0=A0=A0 =A0ui_out_field_fmt (uiout, "id", "%s", pid); > =A0=A0=A0=A0=A0=A0=A0=A0 =A0ui_out_field_string (uiout, "type", "process"= ); > @@ -429,21 +567,57 @@ mi_cmd_list_thread_groups (char *command, char **ar= gv, int argc) > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0ui_out_field_string (uiout, "description"= , cmd); > =A0=A0=A0=A0=A0=A0=A0=A0 =A0if (user) > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0ui_out_field_string (uiout, "user", user); > +=A0=A0=A0=A0=A0=A0=A0 =A0if (cores) > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0output_cores (uiout, "cores", cores); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0if (item->children && recurse) > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0struct osdata_item *child; > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0int ix_child;=A0=A0=A0=A0=A0 =A0 =A0 =A0 > + > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0make_cleanup_ui_out_list_begin_end (uio= ut, "threads"); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0for (ix_child =3D 0; VEC_iterate (osdat= a_item_s,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0item->children->it= ems,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0ix_child, child); = ++ix_child) Please format as: for (ix_child =3D 0; VEC_iterate (osdata_item_s, item->children->items, ix_child, child); ++ix_child) (it took me a bit to locate the ++ix_child statement in your formatting) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0struct cleanup *back_to= _2 =3D=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0make_cleanup_ui_out= _tuple_begin_end (uiout, NULL); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0const char *tid =3D get= _osdata_column (child, "tid"); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0const char *tcore =3D g= et_osdata_column (child, "core"); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0ui_out_field_string (ui= out, "id", tid); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0if (tcore) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0ui_out_field_string= (uiout, "core", tcore); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0do_cleanups (back_to_2); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0}=A0=A0=A0=A0=A0=A0=A0 =A0 = =A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0} > =A0 > =A0=A0=A0=A0=A0=A0=A0=A0 =A0do_cleanups (back_to); > =A0=A0=A0=A0=A0=A0=A0=A0} > =A0 =A0 =A0} > - =A0else if (id) > + =A0else if (VEC_length (int, ids) =3D=3D 1) > =A0 =A0 =A0{ > - =A0 =A0 =A0int pid =3D atoi (id); > + =A0 =A0 =A0/* Local thread groups, single id. */ > + =A0 =A0 =A0int pid =3D *VEC_address (int, ids); > =A0 =A0 =A0 =A0if (!in_inferior_list (pid)) > -=A0=A0=A0=A0=A0=A0=A0error ("Invalid thread group id '%s'", id); > +=A0=A0=A0=A0=A0=A0=A0error ("Invalid thread group id '%d'", pid); > =A0 =A0 =A0 =A0print_thread_info (uiout, -1, pid); =A0 =A0 > =A0 =A0 =A0} > =A0 =A0else > =A0 =A0 =A0{ > + =A0 =A0 =A0struct print_one_inferior_data data; > + =A0 =A0 =A0data.recurse =3D recurse; > + =A0 =A0 =A0data.inferiors =3D ids; > + > + =A0 =A0 =A0/* Local thread groups. Either no explicit ids -- and we > +=A0=A0=A0=A0=A0=A0=A0 print everything, or several explicit ids. In both= cases, > +=A0=A0=A0=A0=A0=A0=A0 we print more than one group, and have to use 'gro= ups' > +=A0=A0=A0=A0=A0=A0=A0 as the top-level element. =A0*/ > =A0 =A0 =A0 =A0make_cleanup_ui_out_list_begin_end (uiout, "groups"); > - =A0 =A0 =A0iterate_over_inferiors (print_one_inferior, NULL); > + =A0 =A0 =A0update_thread_list (); > + =A0 =A0 =A0iterate_over_inferiors (print_one_inferior, &data); > =A0 =A0 =A0} > =A0 =A0 > =A0 =A0do_cleanups (back_to); > diff --git a/gdb/osdata.c b/gdb/osdata.c > index 3d2c23c..394fc4d 100644 > --- a/gdb/osdata.c > +++ b/gdb/osdata.c > @@ -50,8 +50,10 @@ osdata_parse (const char *xml) > =A0/* Internal parsing data passed to all XML callbacks. =A0*/ > =A0struct osdata_parsing_data > =A0 =A0{ > + =A0 =A0struct osdata *top_osdata; > =A0 =A0 =A0struct osdata *osdata; > =A0 =A0 =A0char *property_name; > + =A0 =A0struct osdata_item *current_item; > =A0 =A0}; > =A0 > =A0/* Handle the start of a element. =A0*/ > @@ -71,9 +73,37 @@ osdata_start_osdata (struct gdb_xml_parser *parser, > =A0 =A0type =3D VEC_index (gdb_xml_value_s, attributes, 0)->value; > =A0 =A0osdata =3D XZALLOC (struct osdata); > =A0 =A0osdata->type =3D xstrdup (type); > + =A0data->top_osdata =3D data->osdata =3D osdata; > +} > + > +/* Handle the start of a element. =A0*/ > + > +static void > +osdata_start_threads (struct gdb_xml_parser *parser, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0const struct gd= b_xml_element *element, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0void *user_data= , VEC(gdb_xml_value_s) *attributes) > +{ > + =A0struct osdata_parsing_data *data =3D user_data; > + =A0char *type; > + =A0struct osdata *osdata; > + > + =A0osdata =3D XZALLOC (struct osdata); > + =A0osdata->type =3D xstrdup ("threads"); > =A0 =A0data->osdata =3D osdata; > =A0} > =A0 > +static void > +osdata_end_threads (struct gdb_xml_parser *parser, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0const struct gdb_xm= l_element *element, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0void *user_data, co= nst char *body_text) > +{ > + =A0struct osdata_parsing_data *data =3D user_data; > + =A0 > + =A0data->current_item->children =3D data->osdata; > + =A0data->osdata =3D data->top_osdata; > +} > + > + > =A0/* Handle the start of a element. =A0*/ > =A0 > =A0static void > @@ -84,6 +114,8 @@ osdata_start_item (struct gdb_xml_parser *parser, > =A0 =A0struct osdata_parsing_data *data =3D user_data; > =A0 =A0struct osdata_item item =3D { NULL }; > =A0 =A0VEC_safe_push (osdata_item_s, data->osdata->items, &item); > + =A0if (data->osdata =3D=3D data->top_osdata) > + =A0 =A0data->current_item =3D VEC_last (osdata_item_s, data->osdata->it= ems); > =A0} > =A0 > =A0/* Handle the start of a element. =A0*/ > @@ -137,10 +169,15 @@ const struct gdb_xml_attribute column_attributes[] = =3D { > =A0 =A0{ NULL, GDB_XML_AF_NONE, NULL, NULL } > =A0}; > =A0 > +extern const struct gdb_xml_element osdata_children[]; > + > =A0const struct gdb_xml_element item_children[] =3D { > =A0 =A0{ "column", column_attributes, NULL, > =A0 =A0 =A0GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, > =A0 =A0 =A0osdata_start_column, osdata_end_column }, > + =A0{ "threads", NULL, osdata_children, > + =A0 =A0GDB_XML_EF_OPTIONAL, osdata_start_threads, > + =A0 =A0osdata_end_threads }, > =A0 =A0{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } > =A0}; > =A0 > diff --git a/gdb/osdata.h b/gdb/osdata.h > index 4ada454..485789e 100644 > --- a/gdb/osdata.h > +++ b/gdb/osdata.h > @@ -22,6 +22,9 @@ > =A0 > =A0#include "vec.h" > =A0 > +typedef struct osdata *osdata_p; > +DEF_VEC_P(osdata_p); > + > =A0typedef struct osdata_column > =A0{ > =A0 =A0char *name; > @@ -32,6 +35,13 @@ DEF_VEC_O(osdata_column_s); > =A0typedef struct osdata_item > =A0{ > =A0 =A0VEC(osdata_column_s) *columns; > + > + =A0/* Nested items. Presently, a given item may only contain > + =A0 =A0 children of specific kind, and children->type gives > + =A0 =A0 the kind of children. In fact, only 'threads' may be > + =A0 =A0 the kind of children right now. =A0*/ > + =A0osdata_p children; > + =A0 > =A0} osdata_item_s; > =A0DEF_VEC_O(osdata_item_s); > =A0 > @@ -41,8 +51,7 @@ struct osdata > =A0 > =A0 =A0VEC(osdata_item_s) *items; > =A0}; > -typedef struct osdata *osdata_p; > -DEF_VEC_P(osdata_p); > + > =A0 > =A0struct osdata *osdata_parse (const char *xml); > =A0void osdata_free (struct osdata *); > diff --git a/gdb/remote.c b/gdb/remote.c > index 9fa92fb..eb47713 100644 > --- a/gdb/remote.c > +++ b/gdb/remote.c > @@ -60,6 +60,7 @@ > =A0#include "remote-fileio.h" > =A0#include "gdb/fileio.h" > =A0#include "gdb_stat.h" > +#include "xml-support.h" > =A0 > =A0#include "memory-map.h" > =A0 > @@ -303,6 +304,13 @@ struct remote_state > =A0 =A0int ctrlc_pending_p; > =A0}; > =A0 > +/* Private data that we'll store in (struct thread_info)->private. =A0*/ > +struct private_thread_info > +{ > + =A0char *extra; > + =A0int core; > +}; > + > =A0/* Returns true if the multi-process extensions are in effect. =A0*/ > =A0static int > =A0remote_multi_process_p (struct remote_state *rs) > @@ -1054,6 +1062,7 @@ enum { > =A0 =A0PACKET_qXfer_spu_read, > =A0 =A0PACKET_qXfer_spu_write, > =A0 =A0PACKET_qXfer_osdata, > + =A0PACKET_qXfer_threads, > =A0 =A0PACKET_qGetTLSAddr, > =A0 =A0PACKET_qSupported, > =A0 =A0PACKET_QPassSignals, > @@ -2303,6 +2312,76 @@ remote_find_new_threads (void) > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 =A0CRAZY_MAX_THREADS); > =A0} > =A0 > +typedef struct thread_item > +{ > + =A0ptid_t ptid; > + =A0char *extra; > + =A0int core; > +} thread_item_t; > +DEF_VEC_O(thread_item_t); > + > +struct threads_parsing_context > +{ > + =A0VEC (thread_item_t) *items; > +}; > + > +static void > +start_thread (struct gdb_xml_parser *parser, > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0const struct gdb_xml_element *element, > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0void *user_data, VEC(gdb_xml_value_s) *= attributes) libexpat is an optional dependency. This code should be conditionalized on HAVE_LIBEXPAT. After fixing, please confirm GDB still builds and works against gdbserver when configured with "--with-expat=3Dno". > +{ > + =A0struct threads_parsing_context *data =3D user_data; > + > + =A0struct thread_item item; > + =A0char *id; > + > + =A0id =3D VEC_index (gdb_xml_value_s, attributes, 0)->value; > + =A0item.ptid =3D read_ptid (id, NULL); > + > + =A0if (VEC_length (gdb_xml_value_s, attributes) > 1) > + =A0 =A0item.core =3D *(ULONGEST *) VEC_index (gdb_xml_value_s, attribut= es, 1)->value; > + =A0else > + =A0 =A0item.core =3D -1; > + > + =A0item.extra =3D 0; > + > + =A0VEC_safe_push (thread_item_t, data->items, &item); > +} > + > +static void=20 > +end_thread (struct gdb_xml_parser *parser,=20 > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0const struct gdb_xml_element *element, > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0void *user_data, const char *body_text) > +{ > + =A0struct threads_parsing_context *data =3D user_data; > + > + =A0if (body_text && *body_text) > + =A0 =A0VEC_last (thread_item_t, data->items)->extra =3D strdup (body_te= xt); > +} > + > +const struct gdb_xml_attribute thread_attributes[] =3D { > + =A0{ "id", GDB_XML_AF_NONE, NULL, NULL }, > + =A0{ "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL }, > + =A0{ NULL, GDB_XML_AF_NONE, NULL, NULL } > +}; > + > +const struct gdb_xml_element thread_children[] =3D { > + =A0{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } > +}; > + > +const struct gdb_xml_element threads_children[] =3D { > + =A0{ "thread", thread_attributes, thread_children, > + =A0 =A0GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, > + =A0 =A0start_thread, end_thread }, > + =A0{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } > +}; > + > +const struct gdb_xml_element threads_elements[] =3D { > + =A0{ "threads", NULL, threads_children, > + =A0 =A0GDB_XML_EF_NONE, NULL, NULL }, > + =A0{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL } > +}; > + > =A0/* > =A0 * Find all threads for info threads command. > =A0 * Uses new thread protocol contributed by Cisco. > @@ -2320,6 +2399,66 @@ remote_threads_info (struct target_ops *ops) > =A0 =A0if (remote_desc =3D=3D 0)=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0/* paranoia */ > =A0 =A0 =A0error (_("Command can only be used when connected to the remot= e target.")); > =A0 > + =A0if (remote_protocol_packets[PACKET_qXfer_threads].support =3D=3D PAC= KET_ENABLE) > + =A0 =A0{ > + =A0 =A0 =A0char *xml =3D target_read_stralloc (¤t_target,=20 > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 TARGET_OBJECT_THREADS, NULL); > + > + =A0 =A0 =A0struct cleanup *back_to =3D make_cleanup (xfree, xml); > + =A0 =A0 =A0if (xml && *xml)=20 > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0struct gdb_xml_parser *parser; > +=A0=A0=A0=A0=A0=A0=A0 =A0struct threads_parsing_context context; > +=A0=A0=A0=A0=A0=A0=A0 =A0struct cleanup *back_to =3D make_cleanup (null_= cleanup, NULL); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0context.items =3D 0; > +=A0=A0=A0=A0=A0=A0=A0 =A0parser =3D gdb_xml_create_parser_and_cleanup (_= ("threads"), > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 = =A0 =A0threads_elements, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 = =A0 =A0&context); > + > +=A0=A0=A0=A0=A0=A0=A0 =A0gdb_xml_use_dtd (parser, "threads.dtd"); > +=A0=A0=A0=A0=A0=A0=A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0 =A0if (gdb_xml_parse (parser, xml) =3D=3D 0) > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0int i; > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0struct thread_item *item; > + > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0for (i =3D 0; VEC_iterate (thread_item_= t, context.items, i, item); ++i) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0if (!ptid_equal (item->= ptid, null_ptid)) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0struct thread_i= nfo *info; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0/* In non-stop = mode, we assume new found threads > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 ar= e running until proven otherwise with a > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 st= op reply. =A0In all-stop, we can only get > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 he= re if all threads are stopped. =A0*/ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0int running =3D= non_stop ? 1 : 0; > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0remote_notice_n= ew_inferior (item->ptid, running); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0info =3D find_t= hread_ptid (item->ptid); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0if (info) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0if (!info->private) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0info->private =3D (struct private_thread_info *) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 =A0xmalloc (sizeof (struct private_thread_info)); > + > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0info->private->extra =3D item->extra; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0item->extra =3D 0; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0info->private->core =3D item->core; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0} > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0} > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0xfree (item->extra); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0}=A0=A0=A0=A0=A0=A0=A0 =A0 = =A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0} > +=A0=A0=A0=A0=A0=A0=A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0 =A0VEC_free (thread_item_t, context.items);=A0=A0= =A0=A0=A0=A0 =A0 > +=A0=A0=A0=A0=A0=A0=A0} > + > + =A0 =A0 =A0do_cleanups (back_to); > + =A0 =A0 =A0return; > + =A0 =A0} > + > =A0 =A0if (use_threadinfo_query) > =A0 =A0 =A0{ > =A0 =A0 =A0 =A0putpkt ("qfThreadInfo"); > @@ -2392,6 +2531,15 @@ remote_threads_extra_info (struct thread_info *tp) > =A0 =A0 =A0 =A0 server doesn't know about it. =A0*/ > =A0 =A0 =A0return NULL; > =A0 > + =A0if (remote_protocol_packets[PACKET_qXfer_threads].support =3D=3D PAC= KET_ENABLE) > + =A0 =A0{ > + =A0 =A0 =A0struct thread_info *info =3D find_thread_ptid (tp->ptid); > + =A0 =A0 =A0if (info && info->private) > +=A0=A0=A0=A0=A0=A0=A0return info->private->extra; > + =A0 =A0 =A0else > +=A0=A0=A0=A0=A0=A0=A0return NULL; > + =A0 =A0} > + > =A0 =A0if (use_threadextra_query) > =A0 =A0 =A0{ > =A0 =A0 =A0 =A0char *b =3D rs->buf; > @@ -3152,6 +3300,8 @@ static struct protocol_feature remote_protocol_feat= ures[] =3D { > =A0 =A0 =A0PACKET_qXfer_spu_write }, > =A0 =A0{ "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet, > =A0 =A0 =A0PACKET_qXfer_osdata }, > + =A0{ "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet, > + =A0 =A0PACKET_qXfer_threads }, > =A0 =A0{ "QPassSignals", PACKET_DISABLE, remote_supported_packet, > =A0 =A0 =A0PACKET_QPassSignals }, > =A0 =A0{ "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet, > @@ -4262,6 +4412,8 @@ struct stop_reply > =A0 > =A0 =A0int solibs_changed; > =A0 =A0int replay_event; > + > + =A0int core; > =A0}; > =A0 > =A0/* The list of already fetched and acknowledged stop events. =A0*/ > @@ -4425,6 +4577,7 @@ remote_parse_stop_reply (char *buf, struct stop_rep= ly *event) > =A0 =A0event->replay_event =3D 0; > =A0 =A0event->stopped_by_watchpoint_p =3D 0; > =A0 =A0event->regcache =3D NULL; > + =A0event->core =3D -1; > =A0 > =A0 =A0switch (buf[0]) > =A0 =A0 =A0{ > @@ -4451,7 +4604,8 @@ remote_parse_stop_reply (char *buf, struct stop_rep= ly *event) > =A0=A0=A0=A0=A0=A0=A0=A0 =A0/* If this packet is an awatch packet, don't = parse the 'a' > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 as a register number. =A0*/ > =A0 > -=A0=A0=A0=A0=A0=A0=A0 =A0if (strncmp (p, "awatch", strlen("awatch")) != =3D 0) > +=A0=A0=A0=A0=A0=A0=A0 =A0if (strncmp (p, "awatch", strlen("awatch")) != =3D 0 > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0&& strncmp (p, "core", strlen ("core") = !=3D 0)) > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0{ > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0/* Read the ``P'' register number. = =A0*/ > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0pnum =3D strtol (p, &p_temp, 16); > @@ -4497,6 +4651,12 @@ Packet: '%s'\n"), > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0if (p_temp) > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0p =3D p_temp; > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0} > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0else if (strncmp (p, "core", p1 - p) = =3D=3D 0) > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0ULONGEST c; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0p =3D unpack_varlen_hex= (++p1, &c); > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0event->core =3D c; > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0} > =A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0 =A0else > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0{ > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0/* Silently skip unkn= own optional info. =A0*/ > @@ -4706,6 +4866,7 @@ process_stop_reply (struct stop_reply *stop_reply, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 =A0 =A0struct target_wai= tstatus *status) > =A0{ > =A0 =A0ptid_t ptid; > + =A0struct thread_info *info; > =A0 > =A0 =A0*status =3D stop_reply->ws; > =A0 =A0ptid =3D stop_reply->ptid; > @@ -4736,6 +4897,12 @@ process_stop_reply (struct stop_reply *stop_reply, > =A0 =A0 =A0 =A0remote_stopped_by_watchpoint_p =3D stop_reply->stopped_by_= watchpoint_p; > =A0 =A0 =A0 =A0remote_watch_data_address =3D stop_reply->watch_data_addre= ss; > =A0 > + =A0 =A0 =A0/* Update the core associated with a thread when we process = stop > +=A0=A0=A0=A0=A0=A0=A0 event in that thread. =A0*/ > + =A0 =A0 =A0info =3D find_thread_ptid (ptid); > + =A0 =A0 =A0if (info && info->private) > +=A0=A0=A0=A0=A0=A0=A0info->private->core =3D stop_reply->core; Consider all-stop, multi-threaded applications: What about the core of the other threads? Shouldn't they all be invalidated? Won't then be stale? (see comment below) > + > =A0 =A0 =A0 =A0remote_notice_new_inferior (ptid, 0); > =A0 =A0 =A0} > =A0 > @@ -7579,6 +7746,11 @@ remote_xfer_partial (struct target_ops *ops, enum = target_object object, > =A0 =A0 =A0 =A0 (ops, "osdata", annex, readbuf, offset, len, > =A0 =A0 =A0 =A0 =A0&remote_protocol_packets[PACKET_qXfer_osdata]); > =A0 > + =A0 =A0case TARGET_OBJECT_THREADS: > + =A0 =A0 =A0gdb_assert (annex =3D=3D NULL); > + =A0 =A0 =A0return remote_read_qxfer (ops, "threads", annex, readbuf, of= fset, len, > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0&remote_protocol_packets[PACKET_qXfer_threads]); > + > =A0 =A0 =A0default: > =A0 =A0 =A0 =A0return -1; > =A0 =A0 =A0} > @@ -8895,6 +9067,15 @@ remote_supports_cond_tracepoints (void) > =A0 =A0return rs->cond_tracepoints; > =A0} > =A0 > +static int > +remote_core_of_thread (struct target_ops *ops, ptid_t ptid) > +{ > + =A0struct thread_info *info =3D find_thread_ptid (ptid); > + =A0if (info && info->private) > + =A0 =A0return info->private->core; > + =A0return -1; > +} (see comment above) I think this returns stale core data for all other threads other than the one that reported the stop, no? How is that handled? > + > =A0static void > =A0init_remote_ops (void) > =A0{ > @@ -8958,6 +9139,7 @@ Specify the serial device it is connected to\n\ > =A0 =A0remote_ops.to_terminal_ours =3D remote_terminal_ours; > =A0 =A0remote_ops.to_supports_non_stop =3D remote_supports_non_stop; > =A0 =A0remote_ops.to_supports_multi_process =3D remote_supports_multi_pro= cess; > + =A0remote_ops.to_core_of_thread =3D remote_core_of_thread; > =A0} > =A0 > =A0/* Set up the extended remote vector by making a copy of the standard > @@ -9317,6 +9499,9 @@ Show the maximum size of the address (in bits) in a= memory packet."), NULL, > =A0 =A0add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdat= a], > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0"qXfer:osdata:read", "= osdata", 0); > =A0 > + =A0add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads= ], > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "q= Xfer:threads:read", "threads", 0); > + > =A0 =A0add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_sigin= fo_read], > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "qXfer:siginfo:read",= "read-siginfo-object", 0); > =A0 > diff --git a/gdb/target.c b/gdb/target.c > index cd1614b..4fd82b9 100644 > --- a/gdb/target.c > +++ b/gdb/target.c > @@ -3024,6 +3024,26 @@ target_store_registers (struct regcache *regcache,= int regno) > =A0 =A0noprocess (); > =A0} > =A0 > +int > +target_core_of_thread (ptid_t ptid) > +{ > + =A0struct target_ops *t; > + > + =A0for (t =3D current_target.beneath; t !=3D NULL; t =3D t->beneath) > + =A0 =A0{ > + =A0 =A0 =A0if (t->to_core_of_thread !=3D NULL) > +=A0=A0=A0=A0=A0=A0=A0{ > +=A0=A0=A0=A0=A0=A0=A0 =A0int retval =3D t->to_core_of_thread (t, ptid); > +=A0=A0=A0=A0=A0=A0=A0 =A0if (targetdebug) > +=A0=A0=A0=A0=A0=A0=A0 =A0 =A0fprintf_unfiltered (gdb_stdlog, "target_cor= e_of_thread (%d) =3D %d\n", > +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0PIDGET (ptid), retval); > +=A0=A0=A0=A0=A0=A0=A0 =A0return retval; > +=A0=A0=A0=A0=A0=A0=A0} > + =A0 =A0} > + > + =A0return -1; > +} > + > =A0static void > =A0debug_to_prepare_to_store (struct regcache *regcache) > =A0{ > diff --git a/gdb/target.h b/gdb/target.h > index ebe6056..7435433 100644 > --- a/gdb/target.h > +++ b/gdb/target.h > @@ -255,7 +255,9 @@ enum target_object > =A0 =A0/* Extra signal info. =A0Usually the contents of `siginfo_t' on un= ix > =A0 =A0 =A0 platforms. =A0*/ > =A0 =A0TARGET_OBJECT_SIGNAL_INFO, > - =A0/* Possible future objects: TARGET_OBJECT_FILE, ... */ > + =A0/* The list of threads that are being debugged. =A0*/ > + =A0TARGET_OBJECT_THREADS, > + =A0/* Possible future objects: TARGET_OBJECT_FILE, ... */ =A0 > =A0}; > =A0 > =A0/* Request that OPS transfer up to LEN 8-bit bytes of the target's > @@ -597,6 +599,14 @@ struct target_ops > =A0 =A0 =A0struct address_space *(*to_thread_address_space) (struct targe= t_ops *, > =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 = =A0 =A0 =A0ptid_t); > =A0 > + =A0 =A0/* Return the core that thread PTID is on, or -1 if such informa= tion > + =A0 =A0 =A0 is not available. For a stopped thread, this is supposed to= return > + =A0 =A0 =A0 the core the thread was last running on. =A0For running thr= eads, it > + =A0 =A0 =A0 should return one of the cores that the thread was running = between > + =A0 =A0 =A0 the call to this function and return -- and if it was runni= ng on > + =A0 =A0 =A0 several cores, any other may be returned. =A0*/ > + =A0 =A0int (*to_core_of_thread) (struct target_ops *, ptid_t ptid); I guess that on some targets, it will be impossible to know which core a running thread was running on. Should it be simply documented as undefined instead? > + > =A0 =A0 =A0int to_magic; > =A0 =A0 =A0/* Need sub-structure for target machine related rather than c= omm related? > =A0 =A0 =A0 */ > @@ -1246,6 +1256,9 @@ extern int target_search_memory (CORE_ADDR start_ad= dr, > =A0 =A0 =A0 =A0(*current_target.to_log_command) (p);=A0=A0=A0=A0=A0=A0=A0= =A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0\ > =A0 =A0while (0) > =A0 > + > +extern int target_core_of_thread (ptid_t ptid); > + > =A0/* Routines for maintenance of the target structures... > =A0 > =A0 =A0 add_target: =A0 Add a target to the list of all possible targets. > diff --git a/gdb/thread.c b/gdb/thread.c > index 689e9d5..fc2ee2a 100644 > --- a/gdb/thread.c > +++ b/gdb/thread.c > @@ -468,8 +468,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, vo= id *arg) > =A0 =A0struct cleanup *cleanup_chain; > =A0 =A0int current_thread =3D -1; > =A0 > - =A0prune_threads (); > - =A0target_find_new_threads (); > + =A0update_thread_list (); > =A0 > =A0 =A0cleanup_chain =3D make_cleanup_ui_out_tuple_begin_end (uiout, "thr= ead-ids"); > =A0 > @@ -748,8 +747,7 @@ print_thread_info (struct ui_out *uiout, int requeste= d_thread, int pid) > =A0 =A0char *extra_info; > =A0 =A0int current_thread =3D -1; > =A0 > - =A0prune_threads (); > - =A0target_find_new_threads (); > + =A0update_thread_list (); > =A0 =A0current_ptid =3D inferior_ptid; > =A0 > =A0 =A0/* We'll be switching threads temporarily. =A0*/ > @@ -759,6 +757,7 @@ print_thread_info (struct ui_out *uiout, int requeste= d_thread, int pid) > =A0 =A0for (tp =3D thread_list; tp; tp =3D tp->next) > =A0 =A0 =A0{ > =A0 =A0 =A0 =A0struct cleanup *chain2; > + =A0 =A0 =A0int core; > =A0 > =A0 =A0 =A0 =A0if (requested_thread !=3D -1 && tp->num !=3D requested_thr= ead) > =A0=A0=A0=A0=A0=A0=A0=A0continue; > @@ -817,6 +816,10 @@ print_thread_info (struct ui_out *uiout, int request= ed_thread, int pid) > =A0=A0=A0=A0=A0=A0=A0=A0 =A0ui_out_field_string (uiout, "state", state); > =A0=A0=A0=A0=A0=A0=A0=A0} > =A0 > + =A0 =A0 =A0core =3D target_core_of_thread (tp->ptid); > + =A0 =A0 =A0if (ui_out_is_mi_like_p (uiout) && core !=3D -1) > +=A0=A0=A0=A0=A0=A0=A0ui_out_field_int (uiout, "core", core); > + > =A0 =A0 =A0 =A0do_cleanups (chain2); > =A0 =A0 =A0} > =A0 > @@ -1058,8 +1061,7 @@ thread_apply_all_command (char *cmd, int from_tty) > =A0 =A0if (cmd =3D=3D NULL || *cmd =3D=3D '\000') > =A0 =A0 =A0error (_("Please specify a command following the thread ID lis= t")); > =A0 > - =A0prune_threads (); > - =A0target_find_new_threads (); > + =A0update_thread_list (); > =A0 > =A0 =A0old_chain =3D make_cleanup_restore_current_thread (); > =A0 > @@ -1245,6 +1247,13 @@ gdb_thread_select (struct ui_out *uiout, char *tid= str, char **error_message) > =A0 =A0return GDB_RC_OK; > =A0} > =A0 > +void > +update_thread_list (void) > +{ > + =A0prune_threads (); > + =A0target_find_new_threads (); > +} > + > =A0/* Commands with a prefix of `thread'. =A0*/ > =A0struct cmd_list_element *thread_cmd_list =3D NULL; > =A0 > diff --git a/gdb/ui-out.c b/gdb/ui-out.c > index 19a4644..6ecdd1f 100644 > --- a/gdb/ui-out.c > +++ b/gdb/ui-out.c > @@ -44,7 +44,7 @@ struct ui_out_hdr > =A0 =A0 is always available. =A0Stack/nested level 0 is reserved for the > =A0 =A0 top-level result. */ > =A0 > -enum { MAX_UI_OUT_LEVELS =3D 6 }; > +enum { MAX_UI_OUT_LEVELS =3D 8 }; > =A0 > =A0struct ui_out_level > =A0 =A0{ > diff --git a/gdb/utils.c b/gdb/utils.c > index 16ad084..c9aa612 100644 > --- a/gdb/utils.c > +++ b/gdb/utils.c > @@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s) > =A0 =A0return argv; > =A0} > =A0 > +int > +compare_positive_ints (const void *ap, const void *bp) > +{ > + =A0/* Because we know we're comparing two ints which are positive, > + =A0 =A0 there's no danger of overflow here. =A0*/ > + =A0return * (int *) ap - * (int *) bp; > +} > + > =A0/* Provide a prototype to silence -Wmissing-prototypes. =A0*/ > =A0extern initialize_file_ftype _initialize_utils; > =A0 Other: - did you consider being able to list the available cores? A candidate for "info os cores", I guess. - your patch adds a bunch of trailing whitespace, would it be possible to get rid of those please? >quilt refresh Warning: trailing whitespace in lines 26273,26309,31859 of gdb/doc/gdb.texi= nfo Warning: trailing whitespace in line 260 of gdb/target.h Warning: trailing whitespace in lines 2351,2352,2404,2408,2420,2452,2454,24= 55 of gdb/remote.c Warning: trailing whitespace in line 101 of gdb/osdata.c Warning: trailing whitespace in line 44 of gdb/osdata.h Warning: trailing whitespace in lines 729,732,737,763,767,770,772,781,784,1= 230 of gdb/gdbserver/server.c Warning: trailing whitespace in lines 2827,2837,2838,2854,2875,2880,2888,28= 92,2894,2959,2982,2986,2987,2988,2992,3274,3284,3285,3299,3304,3311 of gdb/= gdbserver/linux-low.c Warning: trailing whitespace in lines 393,411,438,446,459,469,497,503,504,5= 22,556,557,576,580,581,584,594 of gdb/mi/mi-main.c Refreshed patch core-awareness.diff (quilt refresh --strip-trailing-whitespace does it automaticaly, and I guess git has something similar) - Don't forget to go over the patch and add double-space-after-period everywhere. Sorry for the pickyness. --=20 Pedro Alves