This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [MI] core awareness


On Monday 11 January 2010 17:17:16 Pedro Alves wrote:

> > - Introduce thread_get_core function in thread.c. That thread will
> >   be documented as returning the core a given thread was last noticed
> >   running on by GDB, with that information updated either by
> >   update_thread_list or when a thread stops. We'd need a new field
> >   inside thread_info to keep this information.
> > - Make linux native update core information for all threads when
> >   listing them.
> 
> Sounds good to me.  Note that linux-nat.c doesn't list threads,
> linux-thread-db.c does.
> 
> > What I am primarily trying to avoid is introducing a packet to get
> > core information for a single running thread. That will be slow,
> > and such functionality is not required by anything at present.
> 
> That's fine.  My gripe is in the disconnect between the
> documentation of what the method should do, and what it
> actually does.

Here's the patch revised per above, and offlist discussion. For the benefit
of list, the problem with the above scheme is that linux_nat_wait seems
like the best place to record core, however, a new thread is only added to
the thread list by linux_thread_db_wait, and while in practice, a thread
will always stop on thread breakpoint and before stopping for real, and
therefore will be present in the thread table when linux_nat_wait wishes
to record core number, such design is brittle. So, this revision makes
linux native behave similarly to remote, and adjust documentation on
the to_core_of_thread method to match that behaviour.

This patch also fixes a few formatting issues that whitespace-mode found,
and adjust naming of some functions to be consistent.



- Volodya
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index d638a3f..76953ae 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1181,8 +1181,8 @@ prepare_resume_reply (char *buf, ptid_t ptid,
 		strcat (buf, ";");
 		buf += strlen (buf);
 
-		if (the_target->core_for_thread)
-		  core = (*the_target->core_for_thread) (ptid);
+		if (the_target->core_of_thread)
+		  core = (*the_target->core_of_thread) (ptid);
 		if (core != -1)
 		  {
 		    sprintf (buf, "core:");
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index dfc92a8..c781552 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -723,8 +723,8 @@ handle_threads_qxfer_proper (struct buffer *buffer)
 
       write_ptid (ptid_s, ptid);
 
-      if (the_target->core_for_thread)
-	core = (*the_target->core_for_thread) (ptid);
+      if (the_target->core_of_thread)
+	core = (*the_target->core_of_thread) (ptid);
 
       if (core != -1)
 	{
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 0ee0d41..0c761e9 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -285,7 +285,7 @@ struct target_ops
   int (*handle_monitor_command) (char *);
 
   /* Returns the core given a thread, or -1 if not known.  */
-  int (*core_for_thread) (ptid_t);
+  int (*core_of_thread) (ptid_t);
 };
 
 extern struct target_ops *the_target;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 717f78c..bf0a5f1 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -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,10 +5425,8 @@ linux_nat_thread_address_space (struct target_ops *t, ptid_t ptid)
   return inf->aspace;
 }
 
-/* Return the core for a thread.  */
-
-static int
-linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
+int
+linux_nat_core_of_thread_1 (ptid_t ptid)
 {
   struct cleanup *back_to;
   char *filename;
@@ -5483,6 +5483,17 @@ linux_nat_core_of_thread (struct target_ops *ops, ptid_t ptid)
   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)
 {
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index 9537740..5aba089 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -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);
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index 659d99d..2c66da7 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -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 *
diff --git a/gdb/remote.c b/gdb/remote.c
index 992ef9a..beb585e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1411,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))
@@ -1421,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;
 	}
 
@@ -1452,12 +1452,12 @@ demand_private_info (ptid_t ptid)
 
   gdb_assert (info);
 
-  if (!info->private) 
+  if (!info->private)
     {
       info->private = xmalloc (sizeof (*(info->private)));
       info->private_dtor = free_private_thread_info;
     }
-  
+
   return info->private;
 }
 
diff --git a/gdb/target.h b/gdb/target.h
index 9725375..a020bf7 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -653,11 +653,10 @@ struct target_ops
        disconnection - set VAL to 1 to keep tracing, 0 to stop.  */
     void (*to_set_disconnected_tracing) (int val);
 
-    /* Return the core that thread PTID is on.  For a stopped thread, should
-       return the core the thread was last running on.  For a running thread,
-       should return one of the cores that the thread was running between
-       the call to this function and return -- and if it was running on
-       several cores, any other may be returned.
+    /* 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);
commit ea1be42644d10bb9197c6cd67927aa760906646e
Author: Vladimir Prus <vladimir@codesourcery.com>
Date:   Wed Dec 16 23:39:19 2009 +0300

    	Implement core awareness.
    
    	gdb/
    	* 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.
    
    	gdbserver/
    	* 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.
    
    	gdb/doc
    	* 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.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4d3e02a..31a0f76 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -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,
diff --git a/gdb/bcache.c b/gdb/bcache.c
index 1bc2eba..4badf6e 100644
--- a/gdb/bcache.c
+++ b/gdb/bcache.c
@@ -301,15 +301,6 @@ bcache_xfree (struct bcache *bcache)
 
 /* Printing statistics.  */
 
