Implement core awareness.
authorVladimir Prus <vladimir@codesourcery.com>
Tue, 12 Jan 2010 21:40:25 +0000 (21:40 +0000)
committerVladimir Prus <vladimir@codesourcery.com>
Tue, 12 Jan 2010 21:40:25 +0000 (21:40 +0000)
* 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.

25 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/bcache.c
gdb/defs.h
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/gdbserver/ChangeLog
gdb/gdbserver/linux-low.c
gdb/gdbserver/remote-utils.c
gdb/gdbserver/server.c
gdb/gdbserver/target.h
gdb/gdbthread.h
gdb/linux-nat.c
gdb/linux-nat.h
gdb/linux-thread-db.c
gdb/mi/mi-interp.c
gdb/mi/mi-main.c
gdb/remote.c
gdb/target.c
gdb/target.h
gdb/testsuite/ChangeLog
gdb/testsuite/lib/mi-support.exp
gdb/thread.c
gdb/ui-out.c
gdb/utils.c

index f771fe05c79b0db5d2fa6e2c406b7339069968cf..d13232c7ae2ac5b405460707933d37a9f220f365 100644 (file)
@@ -1,3 +1,55 @@
+2010-01-13  Vladimir Prus  <vladimir@codesourcery.com>
+
+       Implement core awareness.
+
+       * bcache.c (compare_ints): Remove
+       (print_percentage): Use compare_positive_ints.
+       * defs.h (compare_positive_ints): Declare.
+       * linux-nat.h (struct lin_lwp): New field core.
+       (linux_nat_core_of_thread_1): Declare.
+       * linux-nat.c (add_lwp): Init the 'core' field.
+       (linux_nat_wait_1): Record the core.
+       (linux_nat_core_of_thread_1, linux_nat_core_of_thread): New.
+       (linux_nat_add_target): Register the above.
+       * linux-thread-db.c (update_thread_core): New.
+       (thread_db_find_new_threads): Update core information for
+       every thread.
+       * remote.c (struct private_thread_info): New.
+       (free_private_thread_info, demand_private_info): New.
+       (PACKET_qXfer_threads, use_osdata_threads): New.
+       (struct thread_item, threads_parsing_context
+       (start_thread, end_thread, thread_attributes)
+       (thread_children, threads_children, threads_elements): New.
+       (remote_threads_info): Try qXfer:threads before anything
+       else.
+       (remote_protocol_packets): Register qXfer:threads.
+       (remote_open_1): Init use_osdata_threads.
+       (struct stop_reply): New field 'core'.
+       (remote_parse_stop_reply): Parse core number.
+       (process_stop_reply): Record core number.
+       (remote_xfer_partial): Handle qXfer:threads.
+       (remote_core_of_thread): New.
+       (init_remote_ops): Register remote_core_of_thread.
+       (_initialize_remote): Register qXfer:read.
+       * target.c (target_core_of_thread): New
+       * target.h (enum target_object): New value TARGET_OBJECT_THREADS.
+       (struct target_ops): New field to_core_of_threads.
+       (target_core_of_thread): Declare.
+       * gdbthread.h (struct thread_info): New field private_dtor.
+       * thread.c (print_thread_info): Report the core.
+       * ui-out.c (MAX_UI_OUT_LEVELS): Increase.
+       * utils.c (compare_positive_ints): New.
+       * features/threads.dtd: New.
+       * mi/mi-interp.c (mi_on_normal_stop): Report the core.
+       * mi/mi-main.c (struct collect_cores_data, collect_cores)
+       (do_nothing, free_vector_of_osdata_items)
+       (splay_tree_int_comparator, free_splay_tree): New.
+       (print_one_inferior_data): Implemented printing of selected
+       inferiors.  Collect and print cores.
+       (output_cores): New.
+       (mi_cmd_list_thread_groups): Support --recurse.  Permit specifying
+       thread groups together with --available.
+
 2010-01-12  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
        * configure: Regenerate (for _STRUCTURED_PROC).
index 4d3e02acdb74d0a7973bb8be197bd8a9f7d4deb2..31a0f76f7386c54b2ba826d4675774642b8fa35f 100644 (file)
@@ -444,7 +444,8 @@ RUNTESTFLAGS=
 
 # XML files to build in to GDB.
 XMLFILES = $(srcdir)/features/gdb-target.dtd $(srcdir)/features/xinclude.dtd \
-       $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd
+       $(srcdir)/features/library-list.dtd $(srcdir)/features/osdata.dtd \
+       $(srcdir)/features/threads.dtd
 
 # This is ser-unix.o for any system which supports a v7/BSD/SYSV/POSIX
 # interface to the serial port.  Hopefully if get ported to OS/2, VMS,
index 1bc2eba82043ecf494d13d4a32fc803db9b50843..4badf6edf02b72baeb55a672c9c7655710fb6e93 100644 (file)
@@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
 \f
 /* Printing statistics.  */
 
-static int
-compare_ints (const void *ap, const void *bp)
-{
-  /* Because we know we're comparing two ints which are positive,
-     there's no danger of overflow here.  */
-  return * (int *) ap - * (int *) bp;
-}
-
-
 static void
 print_percentage (int portion, int total)
 {
@@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
 
     /* To compute the median, we need the set of chain lengths sorted.  */
     qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
-          compare_ints);
+          compare_positive_ints);
     qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
-          compare_ints);
+          compare_positive_ints);
 
     if (c->num_buckets > 0)
       {
index 5d251b59a846ad49b492e8be119ea7d2db70edb3..b0a212dd28b5dad6c47fa1d14e89a2d37fb2906a 100644 (file)
@@ -417,6 +417,8 @@ char *ldirname (const char *filename);
 
 char **gdb_buildargv (const char *);
 
+int compare_positive_ints (const void *ap, const void *bp);
+
 /* From demangle.c */
 
 extern void set_demangling_style (char *);
index 0de9f247af26eb2cccd5d8d3354bae3e68edb500..ae9ce2a27390fd3878fb9a066af05df841693ea8 100644 (file)
@@ -1,3 +1,13 @@
+2010-01-13  Vladimir Prus  <vladimir@codesourcery.com>
+
+       * gdb.texinfo (GDB/MI Thread Information): New.
+       (GDB/MI Async Records): Document the core field in *stopped.
+       (GDB/MI Miscellaneous Commands): Expand -list-thread-groups
+       documentation
+       (Process list): Document that osdata document may contain
+       threads.
+       (Remote Serial Protocol): Document qXfer:threads.
+
 2010-01-06  Stan Shebs  <stan@codesourcery.com>
 
        * gdb.texinfo (Starting and Stopping Trace Experiments): Document
index 5b78f50722027d8dae4a19afefbe903db8d624ad..02e2bbd942dc6b40a7880ea347b5d948f7b7aaab 100644 (file)
@@ -15542,6 +15542,10 @@ are:
 @tab @code{qXfer:siginfo:write}
 @tab @code{set $_siginfo}
 
+@item @code{threads}
+@tab @code{qXfer:threads:read}
+@tab @code{info threads}
+
 @item @code{get-thread-local-@*storage-address}
 @tab @code{qGetTLSAddr}
 @tab Displaying @code{__thread} variables
@@ -21828,6 +21832,7 @@ follow development on @email{gdb@@sourceware.org} and
 * GDB/MI Stream Records::
 * GDB/MI Async Records::
 * GDB/MI Frame Information::
+* GDB/MI Thread Information::
 @end menu
 
 @node GDB/MI Result Records
@@ -21920,7 +21925,7 @@ several times, either for different threads, because it cannot resume
 all threads together, or even for a single thread, if the thread must
 be stepped though some code before letting it run freely.
 
-@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}"
+@item *stopped,reason="@var{reason}",thread-id="@var{id}",stopped-threads="@var{stopped}",core="@var{core}"
 The target has stopped.  The @var{reason} field can have one of the
 following values:
 
@@ -21960,7 +21965,9 @@ If all threads are stopped, the @var{stopped} field will have the
 value of @code{"all"}.  Otherwise, the value of the @var{stopped}
 field will be a list of thread identifiers.  Presently, this list will
 always include a single thread, but frontend should be prepared to see
-several threads in the list.
+several threads in the list.  The @var{core} field reports the
+processor core on which the stop event has happened.  This field may be absent
+if such information is not available.
 
 @item =thread-group-created,id="@var{id}"
 @itemx =thread-group-exited,id="@var{id}"
@@ -22037,6 +22044,34 @@ corresponds to the frame's code address.  This field may be absent.
 
 @end table
 
