* [patch] info threads sort by name and name regex matching
@ 2012-08-21 21:19 Aaron Gamble
2012-08-22 10:06 ` Abid, Hafiz
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Aaron Gamble @ 2012-08-21 21:19 UTC (permalink / raw)
To: gdb-patches
[-- 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
^ permalink raw reply [flat|nested] 14+ messages in thread* RE: [patch] info threads sort by name and name regex matching 2012-08-21 21:19 [patch] info threads sort by name and name regex matching Aaron Gamble @ 2012-08-22 10:06 ` Abid, Hafiz 2012-08-22 17:15 ` Eli Zaretskii 2012-08-22 18:52 ` Tom Tromey 2 siblings, 0 replies; 14+ messages in thread From: Abid, Hafiz @ 2012-08-22 10:06 UTC (permalink / raw) To: Aaron Gamble, gdb-patches >+set NR_THREADS 4 >+ >+standard_testfile >+ >+# Start with a fresh gdb. >+gdb_exit >+gdb_start I think you don't need gdb_exit and gdb_start when you are doing clean_restart below. >+ >+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 >+} >+ Regards, Abid > -----Original Message----- > From: gdb-patches-owner@sourceware.org [mailto:gdb-patches- > owner@sourceware.org] On Behalf Of Aaron Gamble > Sent: Tuesday, August 21, 2012 10:19 PM > To: gdb-patches@sourceware.org > Subject: [patch] info threads sort by name and name regex matching > > 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. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-21 21:19 [patch] info threads sort by name and name regex matching Aaron Gamble 2012-08-22 10:06 ` Abid, Hafiz @ 2012-08-22 17:15 ` Eli Zaretskii 2012-08-22 18:52 ` Tom Tromey 2 siblings, 0 replies; 14+ messages in thread From: Eli Zaretskii @ 2012-08-22 17:15 UTC (permalink / raw) To: Aaron Gamble; +Cc: gdb-patches > Date: Tue, 21 Aug 2012 14:18:42 -0700 > From: Aaron Gamble <agamble@google.com> > > Here is a patch for adding sorting by name in 'info threads' and regex > pattern matching of thread names for filtering threads. Thanks. > 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) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This should be documented in the manual. > +@item info threads @r{[}@var{id}@dots{}@b{|}r<@var{regex}>@r{]} No need for <...> around "regex", the @var markup does the equivalent already. > +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 ^^^ Something's missing here, perhaps "for" or an em-dash "---". > +spaces. The list of threads is sorted alphabetically by thread name. ^^ Two spaces between sentences, please. The documentation part is OK with these changes. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-21 21:19 [patch] info threads sort by name and name regex matching Aaron Gamble 2012-08-22 10:06 ` Abid, Hafiz 2012-08-22 17:15 ` Eli Zaretskii @ 2012-08-22 18:52 ` Tom Tromey 2012-08-22 22:37 ` Aaron Gamble 2 siblings, 1 reply; 14+ messages in thread From: Tom Tromey @ 2012-08-22 18:52 UTC (permalink / raw) To: Aaron Gamble; +Cc: gdb-patches >>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes: Aaron> Here is a patch for adding sorting by name in 'info threads' and regex Aaron> pattern matching of thread names for filtering threads. Thanks. I think this is a good idea. Aaron> With this patch sorting by name will happen always. (Perhaps a switch Aaron> to enable sorting would be better?) Yes, I think so. I think sorting by name makes sense but it isn't perhaps always what you want. Sorting by number has the nice feature that it is stable. Aaron> Regex matching is specified by doing 'info threads r<regex>'. This is Aaron> not ambiguous with previous behavior where parameters to info threads Aaron> were only numbers. (spaces between 'r' and <regex> are ignored) I'm curious why you chose this particular spelling. Other possible approaches would be a subcommand, or a flag like "-r". Either of these is perhaps more in keeping with gdb tradition. I'm interested in other opinions here too. Expect some bikeshedding on this point. Aaron> +@item info threads @r{[}@var{id}@dots{}@b{|}r<@var{regex}>@r{]} Aaron> +Display a summary of all threads currently in your program. Optional Aaron> +argument for specifying threads is either @code{r} followed by a regular Aaron> +expression or @var{id}@dots{} one or more numeric thread ids separated by Aaron> +spaces. The list of threads is sorted alphabetically by thread name. I think the documentation should say what the regular expression matches against. Aaron> +# Start with a fresh gdb. Aaron> +gdb_exit Aaron> +gdb_start What Hafiz said :) Aaron> +return 0 There's no need for this in the .exp file. Aaron> diff --git a/gdb/thread.c b/gdb/thread.c [...] Aaron> +#include <stdlib.h> I think defs.h will already include this if it is available. Aaron> +static int print_thread_info_regex_cflags = 0; Aaron> +static int print_thread_info_regex_eflags = 0; I don't think you need these. Just put the constants directly in the re* calls. Also, I'm guessing you want REG_NOSUB at least. Aaron> + if (preg) Aaron> + { Aaron> + int err = regexec (preg, tp->cached_name, 0, NULL, Aaron> + print_thread_info_regex_eflags); I think cached_name can be NULL, because target_thread_name can return NULL. Aaron> +static int Aaron> +print_thread_sort_cmp (const void *p1, const void *p2) Aaron> +{ Aaron> + const char *name1 = (*(struct thread_info **)p1)->cached_name; Aaron> + const char *name2 = (*(struct thread_info **)p2)->cached_name; GNU style requires some spaces in here, before "p1" and "p2". Aaron> + if (name1 && name2) Aaron> + return strcoll (name1, name2); strcoll isn't used in gdb yet. So, you have to look to see whether configury is required. I usually check gnulib. Using strcmp seems just as good though. Aaron> + /* Incase we receive null pointers instead of strings. s/Incase/In case/ and s/null/NULL/ Aaron> +/* Caches the name that will be shown to the user for a thread. Aaron> + We keep track of this for sorting purposes. */ Aaron> + Aaron> +static void Aaron> +thread_cache_name (struct thread_info *tp) Aaron> +{ Aaron> + tp->cached_name = tp->name ? tp->name : target_thread_name (tp); Too much indentation. I think the cached_name field should have a comment explaining that it doesn't need to be freed, and that it is just transient. Aaron> void Aaron> print_thread_info (struct ui_out *uiout, char *requested_threads, int pid) Aaron> { Aaron> struct thread_info *tp; Aaron> ptid_t current_ptid; Aaron> struct cleanup *old_chain; Aaron> - char *extra_info, *name, *target_id; Aaron> int current_thread = -1; Aaron> + struct thread_info **threads = NULL; Aaron> + int n_threads, i, ret; Aaron> + regex_t preg_buffer; Aaron> + regex_t *preg = NULL; Aaron> + Aaron> + if (requested_threads && requested_threads[0] == 'r') Aaron> + { Aaron> + /* User has supplied a regular expression. */ Aaron> + requested_threads = skip_spaces (&requested_threads[1]); Aaron> + ret = regcomp (&preg_buffer, requested_threads, Aaron> + print_thread_info_regex_cflags); Nothing ever calls regfree on preg_buffer. You probably want to use make_regfree_cleanup. You may want to rearrange the code so that either the assignment to old_chain happens before this, or to make a null cleanup first. Aaron> + threads = xmalloc (sizeof (*threads) * thread_list_size); Aaron> + make_cleanup (free, threads); I think making a VEC here would be better. Then you wouldn't need thread_list_size at all. Tom ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-22 18:52 ` Tom Tromey @ 2012-08-22 22:37 ` Aaron Gamble 2012-08-22 23:30 ` Sergio Durigan Junior 0 siblings, 1 reply; 14+ messages in thread From: Aaron Gamble @ 2012-08-22 22:37 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches On Wed, Aug 22, 2012 at 11:51 AM, Tom Tromey <tromey@redhat.com> wrote: *snip* > Aaron> With this patch sorting by name will happen always. (Perhaps a switch > Aaron> to enable sorting would be better?) > > Yes, I think so. I think sorting by name makes sense but it isn't > perhaps always what you want. Sorting by number has the nice feature > that it is stable. > > Aaron> Regex matching is specified by doing 'info threads r<regex>'. This is > Aaron> not ambiguous with previous behavior where parameters to info threads > Aaron> were only numbers. (spaces between 'r' and <regex> are ignored) > > I'm curious why you chose this particular spelling. > Other possible approaches would be a subcommand, or a flag like "-r". > Either of these is perhaps more in keeping with gdb tradition. > > I'm interested in other opinions here too. > > Expect some bikeshedding on this point. I picked a single letter for it to be quick and easy to type. I suppose it's possible 'info threads' could be expanded to do other sorts of matching where different flags would be useful. How about 'info threads [ -a ] [ id.. | -n <name regex> ]' -a - Sort alphabetically by name -n regex - Match thread names with regex Of course then we could do sorting based on the name of the function a thread is in or other sorts of sorting. So other sorting flags would need to be introduced. *snip* > Aaron> + threads = xmalloc (sizeof (*threads) * thread_list_size); > Aaron> + make_cleanup (free, threads); > > I think making a VEC here would be better. > Then you wouldn't need thread_list_size at all. Hmm, not sure if this would improve performance at all. A one time allocation bounded by the number of threads vs VEC's implementation of expanding arrays. I'll wait for others feedback. > > Tom Thanks for the feedback. I'll fix the other things you mentioned I snipped out. -Aaron ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-22 22:37 ` Aaron Gamble @ 2012-08-22 23:30 ` Sergio Durigan Junior 2012-08-23 16:00 ` Tom Tromey 0 siblings, 1 reply; 14+ messages in thread From: Sergio Durigan Junior @ 2012-08-22 23:30 UTC (permalink / raw) To: Aaron Gamble; +Cc: Tom Tromey, gdb-patches On Wednesday, August 22 2012, Aaron Gamble wrote: > On Wed, Aug 22, 2012 at 11:51 AM, Tom Tromey <tromey@redhat.com> wrote: >> Aaron> With this patch sorting by name will happen always. (Perhaps a switch >> Aaron> to enable sorting would be better?) >> >> Yes, I think so. I think sorting by name makes sense but it isn't >> perhaps always what you want. Sorting by number has the nice feature >> that it is stable. >> >> Aaron> Regex matching is specified by doing 'info threads r<regex>'. This is >> Aaron> not ambiguous with previous behavior where parameters to info threads >> Aaron> were only numbers. (spaces between 'r' and <regex> are ignored) >> >> I'm curious why you chose this particular spelling. >> Other possible approaches would be a subcommand, or a flag like "-r". >> Either of these is perhaps more in keeping with gdb tradition. >> >> I'm interested in other opinions here too. >> >> Expect some bikeshedding on this point. > I picked a single letter for it to be quick and easy to type. I > suppose it's possible 'info threads' could be expanded to do other > sorts of matching where different flags would be useful. > > How about 'info threads [ -a ] [ id.. | -n <name regex> ]' > > -a - Sort alphabetically by name > -n regex - Match thread names with regex > > Of course then we could do sorting based on the name of the function a > thread is in or other sorts of sorting. So other sorting flags would > need to be introduced. Why not `-r regex'? I think it would be more clear. Other commands (`sharedlibrary', `info variables', etc) take a regex as their first argument, without requiring a modifier like `-r'. If the "pipe" patch were already in, this alphabetical sorting would not be needed... Anyway, just thinking here. > *snip* >> Aaron> + threads = xmalloc (sizeof (*threads) * thread_list_size); >> Aaron> + make_cleanup (free, threads); >> >> I think making a VEC here would be better. >> Then you wouldn't need thread_list_size at all. > > Hmm, not sure if this would improve performance at all. A one time > allocation bounded by the number of threads vs VEC's implementation of > expanding arrays. I'll wait for others feedback. I guess Tom did not suggest this because of performance per se, but rather because if you have to make a list in GDB then it is already a convention to use VEC for these things. BTW, I noticed one minor nit in your patch: you do not obey the "TAB vs. space" rule. Basically, if you have 8 spaces, then you should convert it to a TAB. Emacs does this for free, but if you're using Vim you can ask me offlist and I can send you a little function that does that. Thanks, -- Sergio ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-22 23:30 ` Sergio Durigan Junior @ 2012-08-23 16:00 ` Tom Tromey 2012-08-24 1:09 ` Aaron Gamble 0 siblings, 1 reply; 14+ messages in thread From: Tom Tromey @ 2012-08-23 16:00 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: Aaron Gamble, gdb-patches Sergio> Why not `-r regex'? I think it would be more clear. Me too. Sergio> Other commands (`sharedlibrary', `info variables', etc) take a regex as Sergio> their first argument, without requiring a modifier like `-r'. If the Sergio> "pipe" patch were already in, this alphabetical sorting would not be Sergio> needed... Anyway, just thinking here. Piping is nice but I think it is also good to have options for common cases. Sergio> I guess Tom did not suggest this because of performance per se, but Sergio> rather because if you have to make a list in GDB then it is already a Sergio> convention to use VEC for these things. Yeah. I thought it would make the code simpler. Tom ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-23 16:00 ` Tom Tromey @ 2012-08-24 1:09 ` Aaron Gamble 2012-08-24 17:58 ` Tom Tromey 0 siblings, 1 reply; 14+ messages in thread From: Aaron Gamble @ 2012-08-24 1:09 UTC (permalink / raw) To: Tom Tromey; +Cc: Sergio Durigan Junior, gdb-patches [-- Attachment #1: Type: text/plain, Size: 1165 bytes --] Here is an updated patch. I have changed the options to be as follows. 'info threads [-a] [ id ... | -r regex ]' -a enables sorting by thread name -r regex matches regex with thread names. I have not changed the threads array to a VEC as of yet. At this point it seems like more trouble than it's worth. But I suppose it could be done if people think it's necessary. On Thu, Aug 23, 2012 at 9:00 AM, Tom Tromey <tromey@redhat.com> wrote: > > Sergio> Why not `-r regex'? I think it would be more clear. > > Me too. > > Sergio> Other commands (`sharedlibrary', `info variables', etc) take a regex as > Sergio> their first argument, without requiring a modifier like `-r'. If the > Sergio> "pipe" patch were already in, this alphabetical sorting would not be > Sergio> needed... Anyway, just thinking here. > > Piping is nice but I think it is also good to have options for common > cases. > > Sergio> I guess Tom did not suggest this because of performance per se, but > Sergio> rather because if you have to make a list in GDB then it is already a > Sergio> convention to use VEC for these things. > > Yeah. I thought it would make the code simpler. > > Tom [-- Attachment #2: thread_sorting_regex_with-a-r-options.patch --] [-- Type: application/octet-stream, Size: 22268 bytes --] From efef7b796f528835d18bb778304805e6228695fd Mon Sep 17 00:00:00 2001 From: Aaron Gamble <agamble@google.com> Date: Thu, 23 Aug 2012 18:04:03 -0700 Subject: [PATCH] sorting with -a -r regex options --- gdb/doc/gdb.texinfo | 11 +- gdb/gdbthread.h | 6 +- gdb/testsuite/gdb.threads/Makefile.in | 2 +- gdb/testsuite/gdb.threads/info-thread-sort-regex.c | 122 +++++++ .../gdb.threads/info-thread-sort-regex.exp | 58 +++ gdb/thread.c | 374 +++++++++++++------- 6 files changed, 441 insertions(+), 132 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..3e6c108 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -2751,10 +2751,13 @@ 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{[}-a@r{]} @r{[}@var{id}@dots{}@b{|}-r @var{regex}@r{]} +Display a summary of all threads currently in your program. Use the option +-a to sort threads alphabetically by name. Selection of threads can be done +by supplying a list or ranges of thread ID's, or by using the -r @var{regex} +option to display only threads with names matching @var{regex}. Spaces +between the -r and @var{regex} are ignored. + @value{GDBN} displays for each thread (in this order): @enumerate diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 0250555..2463cea 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 @@ -385,7 +389,7 @@ extern struct cmd_list_element *thread_cmd_list; `set print thread-events'. */ extern int print_thread_events; -extern void print_thread_info (struct ui_out *uiout, char *threads, +extern void print_thread_info (struct ui_out *uiout, char *args, int pid); extern struct cleanup *make_cleanup_restore_current_thread (void); 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..a49eedf --- /dev/null +++ b/gdb/testsuite/gdb.threads/info-thread-sort-regex.exp @@ -0,0 +1,58 @@ +# 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 + +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 -a" \ + "\\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" diff --git a/gdb/thread.c b/gdb/thread.c index 7e8eec5..68b2dac 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -55,6 +55,7 @@ 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 void thread_command (char *tidstr, int from_tty); @@ -132,6 +133,7 @@ free_thread (struct thread_info *tp) xfree (tp->private); } + thread_list_size--; xfree (tp->name); xfree (tp); } @@ -153,6 +155,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 +172,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 +752,195 @@ 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 ? tp->cached_name : "", + 0, NULL, 0); + 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 strcmp (name1, name2); + + /* In case 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) +{ + /* Does not need to be freed, is only transient. */ + 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) +print_thread_info (struct ui_out *uiout, char *args, 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; + int enable_sort = FALSE; + regex_t preg_buffer; + regex_t *preg = NULL; + char *requested_threads = args; update_thread_list (); current_ptid = inferior_ptid; @@ -774,27 +948,75 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid) /* We'll be switching threads temporarily. */ old_chain = make_cleanup_restore_current_thread (); + /* Process argument string args for options. */ + args = skip_spaces (args); + while (args && *args != '\0') + { + if (*args != '-') + break; + if (args[1] == 'a') + { + enable_sort = TRUE; + args = skip_spaces (args + 2); + requested_threads = args; + continue; + } + if (args[1] == 'r') + { + args = skip_spaces (&args[2]); + ret = regcomp (&preg_buffer, args, REG_NOSUB); + if (ret == 0) + { + preg = &preg_buffer; + make_regfree_cleanup (preg); + } + else + { + char *err = get_regcomp_error (ret, &preg_buffer); + make_cleanup (xfree, err); + error (_("Invalid regexp (%s): %s"), err, requested_threads); + } + break; + } + error (_("Bad option (%s)"), &args[1]); + break; + } + /* 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 (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 (pid != -1 && PIDGET (tp->ptid) != pid) - continue; + 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 (tp->state == THREAD_EXITED) - continue; + if (ptid_equal (tp->ptid, current_ptid)) + current_thread = tp->num; - ++n_threads; } if (n_threads == 0) @@ -803,7 +1025,7 @@ print_thread_info (struct ui_out *uiout, char *requested_threads, int pid) ui_out_message (uiout, 0, _("No threads.\n")); else ui_out_message (uiout, 0, _("No threads match '%s'.\n"), - requested_threads); + args); do_cleanups (old_chain); return; } @@ -815,115 +1037,15 @@ 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 (enable_sort) + qsort (threads, n_threads, sizeof (*threads), print_thread_sort_cmp); - if (pid != -1 && PIDGET (tp->ptid) != pid) + for (i = 0; i < n_threads; i++) { - if (requested_threads != NULL && *requested_threads != '\0') - error (_("Requested thread not found in requested process")); - continue; + print_thread_info_single (uiout, threads[i], current_ptid); } - - 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); - - do_cleanups (chain2); } /* Restores the current thread and the frame selected before -- 1.7.7.3 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-24 1:09 ` Aaron Gamble @ 2012-08-24 17:58 ` Tom Tromey 2012-08-24 22:23 ` Aaron Gamble 0 siblings, 1 reply; 14+ messages in thread From: Tom Tromey @ 2012-08-24 17:58 UTC (permalink / raw) To: Aaron Gamble; +Cc: Sergio Durigan Junior, gdb-patches >>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes: Aaron> -a enables sorting by thread name Is '-a' mnemonic for something? Aaron> +static int Aaron> +print_thread_sort_cmp (const void *p1, const void *p2) Aaron> +{ Aaron> + const char *name1 = (*(struct thread_info **) p1)->cached_name; Aaron> + const char *name2 = (*(struct thread_info **) p2)->cached_name; Aaron> + if (name1 && name2) Aaron> + return strcmp (name1, name2); The gdb style is to have a blank line between declarations and code. Aaron> +static void Aaron> +thread_cache_name (struct thread_info *tp) Aaron> +{ Aaron> + /* Does not need to be freed, is only transient. */ Aaron> + tp->cached_name = tp->name ? tp->name : target_thread_name (tp); I realized while re-reading the patch that this isn't safe. target_thread_name has a funny contract, as you note, where it returns static data. But this means that the result can't be cached -- otherwise all threads with non-NULL names will end up with the same cached name. Either you have to copy the name here or change target_thread_name to do so; and then make sure to free at the right spots, etc. Tom ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-24 17:58 ` Tom Tromey @ 2012-08-24 22:23 ` Aaron Gamble 2012-08-24 22:32 ` Sergio Durigan Junior 0 siblings, 1 reply; 14+ messages in thread From: Aaron Gamble @ 2012-08-24 22:23 UTC (permalink / raw) To: Tom Tromey; +Cc: Sergio Durigan Junior, gdb-patches On Fri, Aug 24, 2012 at 10:57 AM, Tom Tromey <tromey@redhat.com> wrote: > >>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes: > > Aaron> -a enables sorting by thread name > > Is '-a' mnemonic for something? -a is for alphabetical. Would -s be more appropriate? > Aaron> +static int > Aaron> +print_thread_sort_cmp (const void *p1, const void *p2) > Aaron> +{ > Aaron> + const char *name1 = (*(struct thread_info **) p1)->cached_name; > Aaron> + const char *name2 = (*(struct thread_info **) p2)->cached_name; > Aaron> + if (name1 && name2) > Aaron> + return strcmp (name1, name2); > > The gdb style is to have a blank line between declarations and code. Ack. > Aaron> +static void > Aaron> +thread_cache_name (struct thread_info *tp) > Aaron> +{ > Aaron> + /* Does not need to be freed, is only transient. */ > Aaron> + tp->cached_name = tp->name ? tp->name : target_thread_name (tp); > > I realized while re-reading the patch that this isn't safe. > target_thread_name has a funny contract, as you note, where it returns > static data. But this means that the result can't be cached -- > otherwise all threads with non-NULL names will end up with the same > cached name. > > Either you have to copy the name here or change target_thread_name to do so; > and then make sure to free at the right spots, etc. Would it be safe to use target_thread_name in print_thread_sort_cmp each time the name is used in the sort function? I'm not entirely sure why it's unsafe to store the NULL pointers when the name could not be determined. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-24 22:23 ` Aaron Gamble @ 2012-08-24 22:32 ` Sergio Durigan Junior 2012-08-24 23:21 ` Aaron Gamble 2012-08-24 23:28 ` Aaron Gamble 0 siblings, 2 replies; 14+ messages in thread From: Sergio Durigan Junior @ 2012-08-24 22:32 UTC (permalink / raw) To: Aaron Gamble; +Cc: Tom Tromey, gdb-patches On Friday, August 24 2012, Aaron Gamble wrote: > On Fri, Aug 24, 2012 at 10:57 AM, Tom Tromey <tromey@redhat.com> wrote: >> >>>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes: >> >> Aaron> -a enables sorting by thread name >> >> Is '-a' mnemonic for something? > -a is for alphabetical. Would -s be more appropriate? I prefer `-a' FWIW. I could understand that it meant `alphabetical' since the beginning, and I could not find any convention for this on GNU manuals. BTW, `-s' is for `string'? This time I didn't understand it :-). -- Sergio ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-24 22:32 ` Sergio Durigan Junior @ 2012-08-24 23:21 ` Aaron Gamble 2012-08-24 23:28 ` Aaron Gamble 1 sibling, 0 replies; 14+ messages in thread From: Aaron Gamble @ 2012-08-24 23:21 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: Tom Tromey, gdb-patches -s would be for 'sorted' On Fri, Aug 24, 2012 at 3:31 PM, Sergio Durigan Junior <sergiodj@redhat.com> wrote: > > On Friday, August 24 2012, Aaron Gamble wrote: > >> On Fri, Aug 24, 2012 at 10:57 AM, Tom Tromey <tromey@redhat.com> wrote: >>> >>>>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes: >>> >>> Aaron> -a enables sorting by thread name >>> >>> Is '-a' mnemonic for something? >> -a is for alphabetical. Would -s be more appropriate? > > I prefer `-a' FWIW. I could understand that it meant `alphabetical' > since the beginning, and I could not find any convention for this on GNU > manuals. BTW, `-s' is for `string'? This time I didn't understand it :-). > > -- > Sergio ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-24 22:32 ` Sergio Durigan Junior 2012-08-24 23:21 ` Aaron Gamble @ 2012-08-24 23:28 ` Aaron Gamble 2012-08-25 3:12 ` Sergio Durigan Junior 1 sibling, 1 reply; 14+ messages in thread From: Aaron Gamble @ 2012-08-24 23:28 UTC (permalink / raw) To: Sergio Durigan Junior; +Cc: Tom Tromey, gdb-patches -s would be for 'sorted' On Fri, Aug 24, 2012 at 3:31 PM, Sergio Durigan Junior <sergiodj@redhat.com> wrote: > > On Friday, August 24 2012, Aaron Gamble wrote: > >> On Fri, Aug 24, 2012 at 10:57 AM, Tom Tromey <tromey@redhat.com> wrote: >>> >>>>>>>> "Aaron" == Aaron Gamble <agamble@google.com> writes: >>> >>> Aaron> -a enables sorting by thread name >>> >>> Is '-a' mnemonic for something? >> -a is for alphabetical. Would -s be more appropriate? > > I prefer `-a' FWIW. I could understand that it meant `alphabetical' > since the beginning, and I could not find any convention for this on GNU > manuals. BTW, `-s' is for `string'? This time I didn't understand it :-). > > -- > Sergio ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [patch] info threads sort by name and name regex matching 2012-08-24 23:28 ` Aaron Gamble @ 2012-08-25 3:12 ` Sergio Durigan Junior 0 siblings, 0 replies; 14+ messages in thread From: Sergio Durigan Junior @ 2012-08-25 3:12 UTC (permalink / raw) To: Aaron Gamble; +Cc: Tom Tromey, gdb-patches On Friday, August 24 2012, Aaron Gamble wrote: > -s would be for 'sorted' I don't want to make this a big thread on a small thing, but `-s' or `sorted' does not specify what is the criteria for sorting. Anyway, although I prefer `-a', either of them would work as long as it is documented. -- Sergio ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2012-08-25 3:12 UTC | newest] Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2012-08-21 21:19 [patch] info threads sort by name and name regex matching Aaron Gamble 2012-08-22 10:06 ` Abid, Hafiz 2012-08-22 17:15 ` Eli Zaretskii 2012-08-22 18:52 ` Tom Tromey 2012-08-22 22:37 ` Aaron Gamble 2012-08-22 23:30 ` Sergio Durigan Junior 2012-08-23 16:00 ` Tom Tromey 2012-08-24 1:09 ` Aaron Gamble 2012-08-24 17:58 ` Tom Tromey 2012-08-24 22:23 ` Aaron Gamble 2012-08-24 22:32 ` Sergio Durigan Junior 2012-08-24 23:21 ` Aaron Gamble 2012-08-24 23:28 ` Aaron Gamble 2012-08-25 3:12 ` Sergio Durigan Junior
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox