public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/2] Fix "thread apply $conv_var" and misc other related problems
  2016-01-14 11:39 [PATCH 0/2] Star wildcard ranges (e.g., "info thread 2.*") Pedro Alves
  2016-01-14 11:39 ` [PATCH 2/2] " Pedro Alves
@ 2016-01-14 11:39 ` Pedro Alves
  1 sibling, 0 replies; 4+ messages in thread
From: Pedro Alves @ 2016-01-14 11:39 UTC (permalink / raw)
  To: gdb-patches

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-14  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-14  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                     |  19 +++++--
 gdb/tid-parse.c                  |  35 ++++++++++---
 3 files changed, 140 insertions(+), 21 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..f5d9cb0 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1818,7 +1818,7 @@ 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;
@@ -1826,11 +1826,24 @@ thread_apply_command (char *tidlist, int from_tty)
   if (tidlist == NULL || *tidlist == '\000')
     error (_("Please specify a thread ID list"));
 
-  for (cmd = tidlist; *cmd != '\000' && !isalpha (*cmd); cmd++);
+  tid_range_parser_init (&parser, tidlist, current_inferior ()->num);
+  while (!tid_range_parser_finished (&parser))
+    {
+      int inf_num, thr_start, thr_end;
+
+      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

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/2] Star wildcard ranges (e.g., "info thread 2.*")
  2016-01-14 11:39 [PATCH 0/2] Star wildcard ranges (e.g., "info thread 2.*") Pedro Alves
@ 2016-01-14 11:39 ` Pedro Alves
  2016-01-14 11:39 ` [PATCH 1/2] Fix "thread apply $conv_var" and misc other related problems Pedro Alves
  1 sibling, 0 replies; 4+ messages in thread
From: Pedro Alves @ 2016-01-14 11:39 UTC (permalink / raw)
  To: gdb-patches

Add support for specifying "all threads of inferior N", by writing "*"
as thread number/range in thread ID lists.

E.g., "info threads 2.*" or "thread apply 2.* bt".

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

	* NEWS: Mention star wildcard ranges.
	* cli/cli-utils.c (get_number_or_range): Check state->in_range first.
	(number_range_setup_range): New function.
	* cli/cli-utils.h (number_range_setup_range): New declaration.
	* thread.c (thread_apply_command): Support star TID ranges.
	* tid-parse.c (tid_range_parser_finished)
	(tid_range_parser_string, tid_range_parser_skip)
	(get_tid_or_range, get_tid_or_range): Handle
	TID_RANGE_STATE_STAR_RANGE.
	(tid_range_parser_star_range): New function.
	* tid-parse.h (enum tid_range_state) <TID_RANGE_STATE_STAR_RANGE>:
	New value.
	(tid_range_parser_star_range): New declaration.

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

	* gdb.texinfo (Threads) <thread ID lists>: Document star ranges.

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

	* gdb.multi/tids.exp: Test star ranges.
---
 gdb/NEWS                         |  6 +++++
 gdb/cli/cli-utils.c              | 47 +++++++++++++++++++++++++---------------
 gdb/cli/cli-utils.h              |  8 +++++++
 gdb/doc/gdb.texinfo              | 39 ++++++++++++++++++++++++---------
 gdb/testsuite/gdb.multi/tids.exp | 36 ++++++++++++++++++++++++++++++
 gdb/thread.c                     | 20 +++++++++++++++++
 gdb/tid-parse.c                  | 28 +++++++++++++++++++++---
 gdb/tid-parse.h                  |  7 ++++++
 8 files changed, 161 insertions(+), 30 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index d9cbb80..be3536c 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -34,6 +34,12 @@
      [Switching to thread 2.1 (Thread 0x7ffff7fc2740 (LWP 8157))] (running)
      (gdb)
 