+@node GDB/MI Thread Information
+@subsection @sc{gdb/mi} Thread Information
+
+Whenever @value{GDBN} has to report an information about a thread, it
+uses a tuple with the following fields:
+
+@table @code
+@item id
+The numeric id assigned to the thread by @value{GDBN}.  This field is
+always present.
+
+@item target-id
+Target-specific string identifying the thread.  This field is always present.
+
+@item details
+Additional information about the thread provided by the target.
+It is supposed to be human-readable and not interpreted by the
+frontend.  This field is optional.
+
+@item state
+Either @samp{stopped} or @samp{running}, depending on whether the
+thread is presently running.  This field is always present.
+
+@item core
+The value of this field is an integer number of the processor core the
+thread was last seen on.  This field is optional.
+@end table
+
 
 @c %%%%%%%%%%%%%%%%%%%%%%%%%%%% SECTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 @node GDB/MI Simple Examples
@@ -26349,20 +26384,84 @@ while the target is running.
 @subheading Synopsis
 
 @smallexample
--list-thread-groups [ --available ] [ @var{group} ]
+-list-thread-groups [ --available ] [ --recurse 1 ] [ @var{group} ... ]
 @end smallexample
 
-When used without the @var{group} parameter, lists top-level thread
-groups that are being debugged.  When used with the @var{group}
-parameter, the children of the specified group are listed.  The
-children can be either threads, or other groups.  At present,
-@value{GDBN} will not report both threads and groups as children at
-the same time, but it may change in future.
+Lists thread groups (@pxref{Thread groups}).  When a single thread
+group is passed as the argument, lists the children of that group.
+When several thread group are passed, lists information about those
+thread groups.  Without any parameters, lists information about all
+top-level thread groups.
+
+Normally, thread groups that are being debugged are reported.
+With the @samp{--available} option, @value{GDBN} reports thread groups
+available on the target.
+
+The output of this command may have either a @samp{threads} result or
+a @samp{groups} result.  The @samp{thread} result has a list of tuples
+as value, with each tuple describing a thread (@pxref{GDB/MI Thread
+Information}).  The @samp{groups} result has a list of tuples as value,
+each tuple describing a thread group.  If top-level groups are
+requested (that is, no parameter is passed), or when several groups
+are passed, the output always has a @samp{groups} result.  The format
+of the @samp{group} result is described below.
+
+To reduce the number of roundtrips it's possible to list thread groups
+together with their children, by passing the @samp{--recurse} option
+and the recursion depth.  Presently, only recursion depth of 1 is
+permitted.  If this option is present, then every reported thread group
+will also include its children, either as @samp{group} or
+@samp{threads} field.
+
+In general, any combination of option and parameters is permitted, with
+the following caveats:
+
+@itemize @bullet
+@item
+When a single thread group is passed, the output will typically
+be the @samp{threads} result.  Because threads may not contain
+anything, the @samp{recurse} option will be ignored.
+
+@item
+When the @samp{--available} option is passed, limited information may
+be available.  In particular, the list of threads of a process might
+be inaccessible.  Further, specifying specific thread groups might
+not give any performance advantage over listing all thread groups.
+The frontend should assume that @samp{-list-thread-groups --available}
+is always an expensive operation and cache the results.
+
+@end itemize
+
+The @samp{groups} result is a list of tuples, where each tuple may
+have the following fields:
+
+@table @code
+@item id
+Identifier of the thread group.  This field is always present.
+
+@item type
+The type of the thread group.  At present, only @samp{process} is a
+valid type.
+
+@item pid
+The target-specific process identifier.  This field is only present
+for thread groups of type @samp{process}.
 
-With the @samp{--available} option, instead of reporting groups that
-are been debugged, GDB will report all thread groups available on the
-target.  Using the @samp{--available} option together with @var{group}
-is not allowed.
+@item num_children
+The number of children this thread group has.  This field may be
+absent for an available thread group.
+
+@item threads
+This field has a list of tuples as value, each tuple describing a
+thread.  It may be present if the @samp{--recurse} option is
+specified, and it's actually possible to obtain the threads.
+
+@item cores
+This field is a list of integers, each identifying a core that one
+thread of the group is running on.  This field may be absent if
+such information is not available.
+
+@end table
 
 @subheading Example
 
@@ -26376,6 +26475,16 @@ is not allowed.
 @{id="1",target-id="Thread 0xb7e156b0 (LWP 21254)",
    frame=@{level="0",addr="0x0804891f",func="foo",args=[@{name="i",value="10"@}],
            file="/tmp/a.c",fullname="/tmp/a.c",line="158"@},state="running"@}]]
+-list-thread-groups --available
+^done,groups=[@{id="17",type="process",pid="yyy",num_children="2",cores=[1,2]@}]
+-list-thread-groups --available --recurse 1
+ ^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+                threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+                         @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},..]
+-list-thread-groups --available --recurse 1 17 18
+^done,groups=[@{id="17", types="process",pid="yyy",num_children="2",cores=[1,2],
+               threads=[@{id="1",target-id="Thread 0xb7e14b90",cores=[1]@},
+                        @{id="2",target-id="Thread 0xb7e14b90",cores=[2]@}]@},...]
 @end smallexample
 
 @subheading The @code{-interpreter-exec} Command
@@ -28125,6 +28234,7 @@ Show the current setting of the target wait timeout.
 * File-I/O Remote Protocol Extension::
 * Library List Format::
 * Memory Map Format::
+* Thread List Format::
 @end menu
 
 @node Overview
@@ -29024,6 +29134,10 @@ two-digit hex number.
 If @var{n} is @samp{thread}, then @var{r} is the @var{thread-id} of
 the stopped thread, as specified in @ref{thread-id syntax}.
 
+@item
+If @var{n} is @samp{core}, then @var{r} is the hexadecimal number of
+the core on which the stop event was detected.
+
 @item
 If @var{n} is a recognized @dfn{stop reason}, it describes a more
 specific event that stopped the target.  The currently defined stop
@@ -29058,8 +29172,6 @@ logged execution events, because it has reached the end (or the
 beginning when executing backward) of the log.  The value of @var{r}
 will be either @samp{begin} or @samp{end}.  @xref{Reverse Execution}, 
 for more information.
-
-
 @end table
 
 @item W @var{AA}
@@ -29599,6 +29711,12 @@ These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab Yes
 
+@item @samp{qXfer:threads:read}
+@tab No
+@tab @samp{-}
+@tab Yes
+
+
 @item @samp{QNonStop}
 @tab No
 @tab @samp{-}
@@ -29682,6 +29800,10 @@ The remote stub understands the @samp{qXfer:siginfo:read} packet
 The remote stub understands the @samp{qXfer:siginfo:write} packet
 (@pxref{qXfer siginfo write}).
 
+@item qXfer:threads:read
+The remote stub understands the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}).
+
 @item QNonStop
 The remote stub understands the @samp{QNonStop} packet
 (@pxref{QNonStop}).
@@ -29879,6 +30001,15 @@ This packet is not probed by default; the remote stub must request it,
 by supplying an appropriate @samp{qSupported} response
 (@pxref{qSupported}).
 
+@item qXfer:threads:read::@var{offset},@var{length}
+@anchor{qXfer threads read}
+Access the list of threads on target.  @xref{Thread List Format}.  The
+annex part of the generic @samp{qXfer} packet must be empty
+(@pxref{qXfer read}).
+
+This packet is not probed by default; the remote stub must request it,
+by supplying an appropriate @samp{qSupported} response (@pxref{qSupported}).
+
 @item qXfer:osdata:read::@var{offset},@var{length}
 @anchor{qXfer osdata read}
 Access the target's @dfn{operating system information}.  
@@ -31909,6 +32040,30 @@ The formal DTD for memory map format is given below:
 <!ATTLIST property      name    CDATA   #REQUIRED>
 @end smallexample
 
+@node Thread List Format
+@section Thread List Format
+@cindex thread list format
+
+To efficiently update the list of threads and their attributes,
+@value{GDBN} issues the @samp{qXfer:threads:read} packet
+(@pxref{qXfer threads read}) and obtains the XML document with
+the following structure:
+
+@smallexample
+<?xml version="1.0"?>
+<threads>
+    <thread id="id" core="0">
+    ... description ...
+    </thread>
+</threads>
+@end smallexample
+
+Each @samp{thread} element must have the @samp{id} attribute that
+identifies the thread (@pxref{thread-id syntax}).  The
+@samp{core} attribute, if present, specifies which processor core
+the thread was last executing on.  The content of the of @samp{thread}
+element is interpreted as human-readable auxilliary information.
+
 @include agentexpr.texi
 
 @node Target Descriptions
@@ -32468,6 +32623,7 @@ An example document is:
     <column name="pid">1</column>
     <column name="user">root</column>
     <column name="command">/sbin/init</column>
+    <column name="cores">1,2,3</column>
   </item>
 </osdata>
 @end smallexample
@@ -32475,7 +32631,9 @@ An example document is:
 Each item should include a column whose name is @samp{pid}.  The value
 of that column should identify the process on the target.  The
 @samp{user} and @samp{command} columns are optional, and will be
-displayed by @value{GDBN}.  Target may provide additional columns,
+displayed by @value{GDBN}.  The @samp{cores} column, if present,
+should contain a comma-separated list of cores that this process
+is running on.  Target may provide additional columns,
 which @value{GDBN} currently ignores.
 
 @include gpl.texi
