[patch] info threads sort by name and name regex matching

Aaron Gamble
2012-08-21T21:19:05Z

[-- Attachment #1: Type: text/plain, Size: 1272 bytes --]

Hello all,

Here is a patch for adding sorting by name in 'info threads' and regex
pattern matching of thread names for filtering threads.

With this patch sorting by name will happen always. (Perhaps a switch
to enable sorting would be better?)

Regex matching is specified by doing 'info threads r<regex>'. This is
not ambiguous with previous behavior where parameters to info threads
were only numbers. (spaces between 'r' and <regex> are ignored)

e.g. info threads r ^name_prefix

Regards,

gdb/ChangeLog:
* gdbthread.h: Add char *cached_name to struct thread_info for use
when sorting.
* thread.c: #include <stdlib.h>.
(thread_list_size): New global.
(init_Thread_list): Initialize it.
(free_thread): Decrement it.
(new_thread): Increment it.
(print_thread_filter_check): New function.
(print_thread_sort_cmp): New function.
(print_thread_info_single): New function.
(print_thread_info): Sort threads by name and filter by regex match.

testsuite/
* gdb.threads/Makefile.in (EXECUTABLES): Add info-thread-sort-regex.
* gdb.threads/info-thread-sort-regex.c: New file.
* gdb.threads/info-thread-sort-regex.exp: New file.

doc/
* gdb.texinfo (Threads): Add information about sorting and regex parameters.

[-- Attachment #2: info_threads_sort_and_regex.patch --]
[-- Type: application/octet-stream, Size: 22486 bytes --]

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