[PATCH 1/2] Fix "thread apply $conv_var" and misc other related problems

Pedro Alves
2016-01-13T16:34:52Z

This fixes a few bugs in "thread apply".

While this works:

(gdb) thread apply 1 p 1234

Thread 1 (Thread 0x7ffff7fc1740 (LWP 14048)):
$1 = 1234

This doesn't:

(gdb) thread apply $thr p 1234

Thread 1 (Thread 0x7ffff7fc1740 (LWP 12039)):
Invalid thread ID: p 1234
(gdb)

~~~~

Also, while this works:
(gdb) thread apply 1
Please specify a command following the thread ID list

This doesn't:
(gdb) thread apply $thr
Thread 1 (Thread 0x7ffff7fc1740 (LWP 12039)):
[Current thread is 1 (Thread 0x7ffff7fc1740 (LWP 12039))]
(gdb)

~~~~

And, while this works:
(gdb) thread apply
Please specify a thread ID list

This obviously bogus invocation is just silent:
(gdb) thread apply bt
(gdb)

gdb/ChangeLog:
2016-01-13 Pedro Alves <palves@redhat.com>

* thread.c (thread_apply_command): Use the tid range parser to
advance past the thread ID list.
* tid-parse.c (get_positive_number_trailer): New function.
(parse_thread_id): Use it.
(get_tid_or_range): Use it. Return 0 instead of throwing invalid
thread ID error.
(get_tid_or_range): Detect negative values. Return 0 instead of
throwing invalid thread ID error.

gdb/testsuite/ChangeLog:
2016-01-13 Pedro Alves <palves@redhat.com>

* gdb.multi/tids.exp (thr_apply_info_thr_error): Remove "p 1234"
command from "thread apply" invocation.
(thr_apply_info_thr_invalid): Default the expected output to the
input tid list.
(top level): Add tests that use convenience variables. Add tests
for thread apply with a valid TID list, but missing the command.
---
gdb/testsuite/gdb.multi/tids.exp | 107 ++++++++++++++++++++++++++++++++++-----
gdb/thread.c | 20 ++++++--
gdb/tid-parse.c | 35 ++++++++++---
3 files changed, 139 insertions(+), 23 deletions(-)

