From 60f98dde0ae5b9d8a59732a67c6ea0b272cc1615 Mon Sep 17 00:00:00 2001 From: Michael Snyder Date: Tue, 15 Feb 2011 21:17:53 +0000 Subject: [PATCH] 2011-02-15 Michael Snyder * command.h (enum command_class): New class 'no_set_class', for "show" commands without a corresponding "set" command. * value.c (_initialize_values): Use 'no_set_class' for "show values". * copying.c (_initialize_copying): Ditto for "show copying" and "show warranty". * cli/cli-cmds.c (init_cli_cmds): Ditto for "show commands" and "show version". * cli/cli-setshow.c (cmd_show_list): Skip "show" commands for which there is no corresponding "set" command (eg. "show copying"). 2011-02-14 Michael Snyder * gdb.texinfo (threads): Document argument for "info threads" cmd. Document new command "thread find". 2011-02-15 Michael Snyder * gdb.base/default.exp: Add tests for thread commands. * gdb.base/help.exp: Add tests for thread commands. * gdb.threads/thread-find.exp: New test for thread find command. --- gdb/ChangeLog | 24 +- gdb/NEWS | 4 + gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 27 +- gdb/testsuite/ChangeLog | 8 +- gdb/testsuite/gdb.base/default.exp | 10 + gdb/testsuite/gdb.base/help.exp | 10 + gdb/testsuite/gdb.threads/thread-find.exp | 430 ++++++++++++++++++++++ gdb/thread.c | 116 +++++- 9 files changed, 610 insertions(+), 24 deletions(-) create mode 100644 gdb/testsuite/gdb.threads/thread-find.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index c9c08d402e..6d4c923595 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,15 @@ +2011-02-15 Michael Snyder + + * command.h (enum command_class): New class 'no_set_class', for + "show" commands without a corresponding "set" command. + * value.c (_initialize_values): Use 'no_set_class' for "show values". + * copying.c (_initialize_copying): Ditto for "show copying" and + "show warranty". + * cli/cli-cmds.c (init_cli_cmds): Ditto for "show commands" and + "show version". + * cli/cli-setshow.c (cmd_show_list): Skip "show" commands for + which there is no corresponding "set" command (eg. "show copying"). + 2011-02-15 Jan Kratochvil * Makefile.in (ACLOCAL_AMFLAGS): Add `-I ../config'. @@ -30,18 +42,6 @@ (efpr_pseudo_register_write): Use regcache_raw_write_part to read and write data from/to the VMX register. -2011-02-14 Michael Snyder - - * command.h (enum command_class): New class 'no_set_class', for - "show" commands without a corresponding "set" command. - * value.c (_initialize_values): Use 'no_set_class' for "show values". - * copying.c (_initialize_copying): Ditto for "show copying" and - "show warranty". - * cli/cli-cmds.c (init_cli_cmds): Ditto for "show commands" and - "show version". - * cli/cli-setshow.c (cmd_show_list): Skip "show" commands for - which there is no corresponding "set" command (eg. "show copying"). - 2011-02-14 Pedro Alves Jan Kratochvil diff --git a/gdb/NEWS b/gdb/NEWS index 2e7c3e3b3e..a07d32dc8f 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,10 @@ *** Changes since GDB 7.2 +* GDB has a new command: "thread find [REGEXP]". + It finds the thread id whose name, target id, or thread extra info + matches the given regular expression. + * The "catch syscall" command now works on mips*-linux* targets. * The -data-disassemble MI command now supports modes 2 and 3 for diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 8d671aa9ce..ca8f3bb725 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2011-02-14 Michael Snyder + + * gdb.texinfo (threads): Document argument for "info threads" cmd. + Document new command "thread find". + 2011-02-14 Michael Snyder * gdb.texinfo (Threads): Update example of new thread message. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index bed074faae..e16065b994 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -2706,9 +2706,11 @@ number---always a single integer---with each thread in your program. @table @code @kindex info threads -@item info threads -Display a summary of all threads currently in your -program. @value{GDBN} displays for each thread (in this order): +@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. +@value{GDBN} displays for each thread (in this order): @enumerate @item @@ -2805,6 +2807,25 @@ systems, a name specified with @samp{thread name} will override the system-give name, and removing the user-specified name will cause @value{GDBN} to once again display the system-specified name. +@kindex thread find +@cindex search for a thread +@item thread find [@var{regexp}] +Search for and display thread ids whose name or @var{systag} +matches the supplied regular expression. + +As well as being the complement to the @samp{thread name} command, +this command also allows you to identify a thread by its target +@var{systag}. For instance, on @sc{gnu}/Linux, the target @var{systag} +is the LWP id. + +@smallexample +(@value{GDBN}) thread find 26688 +Thread 4 has target id 'Thread 0x41e02940 (LWP 26688)' +(@value{GDBN}) info thread 4 + Id Target Id Frame + 4 Thread 0x41e02940 (LWP 26688) 0x00000031ca6cd372 in select () +@end smallexample + @kindex set print thread-events @cindex print messages on thread start and exit @item set print thread-events diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 6f915ba49b..8f79acd805 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2011-02-15 Michael Snyder + + * gdb.base/default.exp: Add tests for thread commands. + * gdb.base/help.exp: Add tests for thread commands. + * gdb.threads/thread-find.exp: New test for thread find command. + 2011-02-15 Ken Werner * gdb.opencl/datatypes.exp: Expect the size of a bool to be one byte. @@ -461,7 +467,7 @@ updated. * lib/gdb.exp (INTERNAL_GDBFLAGS): Add -data-directory. -2010-11-29 Michael Snyder +2010-11-29 Michael Snyder * gdb.base/callfuncs.exp: Test for skip_float_tests. * gdb.base/call-sc.exp: Ditto. diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index 55beb4bf73..d58c51979f 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -356,6 +356,8 @@ gdb_test "info sources" "No symbol table is loaded. Use the \"file\" command.*" gdb_test_no_output "info target" "info target" #test info terminal gdb_test "info terminal" "No saved terminal information." "info terminal" +# test info threads +gdb_test "info threads" "No threads." "info threads" #test info types gdb_test "info types" "All defined types:" "info types" #test info variables @@ -779,6 +781,14 @@ if ![istarget "*-*-udi*"] then { gdb_test "target" "Argument required .target name.*" "target" #test tbreak gdb_test "tbreak" "No default breakpoint address now." "tbreak" +#test thread +gdb_test "thread" "No thread selected" "thread" +#test thread apply +gdb_test "thread apply" "Please specify a thread ID list" "thread apply" +#test thread find +gdb_test "thread find" "Command requires an argument." "thread find" +#test thread name +gdb_test "thread name" "No thread selected" "thread name" #test tty gdb_test "tty" "Argument required .filename to set it to\..*" "tty" #test until "u" abbreviation diff --git a/gdb/testsuite/gdb.base/help.exp b/gdb/testsuite/gdb.base/help.exp index c0e10b8497..749aee750f 100644 --- a/gdb/testsuite/gdb.base/help.exp +++ b/gdb/testsuite/gdb.base/help.exp @@ -281,6 +281,8 @@ gdb_test "help info symbol" "Describe what symbol is at location ADDR.*" gdb_test "help info target" "Names of targets and files being debugged\.\[\r\n\]+Shows the entire stack of targets currently in use \\(including the exec-file,\[\r\n\]+core-file, and process, if any\\), as well as the symbol file name\." "help info target" # test help info terminal gdb_test "help info terminal" "Print inferior's saved terminal status\." "help info terminal" +# test help info threads +gdb_test "help info threads" "Display currently known threads.*" "help info threads" # test help info types gdb_test "help info types" "All type names, or those matching REGEXP\." "help info types" # test help info variables @@ -644,6 +646,14 @@ test_prefix_command_help "target" { } # test help tbreak gdb_test "help tbreak" "Set a temporary breakpoint.*" "help tbreak" +#test help thread +gdb_test "help thread" "Use this command to switch between threads.*" "help thread" +# test help thread apply +gdb_test "help thread apply" "Apply a command to a list of threads.*" "help thread apply" +# test help thread find +gdb_test "help thread find" "Find threads that match a regular.*" help thread find" +# test help thread name +gdb_test "help thread name" "Set the current thread's name.*" "help thread name" # test help tty gdb_test "help tty" "Set terminal for future runs of program being debugged\.\[\r\n\]+Usage: set inferior-tty /dev/pts/1" "help tty" # test help until "u" abbreviation diff --git a/gdb/testsuite/gdb.threads/thread-find.exp b/gdb/testsuite/gdb.threads/thread-find.exp new file mode 100644 index 0000000000..dd18a95853 --- /dev/null +++ b/gdb/testsuite/gdb.threads/thread-find.exp @@ -0,0 +1,430 @@ +# Copyright 2011 +# 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 . + +# Please email any bugs, comments, and/or additions to this file to: +# bug-gdb@gnu.org + +if $tracelevel then { + strace $tracelevel +} + +set testfile "linux-dp" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != ""} { + return -1 +} + +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} +gdb_test_no_output "set print sevenbit-strings" +runto_main + +# Run until there are some threads. +gdb_breakpoint [gdb_get_line_number "linuxthreads.exp: info threads 2"] +gdb_continue_to_breakpoint "main thread's sleep" + +# Create thread names. +gdb_test "thread apply 1 thread name threadname_1" \ + "Thread 1 .*" \ + "name thread 1" + +gdb_test "thread apply 2 thread name threadname_2" \ + "Thread 2 .*" \ + "name thread 2" + +gdb_test "thread apply 3 thread name threadname_3" \ + "Thread 3 .*" \ + "name thread 3" + +gdb_test "thread apply 4 thread name threadname_4" \ + "Thread 4 .*" \ + "name thread 4" + +gdb_test "thread apply 5 thread name threadname_5" \ + "Thread 5 .*" \ + "name thread 5" + +gdb_test "thread apply 6 thread name threadname_6" \ + "Thread 6 .*" \ + "name thread 6" + +# Collect thread ids, if any. +gdb_test_multiple "info threads" "collect thread id" { + -re ". 6 .*\[Tt\]hread (\[0-9a-fA-Fx\]+).* \"threadname_6\" \[^\r\n\]*" { + set thread6 $expect_out(1,string) + exp_continue + } + -re ". 5 .*\[Tt\]hread (\[0-9a-fA-Fx\]+).* \"threadname_5\" \[^\r\n\]*" { + set thread5 $expect_out(1,string) + exp_continue + } + -re ". 4 .*\[Tt\]hread (\[0-9a-fA-Fx\]+).* \"threadname_4\" \[^\r\n\]*" { + set thread4 $expect_out(1,string) + exp_continue + } + -re ". 3 .*\[Tt\]hread (\[0-9a-fA-Fx\]+).* \"threadname_3\" \[^\r\n\]*" { + set thread3 $expect_out(1,string) + exp_continue + } + -re ". 2 .*\[Tt\]hread (\[0-9a-fA-Fx\]+).* \"threadname_2\" \[^\r\n\]*" { + set thread2 $expect_out(1,string) + exp_continue + } + -re ". 1 .*\[Tt\]hread (\[0-9a-fA-Fx\]+).* \"threadname_1\" \[^\r\n\]*" { + set thread1 $expect_out(1,string) + exp_continue + } + -re ".*$gdb_prompt $" { + pass "collect thread id" + } +} + +if { [info exists thread6] } then { + gdb_test "echo $thread6\\n" "$thread6" "got thread ids" +} + +# Collect process ids, if any. +gdb_test_multiple "info threads" "collect thread id" { + -re ". 6 .*\[Pp\]rocess (\[0-9a-fA-Fx\]+).* \"threadname_6\" \[^\r\n\]*" { + set process6 $expect_out(1,string) + exp_continue + } + -re ". 5 .*\[Pp\]rocess (\[0-9a-fA-Fx\]+).* \"threadname_5\" \[^\r\n\]*" { + set process5 $expect_out(1,string) + exp_continue + } + -re ". 4 .*\[Pp\]rocess (\[0-9a-fA-Fx\]+).* \"threadname_4\" \[^\r\n\]*" { + set process4 $expect_out(1,string) + exp_continue + } + -re ". 3 .*\[Pp\]rocess (\[0-9a-fA-Fx\]+).* \"threadname_3\" \[^\r\n\]*" { + set process3 $expect_out(1,string) + exp_continue + } + -re ". 2 .*\[Pp\]rocess (\[0-9a-fA-Fx\]+).* \"threadname_2\" \[^\r\n\]*" { + set process2 $expect_out(1,string) + exp_continue + } + -re ". 1 .*\[Pp\]rocess (\[0-9a-fA-Fx\]+).* \"threadname_1\" \[^\r\n\]*" { + set process1 $expect_out(1,string) + exp_continue + } + -re ".*$gdb_prompt $" { + pass "collect process id" + } +} + +if { [info exists process6] } then { + gdb_test "echo $process6\\n" "$process6" "got process ids" +} + +# Collect lwp ids, if any. +gdb_test_multiple "info threads" "collect thread id" { + -re ". 6 .*LWP (\[0-9a-fA-Fx\]+).* \"threadname_6\" \[^\r\n\]*" { + set lwp6 $expect_out(1,string) + exp_continue + } + -re ". 5 .*LWP (\[0-9a-fA-Fx\]+).* \"threadname_5\" \[^\r\n\]*" { + set lwp5 $expect_out(1,string) + exp_continue + } + -re ". 4 .*LWP (\[0-9a-fA-Fx\]+).* \"threadname_4\" \[^\r\n\]*" { + set lwp4 $expect_out(1,string) + exp_continue + } + -re ". 3 .*LWP (\[0-9a-fA-Fx\]+).* \"threadname_3\" \[^\r\n\]*" { + set lwp3 $expect_out(1,string) + exp_continue + } + -re ". 2 .*LWP (\[0-9a-fA-Fx\]+).* \"threadname_2\" \[^\r\n\]*" { + set lwp2 $expect_out(1,string) + exp_continue + } + -re ". 1 .*LWP (\[0-9a-fA-Fx\]+).* \"threadname_1\" \[^\r\n\]*" { + set lwp1 $expect_out(1,string) + exp_continue + } + -re ".*$gdb_prompt $" { + pass "collect lwp id" + } +} + +if { [info exists lwp6] } then { + gdb_test "echo $lwp6\\n" "$lwp6" "got lwp ids" +} + +# +# Now: test 'thread find' with names. +# + +gdb_test "thread find threadname_6" \ + "Thread 6 has name 'threadname_6'" "find thread name 6" +gdb_test "thread find threadname_5" \ + "Thread 5 has name 'threadname_5'" "find thread name 5" +gdb_test "thread find threadname_4" \ + "Thread 4 has name 'threadname_4'" "find thread name 4" +gdb_test "thread find threadname_3" \ + "Thread 3 has name 'threadname_3'" "find thread name 3" +gdb_test "thread find threadname_2" \ + "Thread 2 has name 'threadname_2'" "find thread name 2" +gdb_test "thread find threadname_1" \ + "Thread 1 has name 'threadname_1'" "find thread name 1" + +# +# Test 'thread find' with thread ids, if any. +# + +if { [info exists thread6] } then { + gdb_test "thread find $thread6" \ + "Thread 6 has .*$thread6.*" "find thread id 6" + gdb_test "thread find $thread5" \ + "Thread 5 has .*$thread5.*" "find thread id 5" + gdb_test "thread find $thread4" \ + "Thread 4 has .*$thread4.*" "find thread id 4" + gdb_test "thread find $thread3" \ + "Thread 3 has .*$thread3.*" "find thread id 3" + gdb_test "thread find $thread2" \ + "Thread 2 has .*$thread2.*" "find thread id 2" + gdb_test "thread find $thread1" \ + "Thread 1 has .*$thread1.*" "find thread id 1" +} + +# +# Test 'thread find' with process ids, if any. +# + +if { [info exists process6] } then { + gdb_test "thread find $process6" \ + "Thread 6 has .*$process6.*" "find process id 6" + gdb_test "thread find $process5" \ + "Thread 5 has .*$process5.*" "find process id 5" + gdb_test "thread find $process4" \ + "Thread 4 has .*$process4.*" "find process id 4" + gdb_test "thread find $process3" \ + "Thread 3 has .*$process3.*" "find process id 3" + gdb_test "thread find $process2" \ + "Thread 2 has .*$process2.*" "find process id 2" + gdb_test "thread find $process1" \ + "Thread 1 has .*$process1.*" "find process id 1" +} + +# +# Test 'thread find' with lwp ids, if any. +# + +if { [info exists lwp6] } then { + gdb_test "thread find $lwp6" \ + "Thread 6 has .*$lwp6.*" "find lwp id 6" + gdb_test "thread find $lwp5" \ + "Thread 5 has .*$lwp5.*" "find lwp id 5" + gdb_test "thread find $lwp4" \ + "Thread 4 has .*$lwp4.*" "find lwp id 4" + gdb_test "thread find $lwp3" \ + "Thread 3 has .*$lwp3.*" "find lwp id 3" + gdb_test "thread find $lwp2" \ + "Thread 2 has .*$lwp2.*" "find lwp id 2" + gdb_test "thread find $lwp1" \ + "Thread 1 has .*$lwp1.*" "find lwp id 1" +} + +# Test no match. + +gdb_test "thread find foobarbaz" "No threads match .*" "no thread" + +# +# Test regular expression +# + +set see1 0 +set see2 0 +set see3 0 +set see4 0 +set see5 0 +set see6 0 + +gdb_test_multiple "thread find threadname_\[345\]" "test regular exp" { + -re "Thread 6 has name \[^\r\n\]*" { + set see6 1 + exp_continue + } + -re "Thread 5 has name \[^\r\n\]*" { + set see5 1 + exp_continue + } + -re "Thread 4 has name \[^\r\n\]*" { + set see4 1 + exp_continue + } + -re "Thread 3 has name \[^\r\n\]*" { + set see3 1 + exp_continue + } + -re "Thread 2 has name \[^\r\n\]*" { + set see2 1 + exp_continue + } + -re "Thread 1 has name \[^\r\n\]*" { + set see1 1 + exp_continue + } + -re ".*$gdb_prompt $" { + if { $see3 && $see4 && $see5 && !$see1 && !$see2 && !$see6 } then { + pass "test regular exp" + } else { + fail "test regular exp" + } + } +} + +# +# Test info threads on a subset of threads +# + +set see1 0 +set see2 0 +set see3 0 +set see4 0 +set see5 0 +set see6 0 + +gdb_test_multiple "info threads 2 4 6" "info threads 2 4 6" { + -re ". 6 .*\"threadname_6.*\" \[\r\n\]*" { + set see6 1 + exp_continue + } + -re ". 5 .*\"threadname_5.*\" \[\r\n\]*" { + set see5 1 + exp_continue + } + -re ". 4 .*\"threadname_4.*\" \[\r\n\]*" { + set see4 1 + exp_continue + } + -re ". 3 .*\"threadname_3.*\" \[\r\n\]*" { + set see3 1 + exp_continue + } + -re ". 2 .*\"threadname_2.*\" \[\r\n\]*" { + set see2 1 + exp_continue + } + -re ". 1 .*\"threadname_1.*\" \[\r\n\]*" { + set see1 1 + exp_continue + } + -re ".*$gdb_prompt $" { + if { $see2 && $see4 && $see6 && !$see1 && !$see3 && !$see5 } then { + pass "info threads 2 4 6" + } else { + fail "info threads 2 4 6" + } + } +} + +# +# Test info threads on a range +# + +set see1 0 +set see2 0 +set see3 0 +set see4 0 +set see5 0 +set see6 0 + +gdb_test_multiple "info threads 3-5" "info threads 3-5" { + -re ". 6 .*\"threadname_6.*\" \[\r\n\]*" { + set see6 1 + exp_continue + } + -re ". 5 .*\"threadname_5.*\" \[\r\n\]*" { + set see5 1 + exp_continue + } + -re ". 4 .*\"threadname_4.*\" \[\r\n\]*" { + set see4 1 + exp_continue + } + -re ". 3 .*\"threadname_3.*\" \[\r\n\]*" { + set see3 1 + exp_continue + } + -re ". 2 .*\"threadname_2.*\" \[\r\n\]*" { + set see2 1 + exp_continue + } + -re ". 1 .*\"threadname_1.*\" \[\r\n\]*" { + set see1 1 + exp_continue + } + -re ".*$gdb_prompt $" { + if { $see3 && $see4 && $see5 && !$see1 && !$see2 && !$see6 } then { + pass "info threads 3-5" + } else { + fail "info threads 3-5" + } + } +} + +# Test inverted range + +gdb_test "info threads 5-3" "inverted range" "test inverted range" + +# Test degenerate range + +set see1 0 +set see2 0 +set see3 0 +set see4 0 +set see5 0 +set see6 0 + +gdb_test_multiple "info threads 3-3" "info threads 3-3" { + -re ". 6 .*\"threadname_6.*\" \[\r\n\]*" { + set see6 1 + exp_continue + } + -re ". 5 .*\"threadname_5.*\" \[\r\n\]*" { + set see5 1 + exp_continue + } + -re ". 4 .*\"threadname_4.*\" \[\r\n\]*" { + set see4 1 + exp_continue + } + -re ". 3 .*\"threadname_3.*\" \[\r\n\]*" { + set see3 1 + exp_continue + } + -re ". 2 .*\"threadname_2.*\" \[\r\n\]*" { + set see2 1 + exp_continue + } + -re ". 1 .*\"threadname_1.*\" \[\r\n\]*" { + set see1 1 + exp_continue + } + -re ".*$gdb_prompt $" { + if { $see3 && !$see1 && !$see2 && !$see4 && !$see5 && !$see6 } then { + pass "info threads 3-3" + } else { + fail "info threads 3-3" + } + } +} + diff --git a/gdb/thread.c b/gdb/thread.c index 62455c2a49..84691161d8 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -43,6 +43,7 @@ #include "observer.h" #include "annotate.h" #include "cli/cli-decode.h" +#include "gdb_regex.h" /* Definition of struct thread_info exported to gdbthread.h. */ @@ -954,18 +955,54 @@ No selected thread. See `help thread'.\n"); } } - /* Print information about currently known threads - * Note: this has the drawback that it _really_ switches - * threads, which frees the frame cache. A no-side - * effects info-threads command would be nicer. - */ + Optional ARG is a thread id, or list of thread ids. + + Note: this has the drawback that it _really_ switches + threads, which frees the frame cache. A no-side + effects info-threads command would be nicer. */ static void info_threads_command (char *arg, int from_tty) { - print_thread_info (uiout, -1, -1); + int tid = -1; + + if (arg == NULL || *arg == '\0') + { + print_thread_info (uiout, -1, -1); + return; + } + + while (arg != NULL && *arg != '\0') + { + int tmp_tid = strtol (arg, &arg, 0); + unsigned int highrange; + + if (tmp_tid <= 0) + error ("invalid thread id %d\n", tmp_tid); + + tid = tmp_tid; + print_thread_info (uiout, tid, -1); + + while (*arg == ' ' || *arg == '\t') + ++arg; + + if (*arg == '-') + { + /* Do a range of threads. Must be in ascending order. */ + ++arg; /* Skip the hyphen. */ + highrange = strtoul (arg, &arg, 0); + if (highrange < tid) + error (_("inverted range")); + + /* Do the threads in the range (first one already done). */ + while (tid < highrange) + { + print_thread_info (uiout, ++tid, -1); + } + } + } } /* Switch from one thread to another. */ @@ -1313,6 +1350,60 @@ thread_name_command (char *arg, int from_tty) info->name = arg ? xstrdup (arg) : NULL; } +/* Find thread ids with a name, target pid, or extra info matching ARG. */ + +static void +thread_find_command (char *arg, int from_tty) +{ + struct thread_info *tp; + char *tmp; + unsigned long match = 0; + + if (arg == NULL || *arg == '\0') + error (_("Command requires an argument.")); + + tmp = re_comp (arg); + if (tmp != 0) + error (_("Invalid regexp (%s): %s"), tmp, arg); + + update_thread_list (); + for (tp = thread_list; tp; tp = tp->next) + { + if (tp->name != NULL && re_exec (tp->name)) + { + printf_filtered (_("Thread %d has name '%s'\n"), + tp->num, tp->name); + match++; + } + + tmp = target_thread_name (tp); + if (tmp != NULL && re_exec (tmp)) + { + printf_filtered (_("Thread %d has target name '%s'\n"), + tp->num, tmp); + match++; + } + + tmp = target_pid_to_str (tp->ptid); + if (tmp != NULL && re_exec (tmp)) + { + printf_filtered (_("Thread %d has target id '%s'\n"), + tp->num, tmp); + match++; + } + + tmp = target_extra_thread_info (tp); + if (tmp != NULL && re_exec (tmp)) + { + printf_filtered (_("Thread %d has extra info '%s'\n"), + tp->num, tmp); + match++; + } + } + if (!match) + printf_filtered (_("No threads match '%s'\n"), arg); +} + /* Print notices when new threads are attached and detached. */ int print_thread_events = 1; static void @@ -1403,8 +1494,11 @@ _initialize_thread (void) { static struct cmd_list_element *thread_apply_list = NULL; - add_info ("threads", info_threads_command, - _("IDs of currently known threads.")); + add_info ("threads", info_threads_command, + _("Display currently known threads.\n\ +Usage: info threads [ID]...\n\ +Optional arguments are thread IDs with spaces between.\n\ +If no arguments, all threads are displayed.")); add_prefix_cmd ("thread", class_run, thread_command, _("\ Use this command to switch between threads.\n\ @@ -1423,6 +1517,12 @@ The new thread ID must be currently known."), Usage: thread name [NAME]\n\ If NAME is not given, then any existing name is removed."), &thread_cmd_list); + add_cmd ("find", class_run, thread_find_command, _("\ +Find threads that match a regular expression.\n\ +Usage: thread find REGEXP\n\ +Will display thread ids whose name, target ID, or extra info matches REGEXP."), + &thread_cmd_list); + if (!xdb_commands) add_com_alias ("t", "thread", class_run, 1); -- 2.34.1