+* In commands that accept a thread ID list, you can now refer to all
+  threads of an inferior using a star wildcard.  GDB accepts
+  "INF_NUM.*", to refer to all threads of inferior INF_NUM, and "*" to
+  refer to all threads of the current inferior.  For example, "info
+  threads 2.*".
+
 * You can use "info threads -gid" to display the global thread ID of
   all threads.
 
diff --git a/gdb/cli/cli-utils.c b/gdb/cli/cli-utils.c
index a68b67d..0946db0 100644
--- a/gdb/cli/cli-utils.c
+++ b/gdb/cli/cli-utils.c
@@ -134,7 +134,21 @@ init_number_or_range (struct get_number_or_range_state *state,
 int
 get_number_or_range (struct get_number_or_range_state *state)
 {
-  if (*state->string != '-')
+  if (state->in_range)
+    {
+      /* All number-parsing has already been done.  Return the next
+	 integer value (one greater than the saved previous value).
+	 Do not advance the token pointer until the end of range is
+	 reached.  */
+
+      if (++state->last_retval == state->end_value)
+	{
+	  /* End of range reached; advance token pointer.  */
+	  state->string = state->end_ptr;
+	  state->in_range = 0;
+	}
+    }
+  else if (*state->string != '-')
     {
       /* Default case: state->string is pointing either to a solo
 	 number, or to the first number of a range.  */
@@ -165,27 +179,26 @@ get_number_or_range (struct get_number_or_range_state *state)
 	    state->in_range = 1;
 	}
     }
-  else if (! state->in_range)
-    error (_("negative value"));
   else
-    {
-      /* state->string points to the '-' that betokens a range.  All
-	 number-parsing has already been done.  Return the next
-	 integer value (one greater than the saved previous value).
-	 Do not advance the token pointer until the end of range
-	 is reached.  */
-
-      if (++state->last_retval == state->end_value)
-	{
-	  /* End of range reached; advance token pointer.  */
-	  state->string = state->end_ptr;
-	  state->in_range = 0;
-	}
-    }
+    error (_("negative value"));
   state->finished = *state->string == '\0';
   return state->last_retval;
 }
 