-static int
-compare_ints (const void *ap, const void *bp)
-{
-  /* Because we know we're comparing two ints which are positive,
-     there's no danger of overflow here.  */
-  return * (int *) ap - * (int *) bp;
-}
-
-
 static void
 print_percentage (int portion, int total)
 {
@@ -367,9 +358,9 @@ print_bcache_statistics (struct bcache *c, char *type)
 
     /* To compute the median, we need the set of chain lengths sorted.  */
     qsort (chain_length, c->num_buckets, sizeof (chain_length[0]),
-	   compare_ints);
+	   compare_positive_ints);
     qsort (entry_size, c->unique_count, sizeof (entry_size[0]),
-	   compare_ints);
+	   compare_positive_ints);
 
     if (c->num_buckets > 0)
       {
diff --git a/gdb/defs.h b/gdb/defs.h
index 5d251b5..b0a212d 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -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 *);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 5b78f50..02e2bbd 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -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
@@ -29025,6 +29135,10 @@ 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
 reasons are listed below.  @var{aa} should be @samp{05}, the trap
@@ -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
diff --git a/gdb/features/threads.dtd b/gdb/features/threads.dtd
new file mode 100644
index 0000000..20308ee
--- /dev/null
+++ b/gdb/features/threads.dtd
@@ -0,0 +1,13 @@
+<!-- Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!ELEMENT threads (thread*)>
+<!ATTLIST threads version CDATA #FIXED "1.0">
+
+<!ELEMENT thread (#PCDATA)>
+
+<!ATTLIST thread id CDATA #REQUIRED>
+<!ATTLIST thread core CDATA #IMPLIED>
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index c20322e..5584691 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -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
 {
@@ -2802,6 +2803,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,
 		    CORE_ADDR offset, int len)
@@ -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
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 9b5bad8..76953ae 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,
 	       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;
        }
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 9254121..c781552 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -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;
     }
 
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index ad21eb7..0c761e9 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -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;
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 4ab2111..cd24eaf 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -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 */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 48ea1bc..bf0a5f1 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -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
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index 9537740..5aba089 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -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);
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index 659d99d..2c66da7 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -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 *
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index 96e458c..41388bb 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_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);
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index 16f6102..aee246c 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -50,6 +50,7 @@
 #include "valprint.h"
 #include "inferior.h"
 #include "osdata.h"
+#include "splay-tree.h"
 
 #include <ctype.h>
 #include <sys/time.h>
@@ -360,11 +361,55 @@ mi_cmd_thread_info (char *command, char **argv, int argc)
   print_thread_info (uiout, thread, -1);
 }
 
+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);
 }
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 9c50f7e..beb585e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -60,6 +60,7 @@
 #include "remote-fileio.h"
 #include "gdb/fileio.h"
 #include "gdb_stat.h"
+#include "xml-support.h"
 
 #include "memory-map.h"
 
@@ -324,6 +325,20 @@ struct remote_state
   int ctrlc_pending_p;
 };
 
+/* Private data that we'll store in (struct thread_info)->private.  */
+struct private_thread_info
+{
+  char *extra;
+  int core;
+};
+
+static void
+free_private_thread_info (struct private_thread_info *info)
+{
+  xfree (info->extra);
+  xfree (info);
+}
+
 /* Returns true if the multi-process extensions are in effect.  */
 static int
 remote_multi_process_p (struct remote_state *rs)
@@ -1121,6 +1136,7 @@ enum {
   PACKET_qXfer_spu_read,
   PACKET_qXfer_spu_write,
   PACKET_qXfer_osdata,
+  PACKET_qXfer_threads,
   PACKET_qGetTLSAddr,
   PACKET_qSupported,
   PACKET_QPassSignals,
@@ -1395,7 +1411,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
 	      remote_add_thread (currthread, running);
 	      inferior_ptid = currthread;
 	    }
- 	  return;
+	  return;
 	}
 
       if (ptid_equal (magic_null_ptid, inferior_ptid))
@@ -1405,7 +1421,7 @@ remote_notice_new_inferior (ptid_t currthread, int running)
 	     doesn't support qC.  This is the first stop reported
 	     after an attach, so this is the main thread.  Update the
 	     ptid in the thread list.  */
-  	  thread_change_ptid (inferior_ptid, currthread);
+	  thread_change_ptid (inferior_ptid, currthread);
 	  return;
 	}
 
@@ -1427,6 +1443,24 @@ 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;
+    }
+
+  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 +1471,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 +2399,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 +2490,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 +2617,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 +3411,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 +4527,8 @@ struct stop_reply
 
   int solibs_changed;
   int replay_event;
+
+  int core;
 };
 
 /* The list of already fetched and acknowledged stop events.  */
@@ -4522,6 +4692,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 +4719,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 +4766,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 +4981,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 +5013,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 +7856,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 +9509,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 +9591,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 +10128,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);
 