index 3ec151c2fb2de491ead0e5a41a31a0e372d95611..f9479377a397ecbe1843a9a91880593005248bdc 100644 (file)
@@ -1,3 +1,17 @@
+2010-01-13  Vladimir Prus  <vladimir@codesourcery.com>
+
+       * linux-low.c (linux_core_of_thread): New.
+       (compare_ints, show_process, list_threads): New.
+       (linux_qxfer_osdata): Report threads and cores.
+       (linux_target_op): Register linux_core_of_thread.
+       * remote-utils.c (prepare_resume_reply): Report the core.
+       (buffer_xml_printf): Support %d specifier.
+       * server.c (handle_threads_qxfer_proper, handle_threads_qxfer):
+       New.
+       (handle_query): Handle qXfer:threads.  Announce availability
+       thereof.
+       * target.h (struct target_ops): New field core_of_thread.
+
 2010-01-04  Ulrich Weigand  <uweigand@de.ibm.com>
 
        * Makefile.in (clean): Remove new generated files.
index c20322e6540d432a4a91820c81494e2a85fbea7e..558469162bd06668722bbf0f09f1da2902946db2 100644 (file)
@@ -140,6 +140,7 @@ static int check_removed_breakpoint (struct lwp_info *event_child);
 static void *add_lwp (ptid_t ptid);
 static int linux_stopped_by_watchpoint (void);
 static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
+static int linux_core_of_thread (ptid_t ptid);
 
 struct pending_signals
 {
@@ -2801,6 +2802,175 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
 }
 #endif
 
+static int
+compare_ints (const void *xa, const void *xb)
+{
+  int a = *(const int *)xa;
+  int b = *(const int *)xb;
+
+  return a - b;
+}
+
+static int *
+unique (int *b, int *e)
+{
+  int *d = b;
+  while (++b != e)
+    if (*d != *b)
+      *++d = *b;
+  return ++d;
+}
+
+/* Given PID, iterates over all threads in that process.
+
+   Information about each thread, in a format suitable for qXfer:osdata:thread
+   is printed to BUFFER, if it's not NULL.  BUFFER is assumed to be already
+   initialized, and the caller is responsible for finishing and appending '\0'
+   to it.
+
+   The list of cores that threads are running on is assigned to *CORES, if it
+   is not NULL.  If no cores are found, *CORES will be set to NULL.  Caller
+   should free *CORES.  */
+
+static void
+list_threads (int pid, struct buffer *buffer, char **cores)
+{
+  int count = 0;
+  int allocated = 10;
+  int *core_numbers = xmalloc (sizeof (int) * allocated);
+  char pathname[128];
+  DIR *dir;
+  struct dirent *dp;
+  struct stat statbuf;
+
+  sprintf (pathname, "/proc/%d/task", pid);
+  if (stat (pathname, &statbuf) == 0 && S_ISDIR (statbuf.st_mode))
+    {
+      dir = opendir (pathname);
+      if (!dir)
+       {
+         free (core_numbers);
+         return;
+       }
+
+      while ((dp = readdir (dir)) != NULL)
+       {
+         unsigned long lwp = strtoul (dp->d_name, NULL, 10);
+
+         if (lwp != 0)
+           {
+             unsigned core = linux_core_of_thread (ptid_build (pid, lwp, 0));
+
+             if (core != -1)
+               {
+                 char s[sizeof ("4294967295")];
+                 sprintf (s, "%u", core);
+
+                 if (count == allocated)
+                   {
+                     allocated *= 2;
+                     core_numbers = realloc (core_numbers,
+                                             sizeof (int) * allocated);
+                   }
+                 core_numbers[count++] = core;
+                 if (buffer)
+                   buffer_xml_printf (buffer,
+                                      "<item>"
+                                      "<column name=\"pid\">%d</column>"
+                                      "<column name=\"tid\">%s</column>"
+                                      "<column name=\"core\">%s</column>"
+                                      "</item>", pid, dp->d_name, s);
+               }
+             else
+               {
+                 if (buffer)
+                   buffer_xml_printf (buffer,
+                                      "<item>"
+                                      "<column name=\"pid\">%d</column>"
+                                      "<column name=\"tid\">%s</column>"
+                                      "</item>", pid, dp->d_name);
+               }
+           }
+       }
+    }
+
+  if (cores)
+    {
+      *cores = NULL;
+      if (count > 0)
+       {
+         struct buffer buffer2;
+         int *b;
+         int *e;
+         qsort (core_numbers, count, sizeof (int), compare_ints);
+
+         /* Remove duplicates. */
+         b = core_numbers;
+         e = unique (b, core_numbers + count);
+
+         buffer_init (&buffer2);
+
+         for (b = core_numbers; b != e; ++b)
+           {
+             char number[sizeof ("4294967295")];
+             sprintf (number, "%u", *b);
+             buffer_xml_printf (&buffer2, "%s%s",
+                                (b == core_numbers) ? "" : ",", number);
+           }
+         buffer_grow_str0 (&buffer2, "");
+
+         *cores = buffer_finish (&buffer2);
+       }
+    }
+  free (core_numbers);
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+  char pathname[128];
+  FILE *f;
+  char cmd[MAXPATHLEN + 1];
+
+  sprintf (pathname, "/proc/%d/cmdline", pid);
+
+  if ((f = fopen (pathname, "r")) != NULL)
+    {
+      size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
+      if (len > 0)
+       {
+         char *cores = 0;
+         int i;
+         for (i = 0; i < len; i++)
+           if (cmd[i] == '\0')
+             cmd[i] = ' ';
+         cmd[len] = '\0';
+
+         buffer_xml_printf (buffer,
+                            "<item>"
+                            "<column name=\"pid\">%d</column>"
+                            "<column name=\"user\">%s</column>"
+                            "<column name=\"command\">%s</column>",
+                            pid,
+                            username,
+                            cmd);
+
+         /* This only collects core numbers, and does not print threads.  */
+         list_threads (pid, NULL, &cores);
+
+         if (cores)
+           {
+             buffer_xml_printf (buffer,
+                                "<column name=\"cores\">%s</column>", cores);
+             free (cores);
+           }
+
+         buffer_xml_printf (buffer, "</item>");
+       }
+      fclose (f);
+    }
+}
+
 static int
 linux_qxfer_osdata (const char *annex,
                    unsigned char *readbuf, unsigned const char *writebuf,
@@ -2811,10 +2981,16 @@ linux_qxfer_osdata (const char *annex,
   static const char *buf;
   static long len_avail = -1;
   static struct buffer buffer;
+  int processes = 0;
+  int threads = 0;
 
   DIR *dirp;
 
-  if (strcmp (annex, "processes") != 0)
+  if (strcmp (annex, "processes") == 0)
+    processes = 1;
+  else if (strcmp (annex, "threads") == 0)
+    threads = 1;
+  else
     return 0;
 
   if (!readbuf || writebuf)
@@ -2827,7 +3003,10 @@ linux_qxfer_osdata (const char *annex,
       len_avail = 0;
       buf = NULL;
       buffer_init (&buffer);
-      buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+      if (processes)
+       buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+      else if (threads)
+       buffer_grow_str (&buffer, "<osdata type=\"threads\">");
 
       dirp = opendir ("/proc");
       if (dirp)
@@ -2846,37 +3025,16 @@ linux_qxfer_osdata (const char *annex,
             if (stat (procentry, &statbuf) == 0
                 && S_ISDIR (statbuf.st_mode))
               {
-                char pathname[128];
-                FILE *f;
-                char cmd[MAXPATHLEN + 1];
-                struct passwd *entry;
-
-                sprintf (pathname, "/proc/%s/cmdline", dp->d_name);
-                entry = getpwuid (statbuf.st_uid);
+                int pid = (int) strtoul (dp->d_name, NULL, 10);
 
-                if ((f = fopen (pathname, "r")) != NULL)
+                if (processes)
                   {
-                    size_t len = fread (cmd, 1, sizeof (cmd) - 1, f);
-                    if (len > 0)
-                      {
-                        int i;
-                        for (i = 0; i < len; i++)
-                          if (cmd[i] == '\0')
-                            cmd[i] = ' ';
-                        cmd[len] = '\0';
-
-                        buffer_xml_printf (
-                          &buffer,
-                          "<item>"
-                          "<column name=\"pid\">%s</column>"
-                          "<column name=\"user\">%s</column>"
-                          "<column name=\"command\">%s</column>"
-                          "</item>",
-                          dp->d_name,
-                          entry ? entry->pw_name : "?",
-                          cmd);
-                      }
-                    fclose (f);
+                    struct passwd *entry = getpwuid (statbuf.st_uid);
+                    show_process (pid, entry ? entry->pw_name : "?", &buffer);
+                  }
+                else if (threads)
+                  {
+                    list_threads (pid, &buffer, NULL);
                   }
               }
           }
@@ -3152,6 +3310,55 @@ linux_qxfer_spu (const char *annex, unsigned char *readbuf,
   return ret;
 }
 
+static int
+linux_core_of_thread (ptid_t ptid)
+{
+  char filename[sizeof ("/proc//task//stat")
+                + 2 * 20 /* decimal digits for 2 numbers, max 2^64 bit each */
+                + 1];
+  FILE *f;
+  char *content = NULL;
+  char *p;
+  char *ts = 0;
+  int content_read = 0;
+  int i;
+  int core;
+
+  sprintf (filename, "/proc/%d/task/%ld/stat",
+          ptid_get_pid (ptid), ptid_get_lwp (ptid));
+  f = fopen (filename, "r");
+  if (!f)
+    return -1;
+
+  for (;;)
+    {
+      int n;
+      content = realloc (content, content_read + 1024);
+      n = fread (content + content_read, 1, 1024, f);
+      content_read += n;
+      if (n < 1024)
+       {
+         content[content_read] = '\0';
+         break;
+       }
+    }
+
+  p = strchr (content, '(');
+  p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+  p = strtok_r (p, " ", &ts);
+  for (i = 0; i != 36; ++i)
+    p = strtok_r (NULL, " ", &ts);
+
+  if (sscanf (p, "%d", &core) == 0)
+    core = -1;
+
+  free (content);
+  fclose (f);
+
+  return core;
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_attach,
@@ -3191,10 +3398,11 @@ static struct target_ops linux_target_ops = {
   linux_start_non_stop,
   linux_supports_multi_process,
 #ifdef USE_THREAD_DB
-  thread_db_handle_monitor_command
+  thread_db_handle_monitor_command,
 #else
-  NULL
+  NULL,
 #endif
+  linux_core_of_thread
 };
 
 static void
index 9b5bad868b51ac391a89172503c59ac98f43d923..76953aed42b19bd059316681774b7469bd5704da 100644 (file)
@@ -1170,6 +1170,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
               gdbserver to know what inferior_ptid is.  */
            if (1 || !ptid_equal (general_thread, ptid))
              {
+               int core = -1;
                /* In non-stop, don't change the general thread behind
                   GDB's back.  */
                if (!non_stop)
@@ -1179,6 +1180,17 @@ prepare_resume_reply (char *buf, ptid_t ptid,
                buf = write_ptid (buf, ptid);
                strcat (buf, ";");
                buf += strlen (buf);
+
+               if (the_target->core_of_thread)
+                 core = (*the_target->core_of_thread) (ptid);
+               if (core != -1)
+                 {
+                   sprintf (buf, "core:");
+                   buf += strlen (buf);
+                   sprintf (buf, "%x", core);
+                   strcat (buf, ";");
+                   buf += strlen (buf);
+                 }
              }
          }
 
@@ -1604,6 +1616,16 @@ buffer_xml_printf (struct buffer *buffer, const char *format, ...)
               prev = f + 1;
             }
             break;
+          case 'd':
+            {
+              int i = va_arg (ap, int);
+              char b[sizeof ("4294967295")];
+
+              buffer_grow (buffer, prev, f - prev - 1);
+              sprintf (b, "%d", i);
+              buffer_grow_str (buffer, b);
+              prev = f + 1;
+            }
           }
         percent = 0;
        }
index 92541216c706291171318ceedf6362a4f38acf3d..c7815521448b73ea612eb3c4a0559fe2d3d1b77b 100644 (file)
@@ -707,6 +707,87 @@ handle_monitor_command (char *mon)
     }
 }
 