+/* See documentation in cli-utils.h.  */
+
+void
+number_range_setup_range (struct get_number_or_range_state *state,
+			  int start_value, int end_value, const char *end_ptr)
+{
+  gdb_assert (start_value > 0);
+
+  state->in_range = 1;
+  state->end_ptr = end_ptr;
+  state->last_retval = start_value - 1;
+  state->end_value = end_value;
+}
+
 /* Accept a number and a string-form list of numbers such as is 
    accepted by get_number_or_range.  Return TRUE if the number is
    in the list.
diff --git a/gdb/cli/cli-utils.h b/gdb/cli/cli-utils.h
index ebf11f2..a31fff5 100644
--- a/gdb/cli/cli-utils.h
+++ b/gdb/cli/cli-utils.h
@@ -90,6 +90,14 @@ extern void init_number_or_range (struct get_number_or_range_state *state,
 
 extern int get_number_or_range (struct get_number_or_range_state *state);
 
+/* Setups STATE such that get_number_or_range returns numbers in range
+   START_VALUE to END_VALUE.  When get_number_or_range returns
+   END_VALUE, the STATE string is advanced to END_PTR.  */
+
+extern void number_range_setup_range (struct get_number_or_range_state *state,
+				      int start_value, int end_value,
+				      const char *end_ptr);
+
 /* Accept a number and a string-form list of numbers such as is 
    accepted by get_number_or_range.  Return TRUE if the number is
    in the list.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 7da31c8..a08a196 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -2912,16 +2912,35 @@ 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}.
+argument.  A list element can be:
+
+@enumerate
+@item
+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}.
+
+@item
+A range of thread numbers, again with or without an inferior
+qualifier, as in @var{inf}.@var{thr1}-@var{thr2} or
+@var{thr1}-@var{thr2}.  E.g., @samp{1.2-4} or @samp{2-4}.
+
+@item
+All threads of an inferior, specified with a star wildcard, with or
+without an inferior qualifier, as in @var{inf}.@code{*} (e.g.,
+@samp{1.*}) or @code{*}.  The former refers to all threads of the
+given inferior, and the latter form without an inferior qualifier
+refers to all threads of the current inferior.
+
+@end enumerate
+
+For example, if the current inferior is 1, and inferior 7 has one
+thread with ID 7.1, the thread list @samp{1 2-3 4.5 6.7-9 7.*}
+includes threads 1 to 3 of inferior 1, thread 5 of inferior 4, threads
+7 to 9 of inferior 6 and all threads of inferior 7.  That is, in
+expanded qualified form, the same as @samp{1.1 1.2 1.3 4.5 6.7 6.8 6.9
+7.1}.
+
 
 @anchor{global thread numbers}
 @cindex global thread number
diff --git a/gdb/testsuite/gdb.multi/tids.exp b/gdb/testsuite/gdb.multi/tids.exp
index 28b15a3..0109c10 100644
--- a/gdb/testsuite/gdb.multi/tids.exp
+++ b/gdb/testsuite/gdb.multi/tids.exp
@@ -274,6 +274,36 @@ with_test_prefix "two inferiors" {
     gdb_test "thread apply \$inf.1 p 1234" \
 	"warning: Unknown thread 30.1"
 
+    # Star ranges.
+
+    thr_apply_info_thr "1.*" \
+	"1.1 1.2 1.3"
+
+    thr_apply_info_thr "*" \
+	"1.1 1.2 1.3"
+
+    thr_apply_info_thr "1.* 2.1" \
+	"1.1 1.2 1.3 2.1"
+
+    thr_apply_info_thr "2.1 1.*" \
+	"1.1 1.2 1.3 2.1" \
+	"2.1 1.1 1.2 1.3"
+
+    thr_apply_info_thr "1.* 2.*" \
+	"1.1 1.2 1.3 2.1 2.2 2.3"
+
+    thr_apply_info_thr "2.* 1.*" \
+	"1.1 1.2 1.3 2.1 2.2 2.3" \
+	"2.1 2.2 2.3 1.1 1.2 1.3"
+
+    # There's no inferior 3, but "info threads" treats the thread list
+    # as a filter, so it's OK.  "thread apply" complains about the
+    # unknown inferior through.
+    info_threads "1.1 3.*" \
+	"1.1"
+    gdb_test "thread apply 1.1 3.* p 1" \
+	"Thread 1.1.*warning: Unknown inferior 3"
+
     # Now test a set of invalid thread IDs/ranges.
 
     thr_apply_info_thr_invalid "1." \
@@ -315,6 +345,11 @@ with_test_prefix "two inferiors" {
 	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 "${prefix}1-*" "inverted range"
+	thr_apply_info_thr_invalid "${prefix}*1"
+	thr_apply_info_thr_invalid "${prefix}*foo"
+	thr_apply_info_thr_invalid "${prefix}foo*"
     }
 
     # Check that a valid thread ID list with a missing command errors
@@ -327,6 +362,7 @@ with_test_prefix "two inferiors" {
 	gdb_test "thread apply 1-2" $output
 	gdb_test "thread apply 1.1-2" $output
 	gdb_test "thread apply $thr" $output
+	gdb_test "thread apply 1.*" $output
     }
 
     # Check that we do parse the inferior number and don't confuse it.
diff --git a/gdb/thread.c b/gdb/thread.c
index f5d9cb0..ed81e55 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -1863,6 +1863,26 @@ thread_apply_command (char *tidlist, int from_tty)
       inf = find_inferior_id (inf_num);
       if (inf != NULL)
 	tp = find_thread_id (inf, thr_num);
+
+      if (tid_range_parser_star_range (&parser))
+	{
+	  if (inf == NULL)
+	    {
+	      warning (_("Unknown inferior %d"), inf_num);
+	      tid_range_parser_skip (&parser);
+	      continue;
+	    }
+
+	  /* No use looking for threads past the highest thread number
+	     the inferior ever had.  */
+	  if (thr_num >= inf->highest_thread_num)
+	    tid_range_parser_skip (&parser);
+
+	  /* Be quiet about unknown threads numbers.  */
+	  if (tp == NULL)
+	    continue;
+	}
+
       if (tp == NULL)
 	{
 	  if (show_inferior_qualified_tids ()
diff --git a/gdb/tid-parse.c b/gdb/tid-parse.c
index 45b7ff5..68133c6 100644
--- a/gdb/tid-parse.c
+++ b/gdb/tid-parse.c
@@ -134,6 +134,7 @@ tid_range_parser_finished (struct tid_range_parser *parser)
     case TID_RANGE_STATE_INFERIOR:
       return *parser->string == '\0';
     case TID_RANGE_STATE_THREAD_RANGE:
+    case TID_RANGE_STATE_STAR_RANGE:
       return parser->range_parser.finished;
     }
 
@@ -150,6 +151,7 @@ tid_range_parser_string (struct tid_range_parser *parser)
     case TID_RANGE_STATE_INFERIOR:
       return parser->string;
     case TID_RANGE_STATE_THREAD_RANGE:
+    case TID_RANGE_STATE_STAR_RANGE:
       return parser->range_parser.string;
     }
 
