From b66a50452170bda7f696cea24a459bd8ecc37764 Mon Sep 17 00:00:00 2001
From: Aaron Gamble <agamble@google.com>
Date: Tue, 21 Aug 2012 12:03:06 -0700
Subject: [PATCH] info threads sort and regex

---
 gdb/doc/gdb.texinfo                                |   10 +-
 gdb/gdbthread.h                                    |    4 +
 gdb/testsuite/gdb.threads/Makefile.in              |    2 +-
 gdb/testsuite/gdb.threads/info-thread-sort-regex.c |  122 +++++++
 .../gdb.threads/info-thread-sort-regex.exp         |   64 ++++
 gdb/thread.c                                       |  378 +++++++++++++-------
 6 files changed, 438 insertions(+), 142 deletions(-)
 create mode 100644 gdb/testsuite/gdb.threads/info-thread-sort-regex.c
 create mode 100644 gdb/testsuite/gdb.threads/info-thread-sort-regex.exp

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 08ba92d..53a45d7 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2751,10 +2751,12 @@ number---always a single integer---with each thread in your program.
 
 @table @code
 @kindex info threads
-@item info threads @r{[}@var{id}@dots{}@r{]}
-Display a summary of all threads currently in your program.  Optional 
-argument @var{id}@dots{} is one or more thread ids separated by spaces, and
-means to print information only about the specified thread or threads.
+@item info threads @r{[}@var{id}@dots{}@b{|}r<@var{regex}>@r{]}
+Display a summary of all threads currently in your program.  Optional
+argument for specifying threads is either @code{r} followed by a regular
+expression or @var{id}@dots{} one or more numeric thread ids separated by
+spaces. The list of threads is sorted alphabetically by thread name.
+
 @value{GDBN} displays for each thread (in this order):
 
 @enumerate
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 0250555..c54d1ee 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -138,6 +138,10 @@ struct thread_info
      if the thread does not have a user-given name.  */
   char *name;
 