diff --git a/gdb/testsuite/gdb.multi/tids.exp b/gdb/testsuite/gdb.multi/tids.exp
index b72695d..28b15a3 100644
--- a/gdb/testsuite/gdb.multi/tids.exp
+++ b/gdb/testsuite/gdb.multi/tids.exp
@@ -90,7 +90,7 @@ proc thr_apply_info_thr_error {tid_list exp_error} {
gdb_test "info threads $tid_list" \
$exp_error

- gdb_test "thread apply $tid_list p 1234" \
+ gdb_test "thread apply $tid_list" \
$exp_error \
"thread apply $tid_list"
}
@@ -98,7 +98,10 @@ proc thr_apply_info_thr_error {tid_list exp_error} {
# Issue both "info threads TID_LIST" and "thread apply TID_LIST" and
# expect the command to error out with "Invalid thread ID: $EXPECTED".
# EXPECTED is a literal string, not a regexp.
-proc thr_apply_info_thr_invalid {tid_list expected} {
+proc thr_apply_info_thr_invalid {tid_list {expected ""}} {
+ if {$expected == ""} {
+ set expected $tid_list
+ }
set expected [string_to_regexp $expected]
gdb_test "info threads $tid_list" \
"Invalid thread ID: $expected"
@@ -183,6 +186,12 @@ with_test_prefix "two inferiors" {
gdb_continue_to_breakpoint "twice"
}

+ thr_apply_info_thr "1" \
+ "1.1"
+
+ thr_apply_info_thr "1.1" \
+ "1.1"
+
thr_apply_info_thr "1 2 3" \
"1.1 1.2 1.3"

@@ -214,6 +223,57 @@ with_test_prefix "two inferiors" {
thr_apply_info_thr "1.1-2 2.2-3" \
"1.1 1.2 2.2 2.3"

+ # Now test using GDB convenience variables.
+
+ gdb_test "p \$inf = 1" " = 1"
+ gdb_test "p \$thr_start = 2" " = 2"
+ gdb_test "p \$thr_end = 3" " = 3"
+
+ # Convenience variable for the inferior number, only.
+ thr_apply_info_thr "\$inf.2" \
+ "1.2"
+ thr_apply_info_thr "\$inf.2-3" \
+ "1.2 1.3"
+
+ # Convenience variables for thread numbers as well.
+ foreach prefix {"" "1." "\$inf."} {
+ thr_apply_info_thr "${prefix}\$thr_start" \
+ "1.2"
+ thr_apply_info_thr "${prefix}\$thr_start-\$thr_end" \
+ "1.2 1.3"
+ thr_apply_info_thr "${prefix}2-\$thr_end" \
+ "1.2 1.3"
+ thr_apply_info_thr "${prefix}\$thr_start-3" \
+ "1.2 1.3"
+
+ # Undefined convenience variable.
+ set prefix_re [string_to_regexp $prefix]
+ thr_apply_info_thr_error "${prefix}\$conv123" \
+ [multi_line \
+ "Convenience variable must have integer value\." \
+ "Invalid thread ID: ${prefix_re}\\\$conv123"]
+ }
+
+ # Convenience variables pointing at inexisting thread and/or
+ # inferior.
+ gdb_test "p \$inf = 30" " = 30"
+ gdb_test "p \$thr = 20" " = 20"
+ # Try both the convenience variable and the literal number.
+ foreach thr {"\$thr" "20" "1.20" "\$inf.1" "30.1" } {
+ set expected [string_to_regexp $thr]
+ gdb_test "info threads $thr" "No threads match '${expected}'."
+ # "info threads" works like a filter. If there's any other
+ # valid thread in the list, there's no error.
+ info_threads "$thr 1.1" "1.1"
+ info_threads "1.1 $thr" "1.1"
+ }
+
+ gdb_test "thread apply \$thr p 1234" \
+ "warning: Unknown thread 1.20"
+
+ gdb_test "thread apply \$inf.1 p 1234" \
+ "warning: Unknown thread 30.1"
+
# Now test a set of invalid thread IDs/ranges.

thr_apply_info_thr_invalid "1." \
@@ -234,17 +294,40 @@ with_test_prefix "two inferiors" {
thr_apply_info_thr_invalid "1-2.1" \
"1-2.1"

- thr_apply_info_thr_error "1-0" "inverted range"
- thr_apply_info_thr_error "1.1-0" "inverted range"
-
- thr_apply_info_thr_error "1-" "inverted range"
- thr_apply_info_thr_error "1.1-" "inverted range"
-
- thr_apply_info_thr_error "2-1" "inverted range"
- thr_apply_info_thr_error "1.2-1" "inverted range"
+ gdb_test "p \$zero = 0" " = 0"
+ gdb_test "p \$one = 1" " = 1"
+ gdb_test "p \$minus_one = -11" " = -11"
+ foreach prefix {"" "1." "$one."} {
+ set prefix_re [string_to_regexp $prefix]
+
+ thr_apply_info_thr_invalid "${prefix}foo"
+ thr_apply_info_thr_invalid "${prefix}1foo"
+ thr_apply_info_thr_invalid "${prefix}foo1"
+
+ thr_apply_info_thr_error "${prefix}1-0" "inverted range"
+ thr_apply_info_thr_error "${prefix}1-\$zero" "inverted range"
+ thr_apply_info_thr_error "${prefix}\$one-0" "inverted range"
+ thr_apply_info_thr_error "${prefix}\$one-\$zero" "inverted range"
+ thr_apply_info_thr_error "${prefix}1-" "inverted range"
+ thr_apply_info_thr_error "${prefix}2-1" "inverted range"
+ thr_apply_info_thr_error "${prefix}2-\$one" "inverted range"
+ thr_apply_info_thr_error "${prefix}-1" "negative value"
+ thr_apply_info_thr_error "${prefix}-\$one" "negative value"
+ thr_apply_info_thr_error "${prefix}\$minus_one" \
+ "negative value: ${prefix_re}\\\$minus_one"
+ }

- thr_apply_info_thr_error "-1" "negative value"
- thr_apply_info_thr_error "1.-1" "negative value"
+ # Check that a valid thread ID list with a missing command errors
+ # out.
+ with_test_prefix "missing command" {
+ set output "Please specify a command following the thread ID list"
+ gdb_test "thread apply 1" $output
+ gdb_test "thread apply 1.1" $output
+ gdb_test "thread apply 1.1 1.2" $output
+ gdb_test "thread apply 1-2" $output
+ gdb_test "thread apply 1.1-2" $output
+ gdb_test "thread apply $thr" $output
+ }

# Check that we do parse the inferior number and don't confuse it.
gdb_test "info threads 3.1" \
diff --git a/gdb/thread.c b/gdb/thread.c
index cdd2a2f..01ce88c 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1818,19 +1818,29 @@ thread_apply_all_command (char *cmd, int from_tty)
static void
thread_apply_command (char *tidlist, int from_tty)
{
- char *cmd;
+ char *cmd = NULL;
struct cleanup *old_chain;
char *saved_cmd;
struct tid_range_parser parser;

- if (tidlist == NULL || *tidlist == '\000')
- error (_("Please specify a thread ID list"));
+ tid_range_parser_init (&parser, tidlist, current_inferior ()->num);
+ while (!tid_range_parser_finished (&parser))
+ {
+ int inf_num, thr_start, thr_end;

- for (cmd = tidlist; *cmd != '\000' && !isalpha (*cmd); cmd++);
+ if (!tid_range_parser_get_tid_range (&parser, &inf_num, &thr_start, &thr_end))
+ {
+ cmd = (char *) tid_range_parser_string (&parser);
+ break;
+ }
+ }

- if (*cmd == '\000')
+ if (cmd == NULL)
error (_("Please specify a command following the thread ID list"));

+ if (tidlist == cmd || !isalpha (cmd[0]))
+ invalid_thread_id_error (cmd);
+
/* Save a copy of the command in case it is clobbered by
execute_command. */
saved_cmd = xstrdup (cmd);
diff --git a/gdb/tid-parse.c b/gdb/tid-parse.c
index 21b872d..45b7ff5 100644
--- a/gdb/tid-parse.c
+++ b/gdb/tid-parse.c
@@ -31,6 +31,23 @@ invalid_thread_id_error (const char *string)
error (_("Invalid thread ID: %s"), string);
}

+/* Wrapper for get_number_trailer that throws an error if we get back
+ a negative number. We'll see a negative value if the number is
+ stored in a negative convenience variable (e.g., $minus_one = -1).
+ STRING is the parser string to be used in the error message if we
+ do get back a negative number. */
+
+static int
+get_positive_number_trailer (const char **pp, int trailer, const char *string)
+{
+ int num;
+
+ num = get_number_trailer (pp, trailer);
+ if (num < 0)
+ error (_("negative value: %s"), string);
+ return num;
+}
+
/* See tid-parse.h. */

struct thread_info *
@@ -51,7 +68,7 @@ parse_thread_id (const char *tidstr, const char **end)
int inf_num;

p1 = number;
- inf_num = get_number_trailer (&p1, '.');
+ inf_num = get_positive_number_trailer (&p1, '.', number);
if (inf_num == 0)
invalid_thread_id_error (number);

@@ -69,7 +86,7 @@ parse_thread_id (const char *tidstr, const char **end)
p1 = number;
}

- thr_num = get_number_const (&p1);
+ thr_num = get_positive_number_trailer (&p1, 0, number);
if (thr_num == 0)
invalid_thread_id_error (number);

@@ -183,15 +200,16 @@ get_tid_or_range (struct tid_range_parser *parser, int *inf_num,

/* Parse number to the left of the dot. */
p = parser->string;
- parser->inf_num = get_number_trailer (&p, '.');
+ parser->inf_num
+ = get_positive_number_trailer (&p, '.', parser->string);
if (parser->inf_num == 0)
- invalid_thread_id_error (parser->string);
+ return 0;

parser->qualified = 1;
p = dot + 1;

if (isspace (*p))
- invalid_thread_id_error (parser->string);
+ return 0;
}
else
{
@@ -206,8 +224,13 @@ get_tid_or_range (struct tid_range_parser *parser, int *inf_num,

*inf_num = parser->inf_num;
*thr_start = get_number_or_range (&parser->range_parser);
+ if (*thr_start < 0)
+ error (_("negative value: %s"), parser->string);
if (*thr_start == 0)
- invalid_thread_id_error (parser->string);
+ {
+ parser->state = TID_RANGE_STATE_INFERIOR;
+ return 0;
+ }

/* If we successfully parsed a thread number or finished parsing a
thread range, switch back to assuming the next TID is
--
1.9.3