@@ -161,7 +163,8 @@ tid_range_parser_string (struct tid_range_parser *parser)
 void
 tid_range_parser_skip (struct tid_range_parser *parser)
 {
-  gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE)
+  gdb_assert ((parser->state == TID_RANGE_STATE_THREAD_RANGE
+	       || parser->state == TID_RANGE_STATE_STAR_RANGE)
 	      && parser->range_parser.in_range);
 
   tid_range_parser_init (parser, parser->range_parser.end_ptr,
@@ -219,7 +222,16 @@ get_tid_or_range (struct tid_range_parser *parser, int *inf_num,
 	}
 
       init_number_or_range (&parser->range_parser, p);
-      parser->state = TID_RANGE_STATE_THREAD_RANGE;
+      if (p[0] == '*' && (p[1] == '\0' || isspace (p[1])))
+	{
+	  /* Setup the number range parser to return numbers in the
+	     whole [1,INT_MAX] range.  */
+	  number_range_setup_range (&parser->range_parser, 1, INT_MAX,
+				    skip_spaces_const (p + 1));
+	  parser->state = TID_RANGE_STATE_STAR_RANGE;
+	}
+      else
+	parser->state = TID_RANGE_STATE_THREAD_RANGE;
     }
 
   *inf_num = parser->inf_num;