+  /* The name of the thread, as cached by print_thread_info.
+     Used for sorting info threads output.  */
+  char *cached_name;
+
   /* Non-zero means the thread is executing.  Note: this is different
      from saying that there is an active target and we are stopped at
      a breakpoint, for instance.  This is a real indicator whether the
diff --git a/gdb/testsuite/gdb.threads/Makefile.in b/gdb/testsuite/gdb.threads/Makefile.in
index 8f9f867..2d24ba4 100644
--- a/gdb/testsuite/gdb.threads/Makefile.in
+++ b/gdb/testsuite/gdb.threads/Makefile.in
@@ -5,7 +5,7 @@ EXECUTABLES = attach-into-signal-nothr attach-into-signal-thr \
 	attach-stopped attachstop-mt \
 	bp_in_thread current-lwp-dead execl execl1 fork-child-threads \
 	fork-thread-pending gcore-pthreads hand-call-in-threads \
-	ia64-sigill interrupted-hand-call killed linux-dp \
+	ia64-sigill info-thread-sort-regex interrupted-hand-call killed linux-dp \
 	local-watch-wrong-thread manythreads multi-create pending-step \
 	print-threads pthreads pthread_cond_wait schedlock sigthread \
 	staticthreads switch-threads thread-execl thread-specific \
diff --git a/gdb/testsuite/gdb.threads/info-thread-sort-regex.c b/gdb/testsuite/gdb.threads/info-thread-sort-regex.c
new file mode 100644
index 0000000..98979ce
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/info-thread-sort-regex.c
@@ -0,0 +1,122 @@
+/* Test case for info threads sort and regex matching.
+
+   Copyright 2008, 2010-2012 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef NR_THREADS
+#define NR_THREADS 4
+#endif
+
+int thread_count;
+
+pthread_mutex_t thread_count_mutex;
+
+pthread_cond_t thread_count_condvar;
+
+void
+incr_thread_count (void)
+{
+  pthread_mutex_lock (&thread_count_mutex);
+  ++thread_count;
+  if (thread_count == NR_THREADS)
+    pthread_cond_signal (&thread_count_condvar);
+  pthread_mutex_unlock (&thread_count_mutex);
+}
+
+void
+cond_wait (pthread_cond_t *cond, pthread_mutex_t *mut)
+{
+  pthread_mutex_lock (mut);
+  pthread_cond_wait (cond, mut);
+  pthread_mutex_unlock (mut);
+}
+
+void
+noreturn (void)
+{
+  pthread_mutex_t mut;
+  pthread_cond_t cond;
+
+  pthread_mutex_init (&mut, NULL);
+  pthread_cond_init (&cond, NULL);
+
+  /* Wait for a condition that will never be signaled, so we effectively
+     block the thread here.  */
+  cond_wait (&cond, &mut);
+}
+
+void *
+forever_pthread (void *unused)
+{
+  incr_thread_count ();
+  noreturn ();
+}
+
+/* Wait until all threads are running.  */
+
+void
+wait_all_threads_running (void)
+{
+  pthread_mutex_lock (&thread_count_mutex);
+  if (thread_count == NR_THREADS)
+    {
+      pthread_mutex_unlock (&thread_count_mutex);
+      return;
+    }
+  pthread_cond_wait (&thread_count_condvar, &thread_count_mutex);
+  if (thread_count == NR_THREADS)
+    {
+      pthread_mutex_unlock (&thread_count_mutex);
+      return;
+    }
+  pthread_mutex_unlock (&thread_count_mutex);
+  printf ("failed waiting for all threads to start\n");
+  abort ();
+}
+
+/* Called when all threads are running.
+   Easy place for a breakpoint.  */
+
+void
+all_threads_running (void)
+{
+}
+
+int
+main (void)
+{
+  pthread_t forever[NR_THREADS];
+  int i;
+
+  pthread_mutex_init (&thread_count_mutex, NULL);
+  pthread_cond_init (&thread_count_condvar, NULL);
+
+  for (i = 0; i < NR_THREADS; ++i)
+    pthread_create (&forever[i], NULL, forever_pthread, NULL);
+
+  wait_all_threads_running ();
+  all_threads_running ();
+
+  return 0;
+}
+
diff --git a/gdb/testsuite/gdb.threads/info-thread-sort-regex.exp b/gdb/testsuite/gdb.threads/info-thread-sort-regex.exp
new file mode 100644
index 0000000..bd9e21d
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/info-thread-sort-regex.exp
@@ -0,0 +1,64 @@
+# Copyright (C) 2004, 2007-2008, 2010-2012 Free Software Foundation,
+# Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Test sorting and regex matching in info threads.
+
+set NR_THREADS 4
+
+standard_testfile
+
+# Start with a fresh gdb.
+gdb_exit
+gdb_start
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable [list debug "additional_flags=-DNR_THREADS=$NR_THREADS"]] != "" } {
+    return -1
+}
+
+clean_restart ${binfile}
+
+if { ![runto_main] } {
+    fail "Can't run to main"
+    return 0
+}
+
+gdb_test "break all_threads_running" \
+         "Breakpoint 2 at .*: file .*${srcfile}, line .*" \
+         "breakpoint on all_threads_running"
+
+# Run the program and make sure GDB reports that we stopped after
+# hitting breakpoint 2 in all_threads_running().
+
+gdb_test "continue" \
+         ".*Breakpoint 2, all_threads_running ().*" \
+         "run to all_threads_running"
+
+# Test that the no threads match message prints correctly.
+gdb_test "info threads r \$foo" \
+         "No threads match '\\\$foo'\." \
+         "info thread no match test"
+
+gdb_test_no_output "thread name a" "set thread name to a"
+
+gdb_test "info threads" \
+   "\\s+Id\\s+Target\\s+Id\\s+Frame \r\n\\*\\\s+\\\d+\\\s+Thread\\\s+0x\[A-Fa-f0-9\]+ \\\(LWP \\\d+\\\) \"a\".*" \
+   "check thread a is first"
+
+gdb_test "info threads r ^info" \
+   "\\s+Id\\s+Target\\s+Id\\s+Frame \r\n\\\s+\\\d+\\\s+Thread\\\s+0x\[A-Fa-f0-9\]+ \\\(LWP \\\d+\\\) \"info.*\".*" \
+   "filter out thread named a"
+
+return 0
diff --git a/gdb/thread.c b/gdb/thread.c
index 7e8eec5..68d306e 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -38,6 +38,7 @@
 #include <ctype.h>
 #include <sys/types.h>
 #include <signal.h>
+#include <stdlib.h>
 #include "ui-out.h"
 #include "observer.h"
 #include "annotate.h"
@@ -55,7 +56,10 @@ void _initialize_thread (void);
 /* Prototypes for local functions.  */
 
 struct thread_info *thread_list = NULL;
+static int thread_list_size = 0;
 static int highest_thread_num;
+static int print_thread_info_regex_cflags = 0;
+static int print_thread_info_regex_eflags = 0;
 
 static void thread_command (char *tidstr, int from_tty);
 static void thread_apply_all_command (char *, int);
@@ -132,6 +136,7 @@ free_thread (struct thread_info *tp)
 	xfree (tp->private);
     }
 