+static void
+handle_threads_qxfer_proper (struct buffer *buffer)
+{
+  struct inferior_list_entry *thread;
+
+  buffer_grow_str (buffer, "<threads>\n");
+
+  for (thread = all_threads.head; thread; thread = thread->next)
+    {
+      ptid_t ptid = thread_to_gdb_id ((struct thread_info *)thread);
+      char ptid_s[100];
+      int core = -1;
+      char core_s[21];
+
+      write_ptid (ptid_s, ptid);
+
+      if (the_target->core_of_thread)
+       core = (*the_target->core_of_thread) (ptid);
+
+      if (core != -1)
+       {
+         sprintf (core_s, "%d", core);
+         buffer_xml_printf (buffer, "<thread id=\"%s\" core=\"%s\"/>\n",
+                            ptid_s, core_s);
+       }
+      else
+       {
+         buffer_xml_printf (buffer, "<thread id=\"%s\"/>\n",
+                            ptid_s);
+       }
+    }
+
+  buffer_grow_str0 (buffer, "</threads>\n");
+}
+
+static int
+handle_threads_qxfer (const char *annex,
+                     unsigned char *readbuf,
+                     CORE_ADDR offset, int length)
+{
+  static char *result = 0;
+  static unsigned int result_length = 0;
+
+  if (annex && strcmp (annex, "") != 0)
+    return 0;
+
+  if (offset == 0)
+    {
+      struct buffer buffer;
+      /* When asked for data at offset 0, generate everything and store into
+        'result'.  Successive reads will be served off 'result'.  */
+      if (result)
+       free (result);
+
+      buffer_init (&buffer);
+
+      handle_threads_qxfer_proper (&buffer);
+
+      result = buffer_finish (&buffer);
+      result_length = strlen (result);
+      buffer_free (&buffer);
+    }
+
+  if (offset >= result_length)
+    {
+      /* We're out of data.  */
+      free (result);
+      result = NULL;
+      result_length = 0;
+      return 0;
+    }
+
+  if (length > result_length - offset)
+    length = result_length - offset;
+
+  memcpy (readbuf, result + offset, length);
+
+  return length;
+
+}
+
 /* Handle all of the extended 'q' packets.  */
 void
 handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
@@ -1112,6 +1193,43 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       return;
     }
 
+  if (strncmp ("qXfer:threads:read:", own_buf, 19) == 0)
+    {
+      unsigned char *data;
+      int n;
+      CORE_ADDR ofs;
+      unsigned int len;
+      char *annex;
+
+      require_running (own_buf);
+
+      /* Reject any annex; grab the offset and length.  */
+      if (decode_xfer_read (own_buf + 19, &annex, &ofs, &len) < 0
+         || annex[0] != '\0')
+       {
+         strcpy (own_buf, "E00");
+         return;
+       }
+
+      /* Read one extra byte, as an indicator of whether there is
+        more.  */
+      if (len > PBUFSIZ - 2)
+       len = PBUFSIZ - 2;
+      data = malloc (len + 1);
+      if (!data)
+       return;
+      n = handle_threads_qxfer (annex, data, ofs, len + 1);
+      if (n < 0)
+       write_enn (own_buf);
+      else if (n > len)
+       *new_packet_len_p = write_qxfer_response (own_buf, data, len, 1);
+      else
+       *new_packet_len_p = write_qxfer_response (own_buf, data, n, 0);
+
+      free (data);
+      return;
+    }
+
   /* Protocol features query.  */
   if (strncmp ("qSupported", own_buf, 10) == 0
       && (own_buf[10] == ':' || own_buf[10] == '\0'))
@@ -1168,6 +1286,8 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (target_supports_non_stop ())
        strcat (own_buf, ";QNonStop+");
 
+      strcat (own_buf, ";qXfer:threads:read+");
+
       return;
     }
 
index ad21eb7eb4e2788946b7960240af8b593fc41838..0c761e91f2feda803fbd90137bf48f3255141efd 100644 (file)
@@ -283,6 +283,9 @@ struct target_ops
   /* If not NULL, target-specific routine to process monitor command.
      Returns 1 if handled, or 0 to perform default processing.  */
   int (*handle_monitor_command) (char *);
+
+  /* Returns the core given a thread, or -1 if not known.  */
+  int (*core_of_thread) (ptid_t);
 };
 
 extern struct target_ops *the_target;
index 4ab211174a89f218d754b50e42f194b4b93809b9..cd24eaf2fb08e57aae782c55caa635b3cfb0e95b 100644 (file)
@@ -187,6 +187,10 @@ struct thread_info
 
   /* Private data used by the target vector implementation.  */
   struct private_thread_info *private;
+
+  /* Function that is called to free PRIVATE.  If this is NULL, then
+     xfree will be called on PRIVATE.  */
+  void (*private_dtor) (struct private_thread_info *);
 };
 
 /* Create an empty thread list, or empty the existing one.  */
@@ -346,4 +350,6 @@ extern struct cleanup *make_cleanup_restore_current_thread (void);
    INFERIOR_PTID.  INFERIOR_PTID *must* be in the thread list.  */
 extern struct thread_info* inferior_thread (void);
 
+extern void update_thread_list (void);
+
 #endif /* GDBTHREAD_H */
index 48ea1bc46c81b40c72f83eb5109f4f66e6f2222b..bf0a5f1bdc5e1343a309ddb3c98d600e95b5e88e 100644 (file)
@@ -1184,6 +1184,7 @@ add_lwp (ptid_t ptid)
   lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
 
   lp->ptid = ptid;
