This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH] thread ID ranges (Per-inferior thread IDs)
- From: Pedro Alves <palves at redhat dot com>
- To: Eli Zaretskii <eliz at gnu dot org>
- Cc: gdb-patches at sourceware dot org
- Date: Thu, 24 Dec 2015 14:55:53 +0000
- Subject: [PATCH] thread ID ranges (Per-inferior thread IDs)
- Authentication-results: sourceware.org; auth=none
- References: <1450206316-25680-1-git-send-email-palves at redhat dot com> <8337v333id dot fsf at gnu dot org> <5672B5D0 dot 6020806 at redhat dot com> <83a8p9yq0p dot fsf at gnu dot org> <56744C3A dot 3040407 at redhat dot com>
On 12/18/2015 06:11 PM, Pedro Alves wrote:
>>> >> +@var{thread-id}. It can be a single thread ID, as shown in the first
>>> >> +field of the @samp{info threads} display, with or without an inferior
>>> >> +qualifier (e.g., @samp{2.1} or @samp{1}); or it could be a range of
>>> >> +thread numbers, as in @code{2-4}. To apply a command to all threads
>>> >> +in descending order, type @kbd{thread apply all @var{command}}. To
>>> >> +apply a command to all threads in ascending order, type @kbd{thread
>>> >> +apply all -ascending @var{command}}.
>> >
>> > Can I use a range of qualified IDs, as in "2.1-2.4"?
> I was thinking of adding support for ranges of threads in the "2.1-4"
> form, that is, I didn't think it'd make sense to cross inferior,
> like "2.1-3.3", but I haven't implemented it yet.
>
I did this now. See the patch below, which applies on top of the
previous one (so you can easily see the new additions).
I expect to squash it all into one patch and resubmit later on.
(I also found a couple other "threadnum" references I had somehow
misssed previously.)
I (force) pushed this to the users/palves/thread-ids-per-inferior
branch for testing convenience.
>From 2ff6a3cfdab76841f54ce657af381e6139b0a2df Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 24 Dec 2015 14:37:39 +0000
Subject: [PATCH] thread ID lists and ranges
---
gdb/doc/gdb.texinfo | 56 ++++---
gdb/gdbthread.h | 91 +++++++++++
gdb/testsuite/gdb.multi/per-inferior-tids.c | 20 ++-
gdb/testsuite/gdb.multi/per-inferior-tids.exp | 135 ++++++++++++----
gdb/testsuite/gdb.threads/pthreads.exp | 43 ++++++
gdb/testsuite/gdb.threads/thread-find.exp | 4 +-
gdb/thread.c | 215 ++++++++++++++++++++++----
7 files changed, 482 insertions(+), 82 deletions(-)
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 071212e..06981c3 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2909,6 +2909,20 @@ Until you create a second inferior, @value{GDBN} does not show the
the full @var{inferior-num}.@var{thread-num} form to refer to threads
of inferior 1, the initial inferior.
+@anchor{thread ID lists}
+@cindex thread ID lists
+Some commands accept a space-separated @dfn{thread ID list} as
+argument. A list element can be a thread ID as shown in the first
+field of the @samp{info threads} display, with or without an inferior
+qualifier (e.g., @samp{2.1} or @samp{1}); or can be a range of thread
+numbers, again with or without an inferior qualifier, as in
+@var{inf1}.@var{thr1}-@var{thr2} or @var{thr1}-@var{thr2} (e.g.,
+@samp{1.2-4} or @samp{2-4}). For example, if the current inferior is
+1, the thread list @samp{1 2-3 4.5 6.7-9} includes threads 1 to 3 of
+inferior 1, thread 5 of inferior 4 and threads 7 to 9 of inferior 6.
+That is, in expanded qualified form, the same as @samp{1.1 1.2 1.3 4.5
+6.7 6.8 6.9}.
+
@anchor{global thread numbers}
@cindex global thread number
@cindex global thread identifier (GDB)
@@ -2931,10 +2945,13 @@ general information on convenience variables.
@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{thread-id-list}@r{]}
+
+Display information about one or more threads. With no arguments
+displays information about all threads. You can specify the list of
+threads that you want to display using the thread ID list syntax
+(@pxref{thread ID lists}).
+
@value{GDBN} displays for each thread (in this order):
@enumerate
@@ -3025,17 +3042,14 @@ threads.
@kindex thread apply
@cindex apply command to several threads
-@item thread apply [@var{thread-id} | all [-ascending]] @var{command}
+@item thread apply [@var{thread-id-list} | all [-ascending]] @var{command}
The @code{thread apply} command allows you to apply the named
-@var{command} to one or more threads. Specify the numbers of the
-threads that you want affected with the command argument
-@var{thread-id}. It can be a single thread ID, as shown in the first
-field of the @samp{info threads} display, with or without an inferior
-qualifier (e.g., @samp{2.1} or @samp{1}); or it could be a range of
-thread numbers, as in @code{2-4}. To apply a command to all threads
-in descending order, type @kbd{thread apply all @var{command}}. To
-apply a command to all threads in ascending order, type @kbd{thread
-apply all -ascending @var{command}}.
+@var{command} to one or more threads. Specify the threads that you
+want affected using the thread ID list syntax (@pxref{thread ID
+lists}), or specify @code{all} to apply to all threads. To apply a
+command to all threads in descending order, type @kbd{thread apply all
+@var{command}}. To apply a command to all threads in ascending order,
+type @kbd{thread apply all -ascending @var{command}}.
@kindex thread name
@@ -4033,7 +4047,7 @@ slow down the running of your program.
@table @code
@kindex watch
-@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint for an expression. @value{GDBN} will break when the
expression @var{expr} is written into by the program and its value
changes. The simplest (and the most popular) use of this command is
@@ -4043,9 +4057,9 @@ to watch the value of a single variable:
(@value{GDBP}) watch foo
@end smallexample
-If the command includes a @code{@r{[}thread @var{threadnum}@r{]}}
+If the command includes a @code{@r{[}thread @var{thread-id}@r{]}}
argument, @value{GDBN} breaks only when the thread identified by
-@var{threadnum} changes the value of @var{expr}. If any other threads
+@var{thread-id} changes the value of @var{expr}. If any other threads
change the value of @var{expr}, @value{GDBN} will not break. Note
that watchpoints restricted to a single thread in this way only work
with Hardware Watchpoints.
@@ -4077,12 +4091,12 @@ Examples:
@end smallexample
@kindex rwatch
-@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint that will break when the value of @var{expr} is read
by the program.
@kindex awatch
-@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
+@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{thread-id}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint that will break when @var{expr} is either read from
or written into by the program.
@@ -27781,10 +27795,10 @@ current-thread-id="1",number-of-threads="3"
@subsubheading Synopsis
@smallexample
- -thread-select @var{threadnum}
+ -thread-select @var{thread-id}
@end smallexample
-Make thread with global thread number @var{threadnum} the current
+Make thread with global thread number @var{thread-id} the current
thread. It prints the number of the new current thread, and the
topmost frame for that thread.
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 99fb03f..35911d7 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -30,6 +30,7 @@ struct symtab;
#include "btrace.h"
#include "common/vec.h"
#include "target/waitstatus.h"
+#include "cli/cli-utils.h"
/* Frontend view of the thread state. Possible extensions: stepping,
finishing, until(ling),... */
@@ -410,6 +411,96 @@ extern int valid_global_thread_id (int thread);
thrown. */
struct thread_info *parse_thread_id (const char *tidstr, const char **end);
+/* The possible states of the tid range parser's state machine. */
+enum tid_range_state
+{
+ /* Parsing the inferior number. */
+ TID_RANGE_STATE_INFERIOR,
+
+ /* Parsing the thread number or thread number range. */
+ TID_RANGE_STATE_THREAD_RANGE,
+};
+
+/* An object of this type is passed to tid_range_parser_get_tid. It
+ must be initialized by calling tid_range_parser_init. This type is
+ defined here so that it can be stack-allocated, but all members
+ should be treated as opaque. */
+struct tid_range_parser
+{
+ /* What sub-component are we expecting. */
+ enum tid_range_state state;
+
+ /* The string being parsed. When parsing has finished, this points
+ past the last parsed token. */
+ const char *string;
+
+ /* The range parser state when we're parsing the thread number
+ sub-component. */
+ struct get_number_or_range_state range_parser;
+
+ /* Last inferior number returned. */
+ int inf_num;
+
+ /* True if the TID last parsed was explicitly inferior-qualified.
+ IOW, whether the spec specified an inferior number
+ explicitly. */
+ int qualified;
+
+ /* The inferior number to assume if the TID is not qualified. */
+ int default_inferior;
+};
+
+/* Initialize a tid_range_parser for use with
+ tid_range_parser_get_tid. TIDLIST is the string to be parsed.
+ DEFAULT_INFERIOR is the inferior number to assume if a
+ non-qualified thread ID is found. */
+extern void tid_range_parser_init (struct tid_range_parser *parser,
+ const char *tidlist,
+ int default_inferior);
+
+/* Parse a thread ID or a thread range list.
+
+ A range will be of the form
+ <inferior_num>.<thread_number1>-<thread_number1> and will represent
+ all the threads of inferior inferior_num with number between
+ thread_number1 and thread_number2, inclusive. <inferior_num> can
+ also be omitted, as in <thread_number1>-<thread_number1>, in which
+ case GDB infers the inferior number from the current inferior.
+
+ While processing a thread ID range list, this function is called
+ iteratively; At each call it will return (in the INF_NUM and
+ THR_NUM output parameters) the next thread in the range.
+
+ At the beginning of parsing a thread range, the char pointer
+ PARSER->string will be advanced past <thread_number1> and left
+ pointing at the '-' token. Subsequent calls will not advance the
+ pointer until the range is completed. The call that completes the
+ range will advance the pointer past <thread_number2>. */
+extern void tid_range_parser_get_tid (struct tid_range_parser *parser,
+ int *inf_num, int *thr_num);
+
+/* Returns non-zero if parsing has completed. */
+extern int tid_range_parser_finished (struct tid_range_parser *parser);
+
+/* Return the string being parsed. When parsing has finished, this
+ points past the last parsed token. */
+const char *tid_range_parser_string (struct tid_range_parser *parser);
+
+/* True if the TID last parsed was explicitly inferior-qualified.
+ IOW, whether the spec specified an inferior number explicitly. */
+extern int tid_range_parser_qualified (struct tid_range_parser *parser);
+
+/* Accept a string-form list of thread IDs such as is accepted by
+ tid_range_parser_get_tid. Return TRUE if the INF_NUM.THR.NUM
+ thread is in the list. DEFAULT_INFERIOR is the inferior number to
+ assume if a non-qualified thread ID is found in the list.
+
+ By definition, an empty list includes all threads. This is to be
+ interpreted as typing a command such as "info threads" with no
+ arguments. */
+extern int tid_is_in_list (const char *list, int default_inferior,
+ int inf_num, int thr_num);
+
/* Search function to lookup a thread by 'pid'. */
extern struct thread_info *find_thread_ptid (ptid_t ptid);
diff --git a/gdb/testsuite/gdb.multi/per-inferior-tids.c b/gdb/testsuite/gdb.multi/per-inferior-tids.c
index 9e77031..00a8298 100644
--- a/gdb/testsuite/gdb.multi/per-inferior-tids.c
+++ b/gdb/testsuite/gdb.multi/per-inferior-tids.c
@@ -18,23 +18,35 @@
#include <unistd.h>
#include <pthread.h>
+pthread_t child_thread[2];
+
void *
-thread_function (void *arg)
+thread_function2 (void *arg)
{
while (1)
sleep (1);
}
+void *
+thread_function1 (void *arg)
+{
+ pthread_create (&child_thread[1], NULL, thread_function2, NULL);
+
+ while (1)
+ sleep (1);
+}
+
int
main (void)
{
- pthread_t child_thread;
int i;
alarm (300);
- pthread_create (&child_thread, NULL, thread_function, NULL);
- pthread_join (child_thread, NULL);
+ pthread_create (&child_thread[0], NULL, thread_function1, NULL);
+
+ for (i = 0; i < 2; i++)
+ pthread_join (child_thread[i], NULL);
return 0;
}
diff --git a/gdb/testsuite/gdb.multi/per-inferior-tids.exp b/gdb/testsuite/gdb.multi/per-inferior-tids.exp
index e892758..d947fbc 100644
--- a/gdb/testsuite/gdb.multi/per-inferior-tids.exp
+++ b/gdb/testsuite/gdb.multi/per-inferior-tids.exp
@@ -17,6 +17,14 @@ load_lib gdb-python.exp
standard_testfile
+# Multiple inferiors are needed, therefore both native and extended
+# gdbserver modes are supported. Only non-extended gdbserver is not
+# supported.
+if [target_info exists use_gdb_stub] {
+ untested ${testfile}.exp
+ return
+}
+
if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {pthreads debug}] } {
return -1
}
@@ -27,10 +35,24 @@ if { ![runto_main] } then {
return -1
}
+# Helper procedure. issue "info threads TID_LIST" and expect EXPECTED
+# (a list of thread ids) to be displayed.
+proc info_threads {tid_list expected {message ""}} {
+ set any "\[^\r\n\]*"
+ set expected [string_to_regexp $expected]
+ set r [join $expected " ${any}\r\n${any} "]
+ set r "${any} $r ${any}"
+ set cmd "info threads $tid_list"
+ if {$message == ""} {
+ set message $cmd
+ }
+ gdb_test $cmd $r $message
+}
+
# "info threads" while there's only inferior 1 should show
# single-number thread IDs.
with_test_prefix "single inferior" {
- gdb_test "info threads" " 1 .* main .* at .*$srcfile:.*"
+ info_threads "" "1"
gdb_test "thread" "Current thread is 1 .*"
}
@@ -43,7 +65,7 @@ with_test_prefix "two inferiors" {
# Now that we'd added another inferior, thread IDs now show the
# inferior number.
- gdb_test "info threads" " 1\.1 .* main .* at .*$srcfile:.*"
+ info_threads "" "1.1"
gdb_test "thread" "Current thread is 1\.1 .*"
@@ -54,36 +76,28 @@ with_test_prefix "two inferiors" {
# Now that we'd added another inferior, thread IDs now show the
# inferior number.
- gdb_test "info threads" \
- [multi_line \
- " 1\.1 .* main .* at .*$srcfile:.*" \
- "\\* 2\.1 .* main .* at .*$srcfile:.*"] \
+ info_threads "" "1.1 2.1" \
"info threads show inferior numbers"
gdb_test "thread" "Current thread is 2\.1 .*" \
"switch to thread using extended thread ID"
- gdb_breakpoint "thread_function"
+ gdb_breakpoint "thread_function1"
gdb_continue_to_breakpoint "once"
gdb_test "inferior 1" "Switching to inferior 1 .*"
gdb_continue_to_breakpoint "twice"
- gdb_test "info threads" \
- [multi_line \
- " 1\.1 .*" \
- "\\* 1\.2 .* thread_function .* at .*$srcfile:.*" \
- " 2\.1 .*" \
- " 2\.2 .* thread_function .* at .*$srcfile:.*"] \
+ info_threads "" "1.1 1.2 2.1 2.2" \
"info threads again"
# Same, but show the global ID.
gdb_test "info threads -gid" \
[multi_line \
" 1\.1 +1 +.*" \
- "\\* 1\.2 +4 +.* thread_function .* at .*$srcfile:.*" \
+ "\\* 1\.2 +4 +.* thread_function1 .* at .*$srcfile:.*" \
" 2\.1 +2 +.*" \
- " 2\.2 +3 +.* thread_function .* at .*$srcfile:.*"]
+ " 2\.2 +3 +.* thread_function1 .* at .*$srcfile:.*"]
# Confirm the convenience variables show the expected numbers.
gdb_test "p \$_thread == 2" " = 1"
@@ -94,12 +108,85 @@ with_test_prefix "two inferiors" {
# global ID by mistake.
gdb_test "thread 4" "Unknown thread 1.4\\."
+ # Test thread ID list parsing. Test qualified and unqualified
+ # IDs; qualified and unqualified ranges; invalid IDs and invalid
+ # ranges.
+
+ # First spawn a couple more threads so ranges includes more than
+ # two threads.
+ with_test_prefix "more threads" {
+ gdb_breakpoint "thread_function2"
+
+ gdb_test "inferior 2" "Switching to inferior 2 .*"
+ gdb_continue_to_breakpoint "once"
+
+ gdb_test "inferior 1" "Switching to inferior 1 .*"
+ gdb_continue_to_breakpoint "twice"
+ }
+
+ info_threads "1 2 3" \
+ "1.1 1.2 1.3"
+
+ # Same, but with qualified thread IDs.
+ info_threads "1.1 1.2 1.3 2.1 2.2" \
+ "1.1 1.2 1.3 2.1 2.2"
+
+ # Test a thread number range.
+ info_threads "1-3" \
+ "1.1 1.2 1.3"
+
+ # Same, but using a qualified range.
+ info_threads "1.1-3" \
+ "1.1 1.2 1.3"
+
+ # A mix of qualified and unqualified thread IDs/ranges.
+ info_threads "1.1 2-3" \
+ "1.1 1.2 1.3"
+
+ info_threads "1 1.2-3" \
+ "1.1 1.2 1.3"
+
+ # Likewise, but mix inferiors too.
+ info_threads "2.1 2-3" \
+ "1.2 1.3 2.1"
+
+ # Multiple ranges with mixed explicit inferiors.
+ info_threads "1.1-2 2.2-3" \
+ "1.1 1.2 2.2 2.3"
+
+ # Now test a set of invalid thread IDs/ranges.
+
+ gdb_test "info threads 1." \
+ "Invalid thread ID: 1."
+
+ gdb_test "info threads 1-3 1." \
+ "Invalid thread ID: 1."
+
+ gdb_test "info threads 1.1.1" \
+ "Invalid thread ID: 1.1.1"
+
+ gdb_test "info threads 2 1.1.1" \
+ "Invalid thread ID: 1.1.1"
+
+ gdb_test "info threads 1.1.1 2" \
+ "Invalid thread ID: 1.1.1 2"
+
+ gdb_test "info threads 1-2.1" \
+ "Invalid thread ID: 1-2.1"
+
+ # Check that we do parse the inferior number.
+ gdb_test "info threads 3.1" \
+ "No threads match '3.1'\."
+
# If Python is configured, check that InferiorThread.global_num
# returns the expected number.
if { ![skip_python_tests] } {
- gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" "test gdb.selected_thread" 1
- gdb_test "python print ('result = %s' % t0.num)" " = 2" "test InferiorThread.num"
- gdb_test "python print ('result = %s' % t0.global_num)" " = 4" "test InferiorThread.global_num"
+ gdb_py_test_silent_cmd "python t0 = gdb.selected_thread ()" \
+ "test gdb.selected_thread" 1
+ gdb_test "python print ('result = %s' % t0.num)" " = 3" \
+ "test InferiorThread.num"
+ gdb_test "python print ('result = %s' % t0.global_num)" " = 6" \
+ "test InferiorThread.global_num"
}
}
@@ -112,10 +199,7 @@ with_test_prefix "back to one inferior" {
# "info threads" while there's only inferior 1 should show
# single-number thread IDs.
- gdb_test "info threads" \
- [multi_line \
- "\\* 1 .*" \
- " 2 .* thread_function .* at .*$srcfile:.*"]
+ info_threads "" "1 2 3"
gdb_test "thread" "Current thread is 1 .*"
}
@@ -129,10 +213,7 @@ with_test_prefix "single-inferior but not initial" {
# Now that we'd added another inferior, thread IDs should show the
# inferior number.
- gdb_test "info threads" \
- [multi_line \
- "\\* 1\.1 .*" \
- " 1\.2 .* thread_function .* at .*$srcfile:.*"] \
+ info_threads "" "1.1 1.2 1.3" \
"info threads with multiple inferiors"
gdb_test "thread" "Current thread is 1\.1 .*"
@@ -146,7 +227,7 @@ with_test_prefix "single-inferior but not initial" {
# Even though we have a single inferior, its number is > 1, so
# thread IDs should show the inferior number.
- gdb_test "info threads" " 3\.1 .* main .* at .*$srcfile:.*" \
+ info_threads "" "3.1" \
"info threads with single inferior"
gdb_test "thread" "Current thread is 3\.1 .*" "thread again"
diff --git a/gdb/testsuite/gdb.threads/pthreads.exp b/gdb/testsuite/gdb.threads/pthreads.exp
index b456641..415e2e6 100644
--- a/gdb/testsuite/gdb.threads/pthreads.exp
+++ b/gdb/testsuite/gdb.threads/pthreads.exp
@@ -244,6 +244,49 @@ proc check_backtraces {} {
".* in main .* in thread1 .* in thread2.*" \
"apply backtrace command to all three threads"
+ # Same, but with qualified thread IDs.
+ gdb_test "thread apply 1.1 1.2 1.3 bt" \
+ ".* in main .* in thread1 .* in thread2.*"
+
+ # Test a thread number range.
+ gdb_test "thread apply 1-3 bt" \
+ ".* in main .* in thread1 .* in thread2.*"
+
+ # Same, but using a qualified range.
+ gdb_test "thread apply 1.1-3 bt" \
+ ".* in main .* in thread1 .* in thread2.*"
+
+ # A mix of qualified and unqualified thread IDs/ranges.
+ gdb_test "thread apply 1.1 2-3 bt" \
+ ".* in main .* in thread1 .* in thread2.*"
+
+ gdb_test "thread apply 1 1.2-3 bt" \
+ ".* in main .* in thread1 .* in thread2.*"
+
+ # Now test a set of invalid thread IDs/ranges.
+
+ gdb_test "thread apply 1. bt" \
+ "Invalid thread ID: 1. bt"
+
+ gdb_test "thread apply 1-3 1. bt" \
+ "Invalid thread ID: 1. bt"
+
+ gdb_test "thread apply 1.1.1 bt" \
+ "Invalid thread ID: 1.1.1 bt"
+
+ gdb_test "thread apply 2 1.1.1 bt" \
+ "Invalid thread ID: 1.1.1 bt"
+
+ gdb_test "thread apply 1.1.1 2 bt" \
+ "Invalid thread ID: 1.1.1 2 bt"
+
+ gdb_test "thread apply 1-2.1 bt" \
+ "Invalid thread ID: 1-2.1 bt"
+
+ # Check that we do parse the inferior number.
+ gdb_test "thread apply 2.1 bt" \
+ "Unknown thread 2.1"
+
# Check that we can do thread specific backtraces
# This also tests that we can do thread specific breakpoints.
diff --git a/gdb/testsuite/gdb.threads/thread-find.exp b/gdb/testsuite/gdb.threads/thread-find.exp
index 1af6bbd..ad66e38 100644
--- a/gdb/testsuite/gdb.threads/thread-find.exp
+++ b/gdb/testsuite/gdb.threads/thread-find.exp
@@ -276,9 +276,9 @@ gdb_test_multiple "info threads 3-3" "info threads 3-3" {
# Test bad input
gdb_test "info thread foo" \
- "Args must be numbers or '.' variables." \
+ "Invalid thread ID: foo" \
"info thread foo"
gdb_test "info thread foo -1" \
- "Args must be numbers or '.' variables." \
+ "Invalid thread ID: foo -1" \
"info thread foo -1"
diff --git a/gdb/thread.c b/gdb/thread.c
index 8fc7b25..21238c8 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1126,8 +1126,8 @@ pc_in_thread_step_range (CORE_ADDR pc, struct thread_info *thread)
}
static int
-should_print_thread (const char *requested_threads, int global_ids,
- int pid, struct thread_info *thr)
+should_print_thread (const char *requested_threads, int default_inferior,
+ int global_ids, int pid, struct thread_info *thr)
{
if (pid != -1 && ptid_get_pid (thr->ptid) != pid
&& requested_threads != NULL && *requested_threads != '\0')
@@ -1135,9 +1135,14 @@ should_print_thread (const char *requested_threads, int global_ids,
if (requested_threads != NULL && *requested_threads != '\0')
{
- int id = global_ids ? thr->global_num : thr->per_inf_num;
+ int in_list;
- if (number_is_in_list (requested_threads, id))
+ if (global_ids)
+ in_list = number_is_in_list (requested_threads, thr->global_num);
+ else
+ in_list = tid_is_in_list (requested_threads, default_inferior,
+ thr->inf->num, thr->per_inf_num);
+ if (in_list)
{
if (pid != -1 && ptid_get_pid (thr->ptid) != pid)
error (_("Requested thread not found in requested process"));
@@ -1170,6 +1175,7 @@ print_thread_info_1 (struct ui_out *uiout, char *requested_threads,
const char *extra_info, *name, *target_id;
int current_thread = -1;
struct inferior *inf;
+ int current_inf_num = current_inferior ()->num;
update_thread_list ();
current_ptid = inferior_ptid;
@@ -1188,7 +1194,8 @@ print_thread_info_1 (struct ui_out *uiout, char *requested_threads,
for (tp = thread_list; tp; tp = tp->next)
{
- if (!should_print_thread (requested_threads, global_ids, pid, tp))
+ if (!should_print_thread (requested_threads, current_inf_num,
+ global_ids, pid, tp))
continue;
++n_threads;
@@ -1235,7 +1242,8 @@ print_thread_info_1 (struct ui_out *uiout, char *requested_threads,
if (ptid_equal (tp->ptid, current_ptid))
current_thread = tp->global_num;
- if (!should_print_thread (requested_threads, global_ids, pid, tp))
+ if (!should_print_thread (requested_threads, current_inf_num,
+ global_ids, pid, tp))
continue;
chain2 = make_cleanup_ui_out_tuple_begin_end (uiout, NULL);
@@ -1774,13 +1782,16 @@ print_thread_id (struct thread_info *thr)
return s;
}
+
+/* Implementation of the "thread apply" command. */
+
static void
thread_apply_command (char *tidlist, int from_tty)
{
char *cmd;
struct cleanup *old_chain;
char *saved_cmd;
- struct get_number_or_range_state state;
+ struct tid_range_parser parser;
if (tidlist == NULL || *tidlist == '\000')
error (_("Please specify a thread ID list"));
@@ -1795,33 +1806,44 @@ thread_apply_command (char *tidlist, int from_tty)
saved_cmd = xstrdup (cmd);
old_chain = make_cleanup (xfree, saved_cmd);
- init_number_or_range (&state, tidlist);
- while (!state.finished && state.string < cmd)
- {
- struct thread_info *tp;
- int start;
-
- start = get_number_or_range (&state);
+ make_cleanup_restore_current_thread ();
- make_cleanup_restore_current_thread ();
+ tid_range_parser_init (&parser, tidlist, current_inferior ()->num);
+ while (!tid_range_parser_finished (&parser)
+ && tid_range_parser_string (&parser) < cmd)
+ {
+ struct thread_info *tp = NULL;
+ struct inferior *inf;
+ int inf_num, thr_num;
- tp = find_thread_id (current_inferior (), start);
+ tid_range_parser_get_tid (&parser, &inf_num, &thr_num);
+ inf = find_inferior_id (inf_num);
+ if (inf != NULL)
+ tp = find_thread_id (inf, thr_num);
+ if (tp == NULL)
+ {
+ if (inferior_list->next != NULL || inferior_list->num != 1
+ || tid_range_parser_qualified (&parser))
+ warning (_("Unknown thread %d.%d"), inf_num, thr_num);
+ else
+ warning (_("Unknown thread %d"), thr_num);
+ continue;
+ }
- if (!tp)
- warning (_("Unknown thread %d."), start);
- else if (!thread_alive (tp))
- warning (_("Thread %d has terminated."), start);
- else
+ if (!thread_alive (tp))
{
- switch_to_thread (tp->ptid);
+ warning (_("Thread %s has terminated."), print_thread_id (tp));
+ continue;
+ }
- printf_filtered (_("\nThread %s (%s):\n"), print_thread_id (tp),
- target_pid_to_str (inferior_ptid));
- execute_command (cmd, from_tty);
+ switch_to_thread (tp->ptid);
- /* Restore exact command used previously. */
- strcpy (cmd, saved_cmd);
- }
+ printf_filtered (_("\nThread %s (%s):\n"), print_thread_id (tp),
+ target_pid_to_str (inferior_ptid));
+ execute_command (cmd, from_tty);
+
+ /* Restore exact command used previously. */
+ strcpy (cmd, saved_cmd);
}
do_cleanups (old_chain);
@@ -2003,6 +2025,143 @@ parse_thread_id (const char *tidstr, const char **end)
return tp;
}
+/* See gdbthread.h. */
+
+void
+tid_range_parser_init (struct tid_range_parser *parser, const char *tidlist,
+ int default_inferior)
+{
+ parser->state = TID_RANGE_STATE_INFERIOR;
+ parser->string = tidlist;
+ parser->inf_num = 0;
+ parser->qualified = 0;
+ parser->default_inferior = default_inferior;
+}
+
+/* See gdbthread.h. */
+
+int
+tid_range_parser_finished (struct tid_range_parser *parser)
+{
+ switch (parser->state)
+ {
+ case TID_RANGE_STATE_INFERIOR:
+ return *parser->string == '\0';
+ case TID_RANGE_STATE_THREAD_RANGE:
+ return parser->range_parser.finished;
+ }
+
+ gdb_assert_not_reached (_("unhandled state"));
+}
+
+/* See gdbthread.h. */
+
+const char *
+tid_range_parser_string (struct tid_range_parser *parser)
+{
+ switch (parser->state)
+ {
+ case TID_RANGE_STATE_INFERIOR:
+ return parser->string;
+ case TID_RANGE_STATE_THREAD_RANGE:
+ return parser->range_parser.string;
+ }
+
+ gdb_assert_not_reached (_("unhandled state"));
+}
+
+/* See gdbthread.h. */
+
+int
+tid_range_parser_qualified (struct tid_range_parser *parser)
+{
+ return parser->qualified;
+}
+
+/* See gdbthread.h. */
+
+void
+tid_range_parser_get_tid (struct tid_range_parser *parser, int *inf_num,
+ int *thr_num)
+{
+ if (parser->state == TID_RANGE_STATE_INFERIOR)
+ {
+ const char *p;
+ const char *space;
+
+ space = skip_to_space (parser->string);
+
+ p = parser->string;
+ while (p < space && *p != '.')
+ p++;
+ if (p < space)
+ {
+ const char *dot = p;
+
+ /* Parse number to the left of the dot. */
+ p = parser->string;
+ parser->inf_num = get_number_trailer (&p, '.');
+ if (parser->inf_num == 0)
+ error (_("Invalid thread ID: %s"), parser->string);
+
+ parser->qualified = 1;
+ p = dot + 1;
+
+ if (isspace (*p))
+ error (_("Invalid thread ID: %s"), parser->string);
+ }
+ else
+ {
+ parser->inf_num = parser->default_inferior;
+ parser->qualified = 0;
+ p = parser->string;
+ }
+
+ init_number_or_range (&parser->range_parser, p);
+ parser->state = TID_RANGE_STATE_THREAD_RANGE;
+ }
+
+ *inf_num = parser->inf_num;
+ *thr_num = get_number_or_range (&parser->range_parser);
+ if (*thr_num == 0)
+ error (_("Invalid thread ID: %s"), parser->string);
+
+ /* If we successfully parsed a thread number or finished parsing a
+ thread range, switch back to assuming the next TID is
+ inferior-qualified. */
+ if (parser->range_parser.end_ptr == NULL
+ || parser->range_parser.string == parser->range_parser.end_ptr)
+ {
+ parser->state = TID_RANGE_STATE_INFERIOR;
+ parser->string = parser->range_parser.string;
+ }
+}
+
+/* See gdbthread.h. */
+
+int
+tid_is_in_list (const char *list, int default_inferior,
+ int inf_num, int thr_num)
+{
+ struct tid_range_parser parser;
+
+ if (list == NULL || *list == '\0')
+ return 1;
+
+ tid_range_parser_init (&parser, list, default_inferior);
+ while (!tid_range_parser_finished (&parser))
+ {
+ int tmp_inf, tmp_thr;
+
+ tid_range_parser_get_tid (&parser, &tmp_inf, &tmp_thr);
+ if (tmp_inf == 0 || tmp_thr == 0)
+ error (_("Invalid thread ID: %s"), parser.string);
+ if (tmp_inf == inf_num && tmp_thr == thr_num)
+ return 1;
+ }
+ return 0;
+}
+
static int
do_captured_thread_select (struct ui_out *uiout, void *tidstr_v)
{
--
1.9.3