@@ -247,7 +259,9 @@ get_tid_or_range (struct tid_range_parser *parser, int *inf_num,
 
   /* If we're midway through a range, and the caller wants the end
      value, return it and skip to the end of the range.  */
-  if (thr_end != NULL && parser->state == TID_RANGE_STATE_THREAD_RANGE)
+  if (thr_end != NULL
+      && (parser->state == TID_RANGE_STATE_THREAD_RANGE
+	  || parser->state == TID_RANGE_STATE_STAR_RANGE))
     {
       *thr_end = parser->range_parser.end_value;
       tid_range_parser_skip (parser);
@@ -281,6 +295,14 @@ tid_range_parser_get_tid (struct tid_range_parser *parser,
 /* See tid-parse.h.  */
 
 int
+tid_range_parser_star_range (struct tid_range_parser *parser)
+{
+  return parser->state == TID_RANGE_STATE_STAR_RANGE;
+}
+
+/* See gdbthread.h.  */
+
+int
 tid_is_in_list (const char *list, int default_inferior,
 		int inf_num, int thr_num)
 {
diff --git a/gdb/tid-parse.h b/gdb/tid-parse.h
index a690edf..830cf36 100644
--- a/gdb/tid-parse.h
+++ b/gdb/tid-parse.h
@@ -44,6 +44,9 @@ enum tid_range_state
 
   /* Parsing the thread number or thread number range.  */
   TID_RANGE_STATE_THREAD_RANGE,
+
+  /* Parsing a star wildcard thread range.  E.g., "1.*".  */
+  TID_RANGE_STATE_STAR_RANGE,
 };
 
 /* An object of this type is passed to tid_range_parser_get_tid.  It
@@ -142,6 +145,10 @@ extern int tid_range_parser_get_tid_range (struct tid_range_parser *parser,
 					   int *inf_num,
 					   int *thr_start, int *thr_end);
 
+/* Returns non-zero if processing a star wildcard (e.g., "1.*")
+   range.  */
+extern int tid_range_parser_star_range (struct tid_range_parser *parser);
+
 /* Returns non-zero if parsing has completed.  */
 extern int tid_range_parser_finished (struct tid_range_parser *parser);
 
-- 
1.9.3

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 0/2] Star wildcard ranges (e.g., "info thread 2.*")
@ 2016-01-14 11:39 Pedro Alves
  2016-01-14 11:39 ` [PATCH 2/2] " Pedro Alves
  2016-01-14 11:39 ` [PATCH 1/2] Fix "thread apply $conv_var" and misc other related problems Pedro Alves
  0 siblings, 2 replies; 4+ messages in thread
From: Pedro Alves @ 2016-01-14 11:39 UTC (permalink / raw)
  To: gdb-patches

In v2:
	- Address Eli's documentation review.
	- Fix "thread apply" crash in patch #1.
	- remove 'state->string = "-"' hack in patch #2.

This series adds support for specifying "all threads of inferior N",
by writing "*" as thread number/range in thread ID lists.

E.g., "info threads 2.*" or "thread apply 2.* bt".

While doing this, I noticed a few bugs in "thread apply" (that even
predate the per-inferior thread IDs series).  Patch 1 fixes them.  I'm
only keeping both patches together to make it clear that patch 2
applies on top of patch 1.

Pedro Alves (2):
  Fix "thread apply $conv_var" and misc other related problems
  Star wildcard ranges (e.g., "info thread 2.*")

 gdb/NEWS                         |   6 ++
 gdb/cli/cli-utils.c              |  47 ++++++++-----
 gdb/cli/cli-utils.h              |   8 +++
 gdb/doc/gdb.texinfo              |  39 ++++++++---
 gdb/testsuite/gdb.multi/tids.exp | 143 +++++++++++++++++++++++++++++++++++----
 gdb/thread.c                     |  39 ++++++++++-
 gdb/tid-parse.c                  |  63 ++++++++++++++---
 gdb/tid-parse.h                  |   7 ++
 8 files changed, 301 insertions(+), 51 deletions(-)

-- 
1.9.3

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/2] Fix "thread apply $conv_var" and misc other related problems
  2016-01-13 16:34 [PATCH 0/2] Star wildcard ranges (e.g., "info thread 2.*") Pedro Alves
@ 2016-01-13 16:34 ` Pedro Alves
  0 siblings, 0 replies; 4+ messages in thread
From: Pedro Alves @ 2016-01-13 16:34 UTC (permalink / raw)
  To: gdb-patches

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

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2016-01-14 11:39 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-14 11:39 [PATCH 0/2] Star wildcard ranges (e.g., "info thread 2.*") Pedro Alves
2016-01-14 11:39 ` [PATCH 2/2] " Pedro Alves
2016-01-14 11:39 ` [PATCH 1/2] Fix "thread apply $conv_var" and misc other related problems Pedro Alves
  -- strict thread matches above, loose matches on Subject: below --
2016-01-13 16:34 [PATCH 0/2] Star wildcard ranges (e.g., "info thread 2.*") Pedro Alves
2016-01-13 16:34 ` [PATCH 1/2] Fix "thread apply $conv_var" and misc other related problems Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).