+  lp->core = -1;
 
   lp->next = lwp_list;
   lwp_list = lp;
@@ -3642,6 +3643,7 @@ retry:
     fprintf_unfiltered (gdb_stdlog, "LLW: exit\n");
 
   restore_child_signals_mask (&prev_mask);
+  lp->core = linux_nat_core_of_thread_1 (lp->ptid);
   return lp->ptid;
 }
 
@@ -5423,6 +5425,75 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
   return inf->aspace;
 }
 
+int
+linux_nat_core_of_thread_1 (ptid_t ptid)
+{
+  struct cleanup *back_to;
+  char *filename;
+  FILE *f;
+  char *content = NULL;
+  char *p;
+  char *ts = 0;
+  int content_read = 0;
+  int i;
+  int core;
+
+  filename = xstrprintf ("/proc/%d/task/%ld/stat",
+                        GET_PID (ptid), GET_LWP (ptid));
+  back_to = make_cleanup (xfree, filename);
+
+  f = fopen (filename, "r");
+  if (!f)
+    {
+      do_cleanups (back_to);
+      return -1;
+    }
+
+  make_cleanup_fclose (f);
+
+  for (;;)
+    {
+      int n;
+      content = xrealloc (content, content_read + 1024);
+      n = fread (content + content_read, 1, 1024, f);
+      content_read += n;
+      if (n < 1024)
+       {
+         content[content_read] = '\0';
+         break;
+       }
+    }
+
+  make_cleanup (xfree, content);
+
+  p = strchr (content, '(');
+  p = strchr (p, ')') + 2; /* skip ")" and a whitespace. */
+
+  /* If the first field after program name has index 0, then core number is
+     the field with index 36.  There's no constant for that anywhere.  */
+  p = strtok_r (p, " ", &ts);
+  for (i = 0; i != 36; ++i)
+    p = strtok_r (NULL, " ", &ts);
+
+  if (sscanf (p, "%d", &core) == 0)
+    core = -1;
+
+  do_cleanups (back_to);
+
+  return core;
+}
+
+/* Return the cached value of the processor core for thread PTID.  */
+
+int
+linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+  struct lwp_info *info = find_lwp_pid (ptid);
+  if (info)
+    return info->core;
+  return -1;
+}
+
 void
 linux_nat_add_target (struct target_ops *t)
 {
@@ -5463,6 +5534,8 @@ linux_nat_add_target (struct target_ops *t)
 
   t->to_supports_multi_process = linux_nat_supports_multi_process;
 
+  t->to_core_of_thread = linux_nat_core_of_thread;
+
   /* We don't change the stratum; this target will sit at
      process_stratum and thread_db will set at thread_stratum.  This
      is a little strange, since this is a multi-threaded-capable
index 9537740c553fd04f5e99371309d096f4814e5a14..5aba089eaf3d20a703b0f7db9ed8607a0b3d375e 100644 (file)
@@ -89,6 +89,9 @@ struct lwp_info
      - TARGET_WAITKIND_SYSCALL_RETURN */
   int syscall_state;
 
+  /* The processor core this LWP was last seen on.  */
+  int core;
+
   /* Next LWP in list.  */
   struct lwp_info *next;
 };
@@ -163,3 +166,6 @@ void linux_nat_switch_fork (ptid_t new_ptid);
 
 /* Return the saved siginfo associated with PTID.  */
 struct siginfo *linux_nat_get_siginfo (ptid_t ptid);
+
+/* Compute and return the processor core of a given thread.  */
+int linux_nat_core_of_thread_1 (ptid_t ptid);
index 659d99d9347a95467066c6f513ff0f2af3cd9aef..2c66da7e2beba278f50232a898ee24f638e1302c 100644 (file)
@@ -1454,6 +1454,12 @@ thread_db_find_new_threads_1 (ptid_t ptid)
   thread_db_find_new_threads_2 (ptid, 0);
 }
 
+static int
+update_thread_core (struct lwp_info *info, void *closure)
+{
+  info->core = linux_nat_core_of_thread_1 (info->ptid);
+  return 0;
+}
 
 static void
 thread_db_find_new_threads (struct target_ops *ops)
@@ -1466,6 +1472,9 @@ thread_db_find_new_threads (struct target_ops *ops)
     return;
 
   thread_db_find_new_threads_1 (inferior_ptid);
+
+  iterate_over_lwps (minus_one_ptid /* iterate over all */,
+                    update_thread_core, NULL);
 }
 
 static char *