+  thread_list_size--;
   xfree (tp->name);
   xfree (tp);
 }
@@ -153,6 +158,7 @@ init_thread_list (void)
     }
 
   thread_list = NULL;
+  thread_list_size = 0;
 }
 
 /* Allocate a new thread with target id PTID and add it to the thread
@@ -169,6 +175,7 @@ new_thread (ptid_t ptid)
   tp->num = ++highest_thread_num;
   tp->next = thread_list;
   thread_list = tp;
+  thread_list_size++;
 
   /* Nothing to follow yet.  */
   tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
@@ -748,25 +755,208 @@ finish_thread_state_cleanup (void *arg)
   finish_thread_state (*ptid_p);
 }
 
+/* Returns true if user requested the thread.  */
+
+static int
+print_thread_filter_check (struct thread_info *tp, char *requested_threads,
+                           int pid, regex_t *preg)
+{
+  if (preg)
+    {
+      int err = regexec (preg, tp->cached_name, 0, NULL,
+                         print_thread_info_regex_eflags);
+      if (err == 0)
+        return TRUE;
+      else
+        return FALSE;
+    }
+
+  /* No regular expression was supplied, see if there's a match the list
+     of thread id's.  */
+  if (!number_is_in_list (requested_threads, tp->num))
+    return FALSE;
+
+  if (pid != -1 && PIDGET (tp->ptid) != pid)
+    {
+      if (requested_threads != NULL && *requested_threads != '\0')
+        error (_("Requested thread not found in requested process"));
+      return FALSE;
+    }
+
+  if (tp->state == THREAD_EXITED)
+    return FALSE;
+
+  return TRUE;
+}
+
+
+/* Used to Sort threads in lexicographic order by name.  */
+
+static int
+print_thread_sort_cmp (const void *p1, const void *p2)
+{
+  const char *name1 = (*(struct thread_info **)p1)->cached_name;
+  const char *name2 = (*(struct thread_info **)p2)->cached_name;
+  if (name1 && name2)
+    return strcoll (name1, name2);
+
+  /* Incase we receive null pointers instead of strings.
+     NULL pointers are sorted as empty strings.  */
+  if (!name1 && name2)
+    return -1;
+  if (!name2 && name1)
+    return 1;
+
+  return 0;
+}
+
+/* Prints the info for a single thread.
+   Called by print_thread_info for each thread.  */
+
+static void
+print_thread_info_single (struct ui_out *uiout, struct thread_info *tp,
+                               ptid_t current_ptid)
+{
+  struct cleanup *uiout_cleanup
+    = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
+  int core;
+  char *extra_info, *name, *target_id;
+
+  if (ui_out_is_mi_like_p (uiout))
+    {
+      /* Compatibility.  */
+      if (ptid_equal (tp->ptid, current_ptid))
+        ui_out_text (uiout, "* ");
+      else
+        ui_out_text (uiout, "  ");
+    }
+    else
+    {
+      if (ptid_equal (tp->ptid, current_ptid))
+        ui_out_field_string (uiout, "current", "*");
+      else
+        ui_out_field_skip (uiout, "current");
+    }
+
+  ui_out_field_int (uiout, "id", tp->num);
+
+  /* For the CLI, we stuff everything into the target-id field.
+     This is a gross hack to make the output come out looking
+     correct.  The underlying problem here is that ui-out has no
+     way to specify that a field's space allocation should be
+     shared by several fields.  For MI, we do the right thing
+     instead.  */
+
+  target_id = target_pid_to_str (tp->ptid);
+  extra_info = target_extra_thread_info (tp);
+  if (tp->cached_name)
+    name = tp->cached_name;
+  else
+    name = tp->name ? tp->name : target_thread_name (tp);
+
+  if (ui_out_is_mi_like_p (uiout))
+    {
+      ui_out_field_string (uiout, "target-id", target_id);
+      if (extra_info)
+        ui_out_field_string (uiout, "details", extra_info);
+      if (name)
+        ui_out_field_string (uiout, "name", name);
+    }
+  else
+    {
+      struct cleanup *str_cleanup;
+      char *contents;
+
+      if (extra_info && name)
+        contents = xstrprintf ("%s \"%s\" (%s)", target_id,
+                               name, extra_info);
+      else if (extra_info)
+        contents = xstrprintf ("%s (%s)", target_id, extra_info);
+      else if (name)
+        contents = xstrprintf ("%s \"%s\"", target_id, name);
+      else
+        contents = xstrdup (target_id);
+      str_cleanup = make_cleanup (xfree, contents);
+
+      ui_out_field_string (uiout, "target-id", contents);
+      do_cleanups (str_cleanup);
+    }
+
+  if (tp->state == THREAD_RUNNING)
+    ui_out_text (uiout, "(running)\n");
+  else
+    {
+      /* The switch below puts us at the top of the stack (leaf
+         frame).  */
+      switch_to_thread (tp->ptid);
+      print_stack_frame (get_selected_frame (NULL),
+          /* For MI output, print frame level.  */
+          ui_out_is_mi_like_p (uiout),
+          LOCATION);
+    }
+
+  if (ui_out_is_mi_like_p (uiout))
+    {
+      char *state = "stopped";
+
+      if (tp->state == THREAD_RUNNING)
+        state = "running";
+      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 (uiout_cleanup);
+}
+
+/* Caches the name that will be shown to the user for a thread.
+   We keep track of this for sorting purposes.  */
+
+static void
+thread_cache_name (struct thread_info *tp)
+{
+    tp->cached_name = tp->name ? tp->name : target_thread_name (tp);
+}
+
 /* Prints the list of threads and their details on UIOUT.
    This is a version of 'info_threads_command' suitable for
    use from MI.
-   If REQUESTED_THREAD is not -1, it's the GDB id of the thread
-   that should be printed.  Otherwise, all threads are
-   printed.
-   If PID is not -1, only print threads from the process PID.
-   Otherwise, threads from all attached PIDs are printed.
-   If both REQUESTED_THREAD and PID are not -1, then the thread
-   is printed if it belongs to the specified process.  Otherwise,
-   an error is raised.  */
+   REQUESTED_THREADS is either a list of numeric thread Id's
+   or the character 'r' followed by a regular expression to
+   match with each thread's name. Any spaces between the 'r' and
+   the regular expression are ignored.
+   If neither is supplied then all threads are printed.
+   Threads are sorted by name unless we are sending to MI.  */
+
 void
 print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
 {
   struct thread_info *tp;
   ptid_t current_ptid;
   struct cleanup *old_chain;
-  char *extra_info, *name, *target_id;
   int current_thread = -1;
+  struct thread_info **threads = NULL;
+  int n_threads, i, ret;
+  regex_t preg_buffer;
+  regex_t *preg = NULL;
+
+  if (requested_threads && requested_threads[0] == 'r')
+    {
+      /* User has supplied a regular expression.  */
+      requested_threads = skip_spaces (&requested_threads[1]);
+      ret = regcomp (&preg_buffer, requested_threads,
+                     print_thread_info_regex_cflags);
+      if (ret == 0)
+        preg = &preg_buffer;
+      else
+        {
+          char *err =  get_regcomp_error (ret, &preg_buffer);
+          make_cleanup (xfree, err);
+          error (_("Invalid regexp (%s): %s"), err, requested_threads);
+        }
+    }
 
   update_thread_list ();
   current_ptid = inferior_ptid;
@@ -776,37 +966,52 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
 
   /* For backward compatibility, we make a list for MI.  A table is
      preferable for the CLI, though, because it shows table
-     headers.  */
+     headers.
+     We do not sort the list for MI.  */
   if (ui_out_is_mi_like_p (uiout))
-    make_cleanup_ui_out_list_begin_end (uiout, "threads");
-  else
     {
-      int n_threads = 0;
+      make_cleanup_ui_out_list_begin_end (uiout, "threads");
 
       for (tp = thread_list; tp; tp = tp->next)
-	{
-	  if (!number_is_in_list (requested_threads, tp->num))
-	    continue;
+        {
+          if (ptid_equal (tp->ptid, current_ptid))
+            current_thread = tp->num;
 
-	  if (pid != -1 && PIDGET (tp->ptid) != pid)
-	    continue;
+          if (print_thread_filter_check (tp, requested_threads, pid, NULL))
+            print_thread_info_single (uiout, tp, current_ptid);
+        }
+    }
+  else
+    {
+      threads = xmalloc (sizeof (*threads) * thread_list_size);
+      make_cleanup (free, threads);
 
-	  if (tp->state == THREAD_EXITED)
-	    continue;
 
-	  ++n_threads;
-	}
+      n_threads = 0;
+      for (i = 0, tp = thread_list; tp; tp = tp->next, ++i)
+        {
+          thread_cache_name (tp);
+          if (print_thread_filter_check (tp, requested_threads, pid, preg))
+            {
+              threads[n_threads] = tp;
+              ++n_threads;
+            }
+
+          if (ptid_equal (tp->ptid, current_ptid))
+            current_thread = tp->num;
+
+        }
 
       if (n_threads == 0)
-	{
-	  if (requested_threads == NULL || *requested_threads == '\0')
-	    ui_out_message (uiout, 0, _("No threads.\n"));
-	  else
-	    ui_out_message (uiout, 0, _("No threads match '%s'.\n"),
-			    requested_threads);
-	  do_cleanups (old_chain);
-	  return;
-	}
+        {
+          if (requested_threads == NULL || *requested_threads == '\0')
+            ui_out_message (uiout, 0, _("No threads.\n"));
+          else
+            ui_out_message (uiout, 0, _("No threads match '%s'.\n"),
+                                        requested_threads);
+          do_cleanups (old_chain);
+          return;
+        }
 
       make_cleanup_ui_out_table_begin_end (uiout, 4, n_threads, "threads");
 
@@ -815,115 +1020,14 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid)
       ui_out_table_header (uiout, 17, ui_left, "target-id", "Target Id");
       ui_out_table_header (uiout, 1, ui_left, "frame", "Frame");
       ui_out_table_body (uiout);
-    }
-
-  for (tp = thread_list; tp; tp = tp->next)
-    {
-      struct cleanup *chain2;
-      int core;
-
-      if (!number_is_in_list (requested_threads, tp->num))
-	continue;
-
-      if (pid != -1 && PIDGET (tp->ptid) != pid)
-	{
-	  if (requested_threads != NULL && *requested_threads != '\0')
-	    error (_("Requested thread not found in requested process"));
-	  continue;
-	}
-
-      if (ptid_equal (tp->ptid, current_ptid))
-	current_thread = tp->num;
-
-      if (tp->state == THREAD_EXITED)
-	continue;
-
-      chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
 
-      if (ui_out_is_mi_like_p (uiout))
-	{
-	  /* Compatibility.  */
-	  if (ptid_equal (tp->ptid, current_ptid))
-	    ui_out_text (uiout, "* ");
-	  else
-	    ui_out_text (uiout, "  ");
-	}
-      else
-	{
-	  if (ptid_equal (tp->ptid, current_ptid))
-	    ui_out_field_string (uiout, "current", "*");
-	  else
-	    ui_out_field_skip (uiout, "current");
-	}
-
-      ui_out_field_int (uiout, "id", tp->num);
-
-      /* For the CLI, we stuff everything into the target-id field.
-	 This is a gross hack to make the output come out looking
-	 correct.  The underlying problem here is that ui-out has no
-	 way to specify that a field's space allocation should be
-	 shared by several fields.  For MI, we do the right thing
-	 instead.  */
-
-      target_id = target_pid_to_str (tp->ptid);
-      extra_info = target_extra_thread_info (tp);
-      name = tp->name ? tp->name : target_thread_name (tp);
-
-      if (ui_out_is_mi_like_p (uiout))
-	{
-	  ui_out_field_string (uiout, "target-id", target_id);
-	  if (extra_info)
-	    ui_out_field_string (uiout, "details", extra_info);
-	  if (name)
-	    ui_out_field_string (uiout, "name", name);
-	}
-      else
-	{
-	  struct cleanup *str_cleanup;
-	  char *contents;
-
-	  if (extra_info && name)
-	    contents = xstrprintf ("%s \"%s\" (%s)", target_id,
-				   name, extra_info);
-	  else if (extra_info)
-	    contents = xstrprintf ("%s (%s)", target_id, extra_info);
-	  else if (name)
-	    contents = xstrprintf ("%s \"%s\"", target_id, name);
-	  else
-	    contents = xstrdup (target_id);
-	  str_cleanup = make_cleanup (xfree, contents);
-
-	  ui_out_field_string (uiout, "target-id", contents);
-	  do_cleanups (str_cleanup);
-	}
-
-      if (tp->state == THREAD_RUNNING)
-	ui_out_text (uiout, "(running)\n");
-      else
-	{
-	  /* The switch below puts us at the top of the stack (leaf
-	     frame).  */
-	  switch_to_thread (tp->ptid);
-	  print_stack_frame (get_selected_frame (NULL),
-			     /* For MI output, print frame level.  */
-			     ui_out_is_mi_like_p (uiout),
-			     LOCATION);
-	}
-
-      if (ui_out_is_mi_like_p (uiout))
-	{
-	  char *state = "stopped";
-
-	  if (tp->state == THREAD_RUNNING)
-	    state = "running";
-	  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);
+      qsort (threads, n_threads, sizeof (*threads), print_thread_sort_cmp);
 
-      do_cleanups (chain2);
+      for (i = 0; i < n_threads; i++)
+        {
+          print_thread_info_single (uiout, threads[i], current_ptid);
+        }
     }
 
   /* Restores the current thread and the frame selected before
-- 
1.7.7.3