diff --git a/gdb/target.c b/gdb/target.c
index 25a2cd7..edf8697 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3064,6 +3064,26 @@ target_store_registers (struct regcache *regcache, int regno)
   noprocess ();
 }
 
+int
+target_core_of_thread (ptid_t ptid)
+{
+  struct target_ops *t;
+
+  for (t = current_target.beneath; t != NULL; t = t->beneath)
+    {
+      if (t->to_core_of_thread != NULL)
+	{
+	  int retval = t->to_core_of_thread (t, ptid);
+	  if (targetdebug)
+	    fprintf_unfiltered (gdb_stdlog, "target_core_of_thread (%d) = %d\n",
+				PIDGET (ptid), retval);
+	  return retval;
+	}
+    }
+
+  return -1;
+}
+
 static void
 debug_to_prepare_to_store (struct regcache *regcache)
 {
diff --git a/gdb/target.h b/gdb/target.h
index 20cbe29..a020bf7 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -256,6 +256,8 @@ enum target_object
   /* Extra signal info.  Usually the contents of `siginfo_t' on unix
      platforms.  */
   TARGET_OBJECT_SIGNAL_INFO,
+  /* The list of threads that are being debugged.  */
+  TARGET_OBJECT_THREADS,
   /* Possible future objects: TARGET_OBJECT_FILE, ... */
 };
 
@@ -651,6 +653,14 @@ struct target_ops
        disconnection - set VAL to 1 to keep tracing, 0 to stop.  */
     void (*to_set_disconnected_tracing) (int val);
 
+    /* Return the processor core that thread PTID was last seen on.
+       This information is updated only when:
+       - update_thread_list is called
+       - thread stops
+       If the core cannot be determined -- either for the specified thread, or
+       right now, or in this debug session, or for this target -- return -1.  */
+    int (*to_core_of_thread) (struct target_ops *, ptid_t ptid);
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1332,6 +1342,9 @@ extern int target_search_memory (CORE_ADDR start_addr,
       (*current_target.to_log_command) (p);				\
   while (0)
 
+
+extern int target_core_of_thread (ptid_t ptid);
+
 /* Routines for maintenance of the target structures...
 
    add_target:   Add a target to the list of all possible targets.
diff --git a/gdb/thread.c b/gdb/thread.c
index 80c3786..16a207c 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -114,10 +114,13 @@ free_thread (struct thread_info *tp)
 {
   clear_thread_inferior_resources (tp);
 
-  /* FIXME: do I ever need to call the back-end to give it a
-     chance at this private data before deleting the thread?  */
   if (tp->private)
-    xfree (tp->private);
+    {
+      if (tp->private_dtor)
+	tp->private_dtor (tp->private);
+      else
+	xfree (tp->private);
+    }
 
   xfree (tp);
 }
@@ -468,8 +471,7 @@ do_captured_list_thread_ids (struct ui_out *uiout, void *arg)
   struct cleanup *cleanup_chain;
   int current_thread = -1;
 
-  prune_threads ();
-  target_find_new_threads ();
+  update_thread_list ();
 
   cleanup_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "thread-ids");
 
@@ -748,8 +750,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
   char *extra_info;
   int current_thread = -1;
 
-  prune_threads ();
-  target_find_new_threads ();
+  update_thread_list ();
   current_ptid = inferior_ptid;
 
   /* We'll be switching threads temporarily.  */
@@ -759,6 +760,7 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
   for (tp = thread_list; tp; tp = tp->next)
     {
       struct cleanup *chain2;
+      int core;
 
       if (requested_thread != -1 && tp->num != requested_thread)
 	continue;
@@ -817,6 +819,10 @@ print_thread_info (struct ui_out *uiout, int requested_thread, int pid)
 	  ui_out_field_string (uiout, "state", state);
 	}
 
+      core = target_core_of_thread (tp->ptid);
+      if (ui_out_is_mi_like_p (uiout) && core != -1)
+	ui_out_field_int (uiout, "core", core);
+
       do_cleanups (chain2);
     }
 
@@ -1058,8 +1064,7 @@ thread_apply_all_command (char *cmd, int from_tty)
   if (cmd == NULL || *cmd == '\000')
     error (_("Please specify a command following the thread ID list"));
 
-  prune_threads ();
-  target_find_new_threads ();
+  update_thread_list ();
 
   old_chain = make_cleanup_restore_current_thread ();
 
@@ -1245,6 +1250,13 @@ gdb_thread_select (struct ui_out *uiout, char *tidstr, char **error_message)
   return GDB_RC_OK;
 }
 
+void
+update_thread_list (void)
+{
+  prune_threads ();
+  target_find_new_threads ();
+}
+
 /* Commands with a prefix of `thread'.  */
 struct cmd_list_element *thread_cmd_list = NULL;
 
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index db8c894..e5fd474 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -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
   {
diff --git a/gdb/utils.c b/gdb/utils.c
index 4c03655..f72a9e3 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -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;
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]