index 96e458c3bd081d482a8833fda811ef8a595cdd73..41388bb841bee5f2ea07005bb673d64792fe2c23 100644 (file)
@@ -339,6 +339,7 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
 
   if (print_frame)
     {
+      int core;
       if (uiout != mi_uiout)
        {
          /* The normal_stop function has printed frame information into 
@@ -364,6 +365,10 @@ mi_on_normal_stop (struct bpstats *bs, int print_frame)
        }
       else
        ui_out_field_string (mi_uiout, "stopped-threads", "all");
+
+      core = target_core_of_thread (inferior_ptid);
+      if (core != -1)
+       ui_out_field_int (mi_uiout, "core", core);
     }
   
   fputs_unfiltered ("*stopped", raw_stdout);
index 16f610207476cf17a6c0586e4efb711ddb97cdc1..aee246ceb71274266b30f268d022e0964bf4a1b4 100644 (file)
@@ -50,6 +50,7 @@
 #include "valprint.h"
 #include "inferior.h"
 #include "osdata.h"
+#include "splay-tree.h"
 
 #include <ctype.h>
 #include <sys/time.h>
@@ -360,11 +361,55 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
   print_thread_info (uiout, thread, -1);
 }
 
+struct collect_cores_data
+{
+  int pid;
+
+  VEC (int) *cores;
+};
+
+static int
+collect_cores (struct thread_info *ti, void *xdata)
+{
+  struct collect_cores_data *data = xdata;
+
+  if (ptid_get_pid (ti->ptid) == data->pid)
+    {
+      int core = target_core_of_thread (ti->ptid);
+      if (core != -1)
+       VEC_safe_push (int, data->cores, core);
+    }
+
+  return 0;
+}
+
+static int *
+unique (int *b, int *e)
+{
+  int *d = b;
+  while (++b != e)
+    if (*d != *b)
+      *++d = *b;
+  return ++d;
+}
+
+struct print_one_inferior_data
+{
+  int recurse;
+  VEC (int) *inferiors;
+};
+
 static int
-print_one_inferior (struct inferior *inferior, void *arg)
+print_one_inferior (struct inferior *inferior, void *xdata)
 {
-  if (inferior->pid != 0)
+  struct print_one_inferior_data *top_data = xdata;
+
+  if (VEC_empty (int, top_data->inferiors)
+      || bsearch (&(inferior->pid), VEC_address (int, top_data->inferiors),
+                 VEC_length (int, top_data->inferiors), sizeof (int),
+                 compare_positive_ints))
     {
+      struct collect_cores_data data;
       struct cleanup *back_to
        = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
 
@@ -372,81 +417,299 @@ print_one_inferior (struct inferior *inferior, void *arg)
       ui_out_field_string (uiout, "type", "process");
       ui_out_field_int (uiout, "pid", inferior->pid);
 
+      data.pid = inferior->pid;
+      data.cores = 0;
+      iterate_over_threads (collect_cores, &data);
+
+      if (!VEC_empty (int, data.cores))
+       {
+         int elt;
+         int i;
+         int *b, *e;
+         struct cleanup *back_to_2 =
+           make_cleanup_ui_out_list_begin_end (uiout, "cores");
+
+         qsort (VEC_address (int, data.cores),
+                VEC_length (int, data.cores), sizeof (int),
+                compare_positive_ints);
+
+         b = VEC_address (int, data.cores);
+         e = b + VEC_length (int, data.cores);
+         e = unique (b, e);
+
+         for (; b != e; ++b)
+           ui_out_field_int (uiout, NULL, *b);
+
+         do_cleanups (back_to_2);
+       }
+
+      if (top_data->recurse)
+       print_thread_info (uiout, -1, inferior->pid);
+
       do_cleanups (back_to);
     }
 
   return 0;
 }
 
-void
-mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+/* Output a field named 'cores' with a list as the value.  The elements of
+   the list are obtained by splitting 'cores' on comma.  */
+
+static void
+output_cores (struct ui_out *uiout, const char *field_name, const char *xcores)
 {
-  struct cleanup *back_to;
-  int available = 0;
-  char *id = NULL;
+  struct cleanup *back_to = make_cleanup_ui_out_list_begin_end (uiout,
+                                                               field_name);
+  char *cores = xstrdup (xcores);
+  char *p = cores;
 
-  if (argc > 0 && strcmp (argv[0], "--available") == 0)
-    {
-      ++argv;
-      --argc;
-      available = 1;
-    }
+  make_cleanup (xfree, cores);
 
-  if (argc > 0)
-    id = argv[0];
+  for (p = strtok (p, ","); p;  p = strtok (NULL, ","))
+    ui_out_field_string (uiout, NULL, p);
 
-  back_to = make_cleanup (null_cleanup, NULL);
+  do_cleanups (back_to);
+}
 
-  if (available && id)
-    {
-      error (_("Can only report top-level available thread groups"));
-    }
-  else if (available)
-    {
-      struct osdata *data;
-      struct osdata_item *item;
-      int ix_items;
+static void
+free_vector_of_ints (void *xvector)
+{
+  VEC (int) **vector = xvector;
+  VEC_free (int, *vector);
+}
 
-      data = get_osdata ("processes");
-      make_cleanup_osdata_free (data);
+static void
+do_nothing (splay_tree_key k)
+{
+}
 
-      make_cleanup_ui_out_list_begin_end (uiout, "groups");
+static void
+free_vector_of_osdata_items (splay_tree_value xvalue)
+{
+  VEC (osdata_item_s) *value = (VEC (osdata_item_s) *) xvalue;
+  /* We don't free the items itself, it will be done separately.  */
+  VEC_free (osdata_item_s, value);
+}
+
+static int
+splay_tree_int_comparator (splay_tree_key xa, splay_tree_key xb)
+{
+  int a = xa;
+  int b = xb;
+  return a - b;
+}
+
+static void
+free_splay_tree (void *xt)
+{
+  splay_tree t = xt;
+  splay_tree_delete (t);
+}
+
+static void
+list_available_thread_groups (VEC (int) *ids, int recurse)
+{
+  struct osdata *data;
+  struct osdata_item *item;
+  int ix_items;
+  /* This keeps a map from integer (pid) to VEC (struct osdata_item *)*
+     The vector contains information about all threads for the given
+     pid.  */
+  splay_tree tree;
+
+  /* get_osdata will throw if it cannot return data.  */
+  data = get_osdata ("processes");
+  make_cleanup_osdata_free (data);
+
+  if (recurse)
+    {
+      struct osdata *threads = get_osdata ("threads");
+      make_cleanup_osdata_free (threads);
+
+      tree = splay_tree_new (splay_tree_int_comparator,
+                            do_nothing,
+                            free_vector_of_osdata_items);
+      make_cleanup (free_splay_tree, tree);
 
       for (ix_items = 0;
-          VEC_iterate (osdata_item_s, data->items,
+          VEC_iterate (osdata_item_s, threads->items,
                        ix_items, item);
           ix_items++)
        {
-         struct cleanup *back_to =
-           make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
-
          const char *pid = get_osdata_column (item, "pid");
-         const char *cmd = get_osdata_column (item, "command");
-         const char *user = get_osdata_column (item, "user");
+         int pid_i = strtoul (pid, NULL, 0);
+         VEC (osdata_item_s) *vec = 0;
+
+         splay_tree_node n = splay_tree_lookup (tree, pid_i);
+         if (!n)
+           {
+             VEC_safe_push (osdata_item_s, vec, item);
+             splay_tree_insert (tree, pid_i, (splay_tree_value)vec);
+           }
+         else
+           {
+             vec = (VEC (osdata_item_s) *) n->value;
+             VEC_safe_push (osdata_item_s, vec, item);
+             n->value = (splay_tree_value) vec;
+           }
+       }
+    }
+
+  make_cleanup_ui_out_list_begin_end (uiout, "groups");
+
+  for (ix_items = 0;
+       VEC_iterate (osdata_item_s, data->items,
+                   ix_items, item);
+       ix_items++)
+    {
+      struct cleanup *back_to;
+
+      const char *pid = get_osdata_column (item, "pid");
+      const char *cmd = get_osdata_column (item, "command");
+      const char *user = get_osdata_column (item, "user");
+      const char *cores = get_osdata_column (item, "cores");
+
+      int pid_i = strtoul (pid, NULL, 0);
+
+      /* At present, the target will return all available processes
+        and if information about specific ones was required, we filter
+        undesired processes here.  */
+      if (ids && bsearch (&pid_i, VEC_address (int, ids),
+                         VEC_length (int, ids),
+                         sizeof (int), compare_positive_ints) == NULL)
+       continue;
+
 
-         ui_out_field_fmt (uiout, "id", "%s", pid);
-         ui_out_field_string (uiout, "type", "process");
-         if (cmd)
-           ui_out_field_string (uiout, "description", cmd);
-         if (user)
-           ui_out_field_string (uiout, "user", user);
+      back_to = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
 
-         do_cleanups (back_to);
+      ui_out_field_fmt (uiout, "id", "%s", pid);
+      ui_out_field_string (uiout, "type", "process");
+      if (cmd)
+       ui_out_field_string (uiout, "description", cmd);
+      if (user)
+       ui_out_field_string (uiout, "user", user);
+      if (cores)
+       output_cores (uiout, "cores", cores);
+
+      if (recurse)
+       {
+         splay_tree_node n = splay_tree_lookup (tree, pid_i);
+         if (n)
+           {
+             VEC (osdata_item_s) *children = (VEC (osdata_item_s) *) n->value;
+             struct osdata_item *child;
+             int ix_child;
+
+             make_cleanup_ui_out_list_begin_end (uiout, "threads");
+
+             for (ix_child = 0;
+                  VEC_iterate (osdata_item_s, children, ix_child, child);
+                  ++ix_child)
+               {
+                 struct cleanup *back_to_2 =
+                   make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+
+                 const char *tid = get_osdata_column (child, "tid");
+                 const char *tcore = get_osdata_column (child, "core");
+                 ui_out_field_string (uiout, "id", tid);
+                 if (tcore)
+                   ui_out_field_string (uiout, "core", tcore);
+
+                 do_cleanups (back_to_2);
+               }
+           }
        }
+
+      do_cleanups (back_to);
     }
-  else if (id)
+}
+
+void
+mi_cmd_list_thread_groups (char *command, char **argv, int argc)
+{
+  struct cleanup *back_to;
+  int available = 0;
+  int recurse = 0;
+  VEC (int) *ids = 0;
+
+  enum opt
     {
-      int pid = atoi (id);
+      AVAILABLE_OPT, RECURSE_OPT
+    };
+  static struct mi_opt opts[] =
+  {
+    {"-available", AVAILABLE_OPT, 0},
+    {"-recurse", RECURSE_OPT, 1},
+    { 0, 0, 0 }
+  };
+
+  int optind = 0;
+  char *optarg;
+
+  while (1)
+    {
+      int opt = mi_getopt ("-list-thread-groups", argc, argv, opts,
+                          &optind, &optarg);
+      if (opt < 0)
+       break;
+      switch ((enum opt) opt)
+       {
+       case AVAILABLE_OPT:
+         available = 1;
+         break;
+       case RECURSE_OPT:
+         if (strcmp (optarg, "0") == 0)
+           ;
+         else if (strcmp (optarg, "1") == 0)
+           recurse = 1;
+         else
+           error ("only '0' and '1' are valid values for the '--recurse' option");
+         break;
+       }
+    }
+
+  for (; optind < argc; ++optind)
+    {
+      char *end;
+      int inf = strtoul (argv[optind], &end, 0);
+      if (*end != '\0')
+       error ("invalid group id '%s'", argv[optind]);
+      VEC_safe_push (int, ids, inf);
+    }
+  if (VEC_length (int, ids) > 1)
+    qsort (VEC_address (int, ids),
+          VEC_length (int, ids),
+          sizeof (int), compare_positive_ints);
+
+  back_to = make_cleanup (free_vector_of_ints, &ids);
+
+  if (available)
+    {
+      list_available_thread_groups (ids, recurse);
+    }
+  else if (VEC_length (int, ids) == 1)
+    {
+      /* Local thread groups, single id. */
+      int pid = *VEC_address (int, ids);
       if (!in_inferior_list (pid))
-       error ("Invalid thread group id '%s'", id);
-      print_thread_info (uiout, -1, pid);    
+       error ("Invalid thread group id '%d'", pid);
+      print_thread_info (uiout, -1, pid);
     }
   else
     {
+      struct print_one_inferior_data data;
+      data.recurse = recurse;
+      data.inferiors = ids;
+
+      /* Local thread groups.  Either no explicit ids -- and we
+        print everything, or several explicit ids.  In both cases,
+        we print more than one group, and have to use 'groups'
+        as the top-level element.  */
       make_cleanup_ui_out_list_begin_end (uiout, "groups");
-      iterate_over_inferiors (print_one_inferior, NULL);
+      update_thread_list ();
+      iterate_over_inferiors (print_one_inferior, &data);
     }
-  
+
   do_cleanups (back_to);
 }
 
index 9c50f7ef928b983f45c47d3b4b7ef518bfd95dec..f492082b8f36f0238b0ee287ed05e1a7bd714ea8 100644 (file)
@@ -60,6 +60,7 @@
 #include "remote-fileio.h"
 #include "gdb/fileio.h"
 #include "gdb_stat.h"
+#include "xml-support.h"
 
 #include "memory-map.h"
 
@@ -324,6 +325,20 @@ struct remote_state
   int ctrlc_pending_p;
 };
 
+/* Private data that we'll store in (struct thread_info)->private.  */
+struct private_thread_info
+{
+  char *extra;
+  int core;
+};
+
+static void
+free_private_thread_info (struct private_thread_info *info)
+{
+  xfree (info->extra);
+  xfree (info);
+}
+
 /* Returns true if the multi-process extensions are in effect.  */
 static int
 remote_multi_process_p (struct remote_state *rs)
@@ -1121,6 +1136,7 @@ enum {
   PACKET_qXfer_spu_read,
   PACKET_qXfer_spu_write,
   PACKET_qXfer_osdata,
+  PACKET_qXfer_threads,
   PACKET_qGetTLSAddr,
   PACKET_qSupported,
   PACKET_QPassSignals,
@@ -1395,7 +1411,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
              remote_add_thread (currthread, running);
              inferior_ptid = currthread;
            }
-         return;
+         return;
        }
 
       if (ptid_equal (magic_null_ptid, inferior_ptid))
@@ -1405,7 +1421,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
             doesn't support qC.  This is the first stop reported
             after an attach, so this is the main thread.  Update the
             ptid in the thread list.  */
-         thread_change_ptid (inferior_ptid, currthread);
+         thread_change_ptid (inferior_ptid, currthread);
          return;
        }
 
@@ -1427,6 +1443,26 @@ remote_notice_new_inferior (ptid_t currthread, int running)
     }
 }
 
+/* Return the private thread data, creating it if necessary.  */
+
+struct private_thread_info *
+demand_private_info (ptid_t ptid)
+{
+  struct thread_info *info = find_thread_ptid (ptid);
+
+  gdb_assert (info);
+
+  if (!info->private)
+    {
+      info->private = xmalloc (sizeof (*(info->private)));
+      info->private_dtor = free_private_thread_info;
+      info->private->core = -1;
+      info->private->extra = 0;
+    }
+
+  return info->private;
+}
+
 /* Call this function as a result of
    1) A halt indication (T packet) containing a thread id
    2) A direct query of currthread
@@ -1437,12 +1473,6 @@ static void
 record_currthread (ptid_t currthread)
 {
   general_thread = currthread;
-
-  if (ptid_equal (currthread, minus_one_ptid))
-    /* We're just invalidating the local thread mirror.  */
-    return;
-
-  remote_notice_new_inferior (currthread, 0);
 }
 
 static char *last_pass_packet;
@@ -2371,6 +2401,80 @@ remote_find_new_threads (void)
                              CRAZY_MAX_THREADS);
 }
 
+#if defined(HAVE_LIBEXPAT)
+
+typedef struct thread_item
+{
+  ptid_t ptid;
+  char *extra;
+  int core;
+} thread_item_t;
+DEF_VEC_O(thread_item_t);
+
+struct threads_parsing_context
+{
+  VEC (thread_item_t) *items;
+};
+
+static void
+start_thread (struct gdb_xml_parser *parser,
+             const struct gdb_xml_element *element,
+             void *user_data, VEC(gdb_xml_value_s) *attributes)
+{
+  struct threads_parsing_context *data = user_data;
+
+  struct thread_item item;
+  char *id;
+
+  id = VEC_index (gdb_xml_value_s, attributes, 0)->value;
+  item.ptid = read_ptid (id, NULL);
+
+  if (VEC_length (gdb_xml_value_s, attributes) > 1)
+    item.core = *(ULONGEST *) VEC_index (gdb_xml_value_s, attributes, 1)->value;
+  else
+    item.core = -1;
+
+  item.extra = 0;
+
+  VEC_safe_push (thread_item_t, data->items, &item);
+}
+
+static void
+end_thread (struct gdb_xml_parser *parser,
+           const struct gdb_xml_element *element,
+           void *user_data, const char *body_text)
+{
+  struct threads_parsing_context *data = user_data;
+
+  if (body_text && *body_text)
+    VEC_last (thread_item_t, data->items)->extra = strdup (body_text);
+}
+
+const struct gdb_xml_attribute thread_attributes[] = {
+  { "id", GDB_XML_AF_NONE, NULL, NULL },
+  { "core", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element thread_children[] = {
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_children[] = {
+  { "thread", thread_attributes, thread_children,
+    GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+    start_thread, end_thread },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+const struct gdb_xml_element threads_elements[] = {
+  { "threads", NULL, threads_children,
+    GDB_XML_EF_NONE, NULL, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+#endif
+
 /*
  * Find all threads for info threads command.
  * Uses new thread protocol contributed by Cisco.
@@ -2388,6 +2492,61 @@ remote_threads_info (struct target_ops *ops)
   if (remote_desc == 0)                /* paranoia */
     error (_("Command can only be used when connected to the remote target."));
 
+#if defined(HAVE_LIBEXPAT)
+  if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+    {
+      char *xml = target_read_stralloc (&current_target,
+                                        TARGET_OBJECT_THREADS, NULL);
+
+      struct cleanup *back_to = make_cleanup (xfree, xml);
+      if (xml && *xml)
+       {
+         struct gdb_xml_parser *parser;
+         struct threads_parsing_context context;
+         struct cleanup *back_to = make_cleanup (null_cleanup, NULL);
+
+         context.items = 0;
+         parser = gdb_xml_create_parser_and_cleanup (_("threads"),
+                                                     threads_elements,
+                                                     &context);
+
+         gdb_xml_use_dtd (parser, "threads.dtd");
+
+         if (gdb_xml_parse (parser, xml) == 0)
+           {
+             int i;
+             struct thread_item *item;
+
+             for (i = 0; VEC_iterate (thread_item_t, context.items, i, item); ++i)
+               {
+                 if (!ptid_equal (item->ptid, null_ptid))
+                   {
+                     struct private_thread_info *info;
+                     /* In non-stop mode, we assume new found threads
+                        are running until proven otherwise with a
+                        stop reply.  In all-stop, we can only get
+                        here if all threads are stopped.  */
+                     int running = non_stop ? 1 : 0;
+
+                     remote_notice_new_inferior (item->ptid, running);
+
+                     info = demand_private_info (item->ptid);
+                     info->core = item->core;
+                     info->extra = item->extra;
+                     item->extra = 0;
+                   }
+                 xfree (item->extra);
+               }
+           }
+
+         VEC_free (thread_item_t, context.items);
+       }
+
+      do_cleanups (back_to);
+      return;
+    }
+#endif
+
   if (use_threadinfo_query)
     {
       putpkt ("qfThreadInfo");
@@ -2460,6 +2619,15 @@ remote_threads_extra_info (struct thread_info *tp)
        server doesn't know about it.  */
     return NULL;
 
+  if (remote_protocol_packets[PACKET_qXfer_threads].support == PACKET_ENABLE)
+    {
+      struct thread_info *info = find_thread_ptid (tp->ptid);
+      if (info && info->private)
+       return info->private->extra;
+      else
+       return NULL;
+    }
+
   if (use_threadextra_query)
     {
       char *b = rs->buf;
@@ -3245,6 +3413,8 @@ static struct protocol_feature remote_protocol_features[] = {
     PACKET_qXfer_spu_write },
   { "qXfer:osdata:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_osdata },
+  { "qXfer:threads:read", PACKET_DISABLE, remote_supported_packet,
+    PACKET_qXfer_threads },
   { "QPassSignals", PACKET_DISABLE, remote_supported_packet,
     PACKET_QPassSignals },
   { "QStartNoAckMode", PACKET_DISABLE, remote_supported_packet,
@@ -4359,6 +4529,8 @@ struct stop_reply
 
   int solibs_changed;
   int replay_event;
+
+  int core;
 };
 
 /* The list of already fetched and acknowledged stop events.  */
@@ -4522,6 +4694,7 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
   event->replay_event = 0;
   event->stopped_by_watchpoint_p = 0;
   event->regcache = NULL;
+  event->core = -1;
 
   switch (buf[0])
     {
@@ -4548,7 +4721,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
          /* If this packet is an awatch packet, don't parse the 'a'
             as a register number.  */
 
-         if (strncmp (p, "awatch", strlen("awatch")) != 0)
+         if (strncmp (p, "awatch", strlen("awatch")) != 0
+             && strncmp (p, "core", strlen ("core") != 0))
            {
              /* Read the ``P'' register number.  */
              pnum = strtol (p, &p_temp, 16);
@@ -4594,6 +4768,12 @@ Packet: '%s'\n"),
                  if (p_temp)
                    p = p_temp;
                }
+             else if (strncmp (p, "core", p1 - p) == 0)
+               {
+                 ULONGEST c;
+                 p = unpack_varlen_hex (++p1, &c);
+                 event->core = c;
+               }
              else
                {
                  /* Silently skip unknown optional info.  */
@@ -4803,6 +4983,7 @@ process_stop_reply (struct stop_reply *stop_reply,
                    struct target_waitstatus *status)
 {
   ptid_t ptid;
+  struct thread_info *info;
 
   *status = stop_reply->ws;
   ptid = stop_reply->ptid;
@@ -4834,6 +5015,7 @@ process_stop_reply (struct stop_reply *stop_reply,
       remote_watch_data_address = stop_reply->watch_data_address;
 
       remote_notice_new_inferior (ptid, 0);
+      demand_private_info (ptid)->core = stop_reply->core;
     }
 
   stop_reply_xfree (stop_reply);
@@ -7676,6 +7858,11 @@ remote_xfer_partial (struct target_ops *ops, enum target_object object,
        (ops, "osdata", annex, readbuf, offset, len,
         &remote_protocol_packets[PACKET_qXfer_osdata]);
 
+    case TARGET_OBJECT_THREADS:
+      gdb_assert (annex == NULL);
+      return remote_read_qxfer (ops, "threads", annex, readbuf, offset, len,
+                               &remote_protocol_packets[PACKET_qXfer_threads]);
+
     default:
       return -1;
     }
@@ -9324,6 +9511,15 @@ remote_set_disconnected_tracing (int val)
     error (_("Target does not support this command."));
 }
 
+static int
+remote_core_of_thread (struct target_ops *ops, ptid_t ptid)
+{
+  struct thread_info *info = find_thread_ptid (ptid);
+  if (info && info->private)
+    return info->private->core;
+  return -1;
+}
+
 static void
 init_remote_ops (void)
 {
@@ -9397,6 +9593,7 @@ Specify the serial device it is connected to\n\
   remote_ops.to_trace_find = remote_trace_find;
   remote_ops.to_get_trace_state_variable_value = remote_get_trace_state_variable_value;
   remote_ops.to_set_disconnected_tracing = remote_set_disconnected_tracing;
+  remote_ops.to_core_of_thread = remote_core_of_thread;
 }
 
 /* Set up the extended remote vector by making a copy of the standard
@@ -9933,6 +10130,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_osdata],
                         "qXfer:osdata:read", "osdata", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_threads],
+                        "qXfer:threads:read", "threads", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_siginfo_read],
                          "qXfer:siginfo:read", "read-siginfo-object", 0);
 
index 25a2cd736d6ef53461e0c772b2a7956c27c1a7f1..edf86970f002532d9827c519e154aa5784eb25f4 100644 (file)
@@ -3064,6 +3064,26 @@ target_store_registers (struct regcache *regcache, int regno)
   noprocess ();
 }
 
+int
+target_core_of_thread (ptid_t ptid)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    {
+      if (t->to_core_of_thread != NULL)
+       {
+         int retval = t->to_core_of_thread (t, ptid);
+         if (targetdebug)
+           fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
+                               PIDGET (ptid), retval);
+         return retval;
+       }
+    }
+
+  return -1;
+}
+
 static void
 debug_to_prepare_to_store (struct regcache *regcache)
 {
index 20cbe29eb97255d7df49bc7d6b9bc3262e7a0ea6..a020bf7325555a7f42d7ee42ec0bddb7bb20117c 100644 (file)
@@ -256,6 +256,8 @@ enum target_object
   /* Extra signal info.  Usually the contents of `siginfo_t' on unix
      platforms.  */
   TARGET_OBJECT_SIGNAL_INFO,
+  /* The list of threads that are being debugged.  */
+  TARGET_OBJECT_THREADS,
   /* Possible future objects: TARGET_OBJECT_FILE, ... */
 };
 
@@ -651,6 +653,14 @@ struct target_ops
        disconnection - set VAL to 1 to keep tracing, 0 to stop.  */
     void (*to_set_disconnected_tracing) (int val);
 
+    /* Return the processor core that thread PTID was last seen on.
+       This information is updated only when:
+       - update_thread_list is called
+       - thread stops
+       If the core cannot be determined -- either for the specified thread, or
+       right now, or in this debug session, or for this target -- return -1.  */
+    int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1332,6 +1342,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
       (*current_target.to_log_command) (p);                            \
   while (0)
 
+
+extern int target_core_of_thread (ptid_t ptid);
+
 /* Routines for maintenance of the target structures...
 
    add_target:   Add a target to the list of all possible targets.
index 789043f9294ed333a81eb830d4b3436ee8b55aad..d40a2ffb78e99c3b6645c444cbf4c9efd6b6a366 100644 (file)
@@ -1,3 +1,8 @@
+2010-01-13  Vladimir Prus  <vladimir@codesourcery.com>
+
+       * lib/mi-support.exp (mi_check_thread_states): Handle
+       core number in thread listing.
+
 2010-01-12  Joel Brobecker  <brobecker@adacore.com>
 
        * gdb.base/maint.exp: Adjust the expected output for the
index 877a10ffa0354291cb8c6e2ef02a296b88d6bca9..373b3663b679c090a08de31ee6ad928131da8a1f 100644 (file)
@@ -1877,7 +1877,7 @@ proc mi_check_thread_states { xstates test } {
     foreach s $states {
        set pattern "${pattern}(.*)state=\"$s\""
     }
-    set pattern "$pattern\\\}\\\].*"
+    set pattern "${pattern}(,core=\"\[0-9\]*\")?\\\}\\\].*"
 
     verbose -log "expecting: $pattern"
     mi_gdb_test "-thread-info" $pattern $test
index 80c378661eb970e818878fa7dd9d294faac8d6f5..16a207c5119baf70d5b7f2baccff0ddcbd1ce1aa 100644 (file)
@@ -114,10 +114,13 @@ free_thread (struct thread_info *tp)
 {
   clear_thread_inferior_resources (tp);
 
-  /* FIXME: do I ever need to call the back-end to give it a
-     chance at this private data before deleting the thread?  */
   if (tp->private)
-    xfree (tp->private);
+    {
+      if (tp->private_dtor)
+       tp->private_dtor (tp->private);
+      else
+       xfree (tp->private);
+    }
 
   xfree (tp);
 }
@@ -468,8 +471,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
   struct cleanup *cleanup_chain;
   int current_thread = -1;
 
-  prune_threads ();
-  target_find_new_threads ();
+  update_thread_list ();
 
   cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
 
@@ -748,8 +750,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
   char *extra_info;
   int current_thread = -1;
 
-  prune_threads ();
-  target_find_new_threads ();
+  update_thread_list ();
   current_ptid = inferior_ptid;
 
   /* We'll be switching threads temporarily.  */
@@ -759,6 +760,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
   for (tp = thread_list; tp; tp = tp->next)
     {
       struct cleanup *chain2;
+      int core;
 
       if (requested_thread != -1 && tp->num != requested_thread)
        continue;
@@ -817,6 +819,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
          ui_out_field_string (uiout, "state", state);
        }
 
+      core = target_core_of_thread (tp->ptid);
+      if (ui_out_is_mi_like_p (uiout) && core != -1)
+       ui_out_field_int (uiout, "core", core);
+
       do_cleanups (chain2);
     }
 
@@ -1058,8 +1064,7 @@ thread_apply_all_command (char *cmd, int from_tty)
   if (cmd == NULL || *cmd == '\000')
     error (_("Please specify a command following the thread ID list"));
 
-  prune_threads ();
-  target_find_new_threads ();
+  update_thread_list ();
 
   old_chain = make_cleanup_restore_current_thread ();
 
@@ -1245,6 +1250,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
   return GDB_RC_OK;
 }
 
+void
+update_thread_list (void)
+{
+  prune_threads ();
+  target_find_new_threads ();
+}
+
 /* Commands with a prefix of `thread'.  */
 struct cmd_list_element *thread_cmd_list = NULL;
 
index db8c894ef9f7d735edc8e014df22a81f69663a42..e5fd47476346867a90ae85a6dccdee7c3025efbe 100644 (file)
@@ -44,7 +44,7 @@ struct ui_out_hdr
    is always available.  Stack/nested level 0 is reserved for the
    top-level result. */
 
-enum { MAX_UI_OUT_LEVELS = 6 };
+enum { MAX_UI_OUT_LEVELS = 8 };
 
 struct ui_out_level
   {
index 4c03655d0a48b0661d85cf3567f11c6b05f90748..f72a9e3b0edff08949e66393f3ddf13a0a7c7615 100644 (file)
@@ -3554,6 +3554,14 @@ gdb_buildargv (const char *s)
   return argv;
 }
 
+int
+compare_positive_ints (const void *ap, const void *bp)
+{
+  /* Because we know we're comparing two ints which are positive,
+     there's no danger of overflow here.  */
+  return * (int *) ap - * (int *) bp;
+}
+
 /* Provide a prototype to silence -Wmissing-prototypes.  */
 extern initialize_file_ftype _initialize_utils;