Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [RFC/WIP PATCH v2 01/12] Flip to set target-async on by default
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
@ 2011-12-17  2:59 ` Pedro Alves
  2011-12-23 18:04   ` Marc Khouzam
  2011-12-17  2:59 ` [RFC/WIP PATCH v2 03/12] manythreads.exp: Adjust to handle threads appearing/disappearing after "Program received signal SIGFOO" Pedro Alves
                   ` (12 subsequent siblings)
  13 siblings, 1 reply; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  2:59 UTC (permalink / raw)
  To: gdb-patches

This flips "set target-async" to default to on.  Marc Khouzam's
earlier reply (on the v1 series) indicating Eclipse does not use
target-async for all-stop makes me think it may be prudent to actually
try this with Eclipse and see what happens.  Haven't done that though.
---
 gdb/target.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/gdb/target.c b/gdb/target.c
index 5700066..a6e146d 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -4259,11 +4259,11 @@ maintenance_print_target_stack (char *cmd, int from_tty)
 }
 
 /* Controls if async mode is permitted.  */
-int target_async_permitted = 0;
+int target_async_permitted = 1;
 
 /* The set command writes to this variable.  If the inferior is
    executing, linux_nat_async_permitted is *not* updated.  */
-static int target_async_permitted_1 = 0;
+static int target_async_permitted_1 = 1;
 
 static void
 set_maintenance_target_async_permitted (char *args, int from_tty,


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

* [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets
@ 2011-12-17  2:59 Pedro Alves
  2011-12-17  2:59 ` [RFC/WIP PATCH v2 01/12] Flip to set target-async on by default Pedro Alves
                   ` (13 more replies)
  0 siblings, 14 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  2:59 UTC (permalink / raw)
  To: gdb-patches

Here's v2 of the itsets series.  Several preparatory patches have been
checked in meanwhile, which along with a bunch of changes and fixing
to v1 brought the series close to being regression free.  There's what
seems like one last nasty bug somewhere in execution control
(somewhere with displaced stepping, I suspect) that makes us mishandle
decr_pc_after_break, leading to spurious SIGTRAPs with PCs pointing
one-off of breakpoints.

The major visible change in this version is that I'm no longer
following HPC ptset specs so closely, but instead, I've implemented
the idea of requiring a prefix letter (i, t, c, etc.).  More info in
the individual patches.


The name of this new concept is still up for grabs.  Let me repeat a
bit of the discussion on the v1 series on the topic here:

  Tromey wrote:
  > Pedro> IPTC sets are a collection of arbitrarily many processes,
  > Pedro> threads, and cores.
  > 
  > I wish we had a better name.
  > 
  > The current name seems to gain a letter with every new thread on the
  > topic :-)

  That was kind of on purpose.  :-)

  The best name I could think of is "scope".  If we call these things
  scopes, we can also rename the itfocus command to "scope", which can
  be abbreviated as "sc".  ("s" is already step, obviously).  It's spelled
  itfocus currently because "focus" was already taken by the TUI.

  It looks like this:

  curinf> scope t3 break -stop i1 main
  Breakpoint 2 at 0x40068c: file threads.c, line 35.
  curinf> scope i1
  Current inferior is 1.
  i1> sc all
  Current inferior is 1.
  all>
  
  WDYT?


v1 at: http://sourceware.org/ml/gdb-patches/2011-11/msg00760.html

This series is also available at http://github.com/palves/gdb/tree/itsets-v2
for convenience.

This is still a WIP.  Expect no docs, tests, and to find uncommented
code, and, obvious hacks in need of cleanup here and there.

-- 
Pedro Alves


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

* [RFC/WIP PATCH v2 03/12] manythreads.exp: Adjust to handle threads appearing/disappearing after "Program received signal SIGFOO"
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
  2011-12-17  2:59 ` [RFC/WIP PATCH v2 01/12] Flip to set target-async on by default Pedro Alves
@ 2011-12-17  2:59 ` Pedro Alves
  2011-12-17  3:00 ` [RFC/WIP PATCH v2 04/12] ia64-sigill.exp: Don't assume there's no infrun output after the prompt Pedro Alves
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  2:59 UTC (permalink / raw)
  To: gdb-patches

As infrun only decides to stop or not all threads after handling the
event the target reported, threads can now appear or disappear after
"Program received signal SIGFOO" and presenting the prompt, which
breaks the manythreads.exp test.  This fixes it.

v2:

 - "stop threads 1" test needed adjustment too.


gdb/testsuite/
2011-12-16  Pedro Alves  <pedro@codesourcery.com>

	* gdb.threads/manythreads.exp: Don't assume threads can't appear
	or disappear after printing "Program received signal ".
---
 gdb/testsuite/gdb.threads/manythreads.exp |   68 +++++++++++++++++++++++------
 1 files changed, 54 insertions(+), 14 deletions(-)

diff --git a/gdb/testsuite/gdb.threads/manythreads.exp b/gdb/testsuite/gdb.threads/manythreads.exp
index 12a25b9..a027309 100644
--- a/gdb/testsuite/gdb.threads/manythreads.exp
+++ b/gdb/testsuite/gdb.threads/manythreads.exp
@@ -54,6 +54,22 @@ gdb_test_multiple "continue" "first continue" {
   }
 }
 
+
+# Note that in the tests below, we can't suffix the "Program received
+# signal SIGINT" regexes with $gdb_prompt, as doing so would mean that
+# e.g. with,
+#
+# Program received signal SIGINT, Interrupt.
+# [New Thread FOO]
+# [Switching to Thread BAR]
+# 0xdeadbeef in bar () from bar.so
+# (gdb)
+#
+# the [New ...] or [... exited] regexes would eat the "Program
+# received ..." bit.  [New FOO] and [FOO exited] may appear while GDB
+# is stopping threads.
+
+
 # Wait one second.  This is better than the TCL "after" command, because
 # we don't lose GDB's output while we do it.
 remote_expect host 1 { timeout { } }
@@ -62,6 +78,17 @@ remote_expect host 1 { timeout { } }
 send_gdb "\003"
 set message "stop threads 1"
 gdb_test_multiple "" "stop threads 1" {
+  -re "Program received signal SIGINT" {
+    # Eat the prompt.
+    gdb_expect {
+      -re "$gdb_prompt $" {
+	pass "$message"
+      }
+      timeout {
+	fail "$message (timeout)"
+      }
+    }
+  }
   -re "\\\[New \[^\]\]*\\\]\r\n" {
     exp_continue
   }
@@ -71,9 +98,6 @@ gdb_test_multiple "" "stop threads 1" {
   -re "Thread \[^\n\]* executing\r\n" {
     exp_continue
   }
-  -re "Program received signal SIGINT.*$gdb_prompt $" {
-    pass "$message"
-  }
   timeout {
     fail "$message (timeout)"
   }
@@ -116,6 +140,21 @@ gdb_test_multiple "continue" "second continue" {
 # to handle duplicate SIGINTs sent to multiple threads.
 set failed 0
 remote_expect host 1 {
+  -re "Program received signal SIGINT" {
+
+    # Eat the prompt.
+    gdb_expect {
+      -re "$gdb_prompt $" {
+      }
+    }
+
+    if { $failed == 0 } {
+      fail "check for duplicate SIGINT"
+    }
+    send_gdb "continue\n"
+    set failed 1
+    exp_continue
+  }
   -re "\\\[New \[^\]\]*\\\]\r\n" {
     exp_continue -continue_timer
   }
@@ -125,14 +164,6 @@ remote_expect host 1 {
   -re "Thread \[^\n\]* executing\r\n" {
     exp_continue -continue_timer
   }
-  -re "Program received signal SIGINT.*$gdb_prompt $" {
-    if { $failed == 0 } {
-      fail "check for duplicate SIGINT"
-    }
-    send_gdb "continue\n"
-    set failed 1
-    exp_continue
-  }
   timeout {
     if { $failed == 0 } {
       pass "check for duplicate SIGINT"
@@ -144,6 +175,18 @@ remote_expect host 1 {
 send_gdb "\003"
 set message "stop threads 2"
 gdb_test_multiple "" "stop threads 2" {
+  -re "Program received signal SIGINT" {
+
+    # Eat the prompt.
+    gdb_expect {
+      -re "$gdb_prompt $" {
+	pass "$message"
+      }
+      timeout {
+	fail "$message (timeout)"
+      }
+    }
+  }
   -re "\\\[New \[^\]\]*\\\]\r\n" {
     exp_continue
   }
@@ -153,9 +196,6 @@ gdb_test_multiple "" "stop threads 2" {
   -re "Thread \[^\n\]* executing\r\n" {
     exp_continue
   }
-  -re "Program received signal SIGINT.*$gdb_prompt $" {
-    pass "$message"
-  }
   timeout {
     fail "$message (timeout)"
   }


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

* [RFC/WIP PATCH v2 11/12] Default the current itset to the current inferior set (curset).
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (2 preceding siblings ...)
  2011-12-17  3:00 ` [RFC/WIP PATCH v2 04/12] ia64-sigill.exp: Don't assume there's no infrun output after the prompt Pedro Alves
@ 2011-12-17  3:00 ` Pedro Alves
  2011-12-17  3:00 ` [RFC/WIP PATCH v2 07/12] Expand %ITSET% and %THREAD% in the prompt Pedro Alves
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  3:00 UTC (permalink / raw)
  To: gdb-patches

This goes hand-in-hand with the next patch.  So far, we default to
"all".  And, the "set schedule-multiple" is "off" by default.  This
means, that in some cases that "all" isn't really "all", but the
inferior of the selected thread only.  This is confusing.

If by default we only focus on the current inferior, then we can flip
the schedule-multiple setting to on, meaning, resume all inferiors in
the focus, and so the schedule-multiple setting isn't really required
anymore.
---
 gdb/itset.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/gdb/itset.c b/gdb/itset.c
index dfa2c5f..d65cc37 100644
--- a/gdb/itset.c
+++ b/gdb/itset.c
@@ -2513,7 +2513,7 @@ _initialize_itset (void)
   make_internal_itset (stopped_itset, "stopped");
   make_internal_itset (curinf_itset, "curinf");
 
-  current_itset = itset_reference (all_itset);
+  current_itset = itset_reference (curinf_itset);
 
   add_com ("itfocus", no_class, itfocus_command, _("\
 Change the set of current inferiors/threads."));


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

* [RFC/WIP PATCH v2 04/12] ia64-sigill.exp: Don't assume there's no infrun output after the prompt
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
  2011-12-17  2:59 ` [RFC/WIP PATCH v2 01/12] Flip to set target-async on by default Pedro Alves
  2011-12-17  2:59 ` [RFC/WIP PATCH v2 03/12] manythreads.exp: Adjust to handle threads appearing/disappearing after "Program received signal SIGFOO" Pedro Alves
@ 2011-12-17  3:00 ` Pedro Alves
  2011-12-17  3:00 ` [RFC/WIP PATCH v2 11/12] Default the current itset to the current inferior set (curset) Pedro Alves
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  3:00 UTC (permalink / raw)
  To: gdb-patches

In async mode, we can still see TARGET_WAITKIND_IGNORE and
TARGET_WAITKIND_NORESUMED events after displaying the prompt, breaking
this test with timeouts, due to the anchor in "$gdb_prompt $"

This fixes it.

v2:

 - new in v2.

gdb/testsuite/
2011-12-16  Pedro Alves  <pedro@codesourcery.com>

	* gdb.threads/ia64-sigill.exp: Don't assume there's no infrun
	output after the prompt.
---
 gdb/testsuite/gdb.threads/ia64-sigill.exp |   26 ++++++++++++++++++++------
 1 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/gdb/testsuite/gdb.threads/ia64-sigill.exp b/gdb/testsuite/gdb.threads/ia64-sigill.exp
index 9867041..c79cb12 100644
--- a/gdb/testsuite/gdb.threads/ia64-sigill.exp
+++ b/gdb/testsuite/gdb.threads/ia64-sigill.exp
@@ -55,21 +55,35 @@ gdb_test_no_output {set $sigill_bpnum=$bpnum}
 
 gdb_breakpoint [gdb_get_line_number "break-at-exit"]
 
+# The ia64 SIGILL signal is only visible in the infrun debug output.
+# Note we can't assume there's no infrun output after the prompt from
+# here on.
 gdb_test_no_output "set debug infrun 1"
 
-# The ia64 SIGILL signal is visible only in the lin-lwp debug.
-
-gdb_test "continue" "Breakpoint \[0-9\]+,( .* in)? thread_func .*"
+set test "continue to thread_func"
+gdb_test_multiple "continue" $test {
+    -re "Breakpoint \[0-9\]+,( .* in)? thread_func .*$gdb_prompt " {
+	pass $test
+    }
+}
 
-gdb_test_no_output {delete $sigill_bpnum}
+set test {delete $sigill_bpnum}
+gdb_test_multiple "continue" $test {
+    -re "$gdb_prompt " {
+	pass $test
+    }
+}
 
 set test "continue for the pending signal"
 gdb_test_multiple "continue" $test {
-    -re "Breakpoint \[0-9\]+, .*break-at-exit.*\r\n$gdb_prompt $" {
+    -re "Breakpoint \[0-9\]+, .*break-at-exit.*\r\n$gdb_prompt " {
 	# Breakpoint has been skipped in the other thread.
 	pass $test
     }
-    -re "Program received signal .*\r\n$gdb_prompt $" {
+    -re "Program received signal .*\r\n$gdb_prompt " {
+	fail $test
+    }
+    -re "$gdb_prompt " {
 	fail $test
     }
 }


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

* [RFC/WIP PATCH v2 07/12] Expand %ITSET% and %THREAD% in the prompt
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (3 preceding siblings ...)
  2011-12-17  3:00 ` [RFC/WIP PATCH v2 11/12] Default the current itset to the current inferior set (curset) Pedro Alves
@ 2011-12-17  3:00 ` Pedro Alves
  2011-12-17  3:05 ` [RFC/WIP PATCH v2 08/12] I/T set support for breakpoints - trigger set, and stop set Pedro Alves
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  3:00 UTC (permalink / raw)
  To: gdb-patches

This adds a simple prompt hook that expands "%ITSET%" to the current
set, and "%THREAD%" to the current thread.  It was discussed in v1
that it would be better to reuse or follow the syntax of "set
extended-prompt".

Here's how I use it currently:

$ gdb -q -nx -ex "set prompt %ITSET%;%THREAD%> " ~/gdb/tests/threads
all;i1.t2> info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 31058) "threads" __lll_lock_wait_private () at ../nptl/sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:97
* 2    Thread 0x7ffff7829700 (LWP 31057) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 31053) "threads" clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:84
all;i1.t2>
all;i1.t2> itfocus i1
warning: 3 threads for inferior 1 in the current i/t set, switching to first
Current inferior is 1.
i1;i1.t3>

This was just a quick hack to make it easy to work on the rest of the
stuff without getting lost.  I'll make use of this in all examples in
this series.

v2:

 - also expand %THREAD%.
---
 gdb/event-top.c |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 54 insertions(+), 1 deletions(-)

diff --git a/gdb/event-top.c b/gdb/event-top.c
index a276690..871ab47 100644
--- a/gdb/event-top.c
+++ b/gdb/event-top.c
@@ -23,6 +23,7 @@
 #include "defs.h"
 #include "top.h"
 #include "inferior.h"
+#include "itset.h"
 #include "target.h"
 #include "terminal.h"		/* for job_control */
 #include "event-loop.h"
@@ -36,6 +37,7 @@
 #include "observer.h"
 #include "continuations.h"
 #include "gdbcmd.h"		/* for dont_repeat() */
+#include "gdb_obstack.h"
 
 /* readline include files.  */
 #include "readline/readline.h"
@@ -215,6 +217,53 @@ change_line_handler (void)
     }
 }
 
+static char *
+expand_gdb_prompt (char *prompt)
+{
+  struct obstack obstack;
+  char *p;
+
+  obstack_init (&obstack);
+
+  p = prompt;
+  while (*p)
+    {
+      if (CONST_STRNEQ (p, "%ITSET%"))
+	{
+	  if (itset_name (current_itset) != NULL)
+	    obstack_grow_str (&obstack, itset_name (current_itset));
+	  else
+	    obstack_grow_str (&obstack, itset_spec (current_itset));
+	  p += sizeof ("%ITSET%") - 1;
+	  continue;
+	}
+
+      if (CONST_STRNEQ (p, "%THREAD%"))
+	{
+	  char buf[100];
+
+	  sprintf (buf, "i%d", current_inferior ()->num);
+	  obstack_grow_str (&obstack, buf);
+
+	  if (!ptid_equal (inferior_ptid, null_ptid))
+	    {
+	      sprintf (buf, ".t%d", inferior_thread ()->num);
+	      obstack_grow_str (&obstack, buf);
+	    }
+
+	  p += sizeof ("%THREAD%") - 1;
+	  continue;
+	}
+
+      obstack_1grow (&obstack, *p);
+      p++;
+    }
+
+  obstack_1grow (&obstack, '\0');
+
+  return xstrdup (obstack_finish (&obstack));
+}
+
 /* Displays the prompt.  If the argument NEW_PROMPT is NULL, the
    prompt that is displayed is the current top level prompt.
    Otherwise, it displays whatever NEW_PROMPT is as a local/secondary
@@ -276,8 +325,12 @@ display_gdb_prompt (char *new_prompt)
 	}
       else
 	{
+	  char *top;
+
 	  /* Display the top level prompt.  */
-	  actual_gdb_prompt = top_level_prompt ();
+	  top = top_level_prompt ();
+	  actual_gdb_prompt = expand_gdb_prompt (top);
+	  xfree (top);
 	}
     }
   else


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

* [RFC/WIP PATCH v2 08/12] I/T set support for breakpoints - trigger set, and stop set
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (4 preceding siblings ...)
  2011-12-17  3:00 ` [RFC/WIP PATCH v2 07/12] Expand %ITSET% and %THREAD% in the prompt Pedro Alves
@ 2011-12-17  3:05 ` Pedro Alves
  2011-12-17  3:07 ` [RFC/WIP PATCH v2 13/12] Comment out new info breakpoints output, in order to not break the test suite Pedro Alves
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  3:05 UTC (permalink / raw)
  To: gdb-patches

This adds support for setting a breakpoint that only triggers on a
given set (a superset of the current thread specific breakpoints
support).  In addition, it adds support for specifying the set of
threads that are suspended when the breakpoint is triggered.

Breakpoints need two sets.  The trigger set, which is a generalization
of the "break foo thread N", meaning the set of inferiors/threads
where the breakpoint should fire, and, a suspend/stop set, which is
the set of inferiors/threads that should be suspended when the
breakpoint fires.

The trigger set of breakpoints is set from the current set at the time
the breakpoint is created.  The stop set is passed explicitly as
optional switch.  E.g.,:

 [itfocus TRIGGER-SET] break [-stop STOP-SET] LINESPEC

This leaves LINESPEC last, so that we can keep supporting the current
form, but avoid more hacks in linespecs like the special termination
for "thread/task/if" in the lexers.

So the old:

 (gdb) break LINESPEC

still works just the same.  The breakpoint's trigger set will be
inferred from the current set as set or temporarily overriden by
itfocus.  The stop set, if not specified, is inferred from the "set
non-stop" global option.  If non-stop is on, only the thread that
triggers the breakpoint should be suspended; if non-stop is off, then
all threads in the current focus will be suspended when the breakpoint
fires.  The thread that fired the breakpoint always remains stopped,
nomatter the stop set.

E.g.,

(gdb) info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 2296) "threads" (running)
* 2    Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 2290) "threads" (running)
(gdb) itfocus t2 break -stop t3 63
Breakpoint 4 at 0x40076d: file threads.c, line 63.

Breakpoint 4 triggers on thread 2 (equivalent to break 63 thread 2),
and when the breakpoint fires, thread 3 is suspended.  Like so:

(gdb) c -a&
Continuing.
(gdb)
Breakpoint 4, thread_function0 (arg=0x0) at threads.c:63
63              (*myp) ++;
info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 2296) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2    Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 2290) "threads" (running)

(gdb) info breakpoints
Num     Type           Disp Enb Address            What
4       breakpoint     keep y   0x000000000040076d in thread_function0 at threads.c:63
        stop only in trigger-set: t2
        suspend all in stop-set: t3
        breakpoint already hit 1 time

We can make a breakpoint that stops the world with (recall I had
non-stop on):

(gdb) del 4
(gdb) itfocus t2 break -stop all 63
Breakpoint 5 at 0x40076d: file threads.c, line 63.
(gdb) c -a&
Continuing.
(gdb)
Breakpoint 5, thread_function0 (arg=0x0) at threads.c:63
63              (*myp) ++;
info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 2296) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2    Thread 0x7ffff7829700 (LWP 2295) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 2290) "threads" 0x00007ffff7bc606d in pthread_join () from /lib/x86_64-linux-gnu/libpthread.so.0

Since all-stop was reimplemented on top of the target running in
non-stop mode, when "set non-stop" is off, not specifying a stop set
(or specifying it as empty, like -stop empty) means that all threads
stop by default, e.g., `itfocus t2 break 63', but you can still
override that explicitly by specifying "-stop SET", so the breakpoint
only forces suspension of a given itset, with:

(gdb) itfocus t2 break -stop t3 63
Breakpoint 5 at 0x40076d: file threads.c, line 63.
(gdb) c &
Continuing.
(gdb)
Breakpoint 5, thread_function0 (arg=0x0) at threads.c:63
63              (*myp) ++;
info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 2645) "threads" 0x00007ffff78d75ad in nanosleep () from /lib/x86_64-linux-gnu/libc.so.6
* 2    Thread 0x7ffff7829700 (LWP 2644) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 2610) "threads" (running)


v2:

 - Adjusted to new itset spec syntax.
 - All breakpoint-ish commands adjusted to accept "-stop", and handle
   the trigger and stop sets.
 - The stop set when multiple breakpoints are hit is the union of the
   stop sets of all the breakpoints that caused the stop.
 - Some cleanup, some bugfixes, some hacks removed.


2011-12-16  Pedro Alves  <pedro@codesourcery.com>

	* ada-lang.c: Include itset.h.
	(create_ada_exception_catchpoint): Add trigger_set and stop_set
	parameters.  Adjust.
	(catch_ada_exception_command, catch_assert_command): Parse
	`-stop', and pass down trigger and stop sets.
	* breakpoint.c: Include itset.h.
	(bpstat_check_trigger_set): New.
	(breakpoint_thread_match): Handle the trigger set.
	(bpstat_check_breakpoint_conditions): Handle the trigger set.
	(bpstat_stop_set): New.
	(print_one_breakpoint_location): Print the trigger and stop sets.
	(init_raw_breakpoint_without_location): Initialize new fields.
	(init_catchpoint, create_fork_vfork_event_catchpoint)
	(create_syscall_event_catchpoint, init_breakpoint_sal)
	(create_breakpoint_sal): Add trigger_set and stop_set parameters.
	Adjust.
	(parse_breakpoint_sals): Handle pointer to pointer to empty string
	as no arg.
	(parse_breakpoint_args): New.
	(create_breakpoint, watch_command_1, catch_fork_command_1)
	(catch_exec_command_1): Parse `-stop', and pass down trigger and
	stop sets.
	(init_ada_exception_breakpoint): Add trigger_set and stop_set
	parameters.  Adjust.
	(catch_syscall_command_1): Parse `-stop', and pass down trigger
	and stop sets.
	(base_breakpoint_dtor): Free the trigger and stop sets.
	* breakpoint.h (struct itset): Forward declare.
	(struct breakpoint) <trigger_set, stop_set>: New fields.
	(bpstat_stop_set): Declare.
	(init_ada_exception_breakpoint): Adjust.
	* infcall.c (call_function_by_hand): Set the breakpoint's trigger
	and stop sets.
	* infrun.c: Include itset.h.
	(struct execution_control_state) <stop_set>: New field.
	(ecs_destroy, ecs_destroy_cleanup): New.
	(infrun_thread_stop_requested_callback): Destroy ecs.
	(select_event_thread): Adjust.
	(wait_for_inferior, fetch_inferior_event): Destroy ecs.
	(stop_set_match): New.
	(stop_all_threads): New parameter `stop_set'.  Stop only stops in
	the specified stop set.
	(handle_inferior_event): Set the stop set from the bpstat's stop
	set.
	(stop_stepping): If there's no stop set in the ecs, infer from
	non_stop setting.  Only clear the resumed flag of threads in the
	stop set.
	(keep_going): Adjust.
---
 gdb/ada-lang.c   |   38 ++++++++
 gdb/breakpoint.c |  243 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 gdb/breakpoint.h |   15 +++
 gdb/infcall.c    |    7 ++
 gdb/infrun.c     |   96 ++++++++++++++++++---
 5 files changed, 362 insertions(+), 37 deletions(-)

diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 1befc43..c018bfe 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -63,6 +63,7 @@
 #include "mi/mi-common.h"
 #include "arch-utils.h"
 #include "exceptions.h"
+#include "itset.h"
 
 /* Define whether or not the C operator '/' truncates towards zero for
    differently signed operands (truncation direction is undefined in C).
@@ -11752,7 +11753,9 @@ ada_decode_exception_location (char *args, char **addr_string,
 /* Create an Ada exception catchpoint.  */
 
 static void
-create_ada_exception_catchpoint (struct gdbarch *gdbarch,
+create_ada_exception_catchpoint (struct itset *trigger_set,
+				 struct itset *stop_set,
+				 struct gdbarch *gdbarch,
 				 struct symtab_and_line sal,
 				 char *addr_string,
 				 char *excep_string,
@@ -11763,7 +11766,8 @@ create_ada_exception_catchpoint (struct gdbarch *gdbarch,
   struct ada_catchpoint *c;
 
   c = XNEW (struct ada_catchpoint);
-  init_ada_exception_breakpoint (&c->base, gdbarch, sal, addr_string,
+  init_ada_exception_breakpoint (&c->base, trigger_set, stop_set,
+				 gdbarch, sal, addr_string,
 				 ops, tempflag, from_tty);
   c->excep_string = excep_string;
   create_excep_cond_exprs (c);
@@ -11772,6 +11776,10 @@ create_ada_exception_catchpoint (struct gdbarch *gdbarch,
 
 /* Implement the "catch exception" command.  */
 
+void parse_breakpoint_args (char **args,
+			    struct itset **trigger_set,
+			    struct itset **stop_set);
+
 static void
 catch_ada_exception_command (char *arg, int from_tty,
 			     struct cmd_list_element *command)
@@ -11782,14 +11790,25 @@ catch_ada_exception_command (char *arg, int from_tty,
   char *addr_string = NULL;
   char *excep_string = NULL;
   const struct breakpoint_ops *ops = NULL;
+  struct itset *trigger_set;
+  struct itset *stop_set;
+  struct cleanup *old_chain;
 
   tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
 
+  parse_breakpoint_args (&arg, &trigger_set, &stop_set);
+
+  old_chain = make_cleanup_itset_free (trigger_set);
+  make_cleanup_itset_free (stop_set);
+
   if (!arg)
     arg = "";
   sal = ada_decode_exception_location (arg, &addr_string, &excep_string, &ops);
-  create_ada_exception_catchpoint (gdbarch, sal, addr_string,
+  create_ada_exception_catchpoint (trigger_set, stop_set,
+				   gdbarch, sal, addr_string,
 				   excep_string, ops, tempflag, from_tty);
+
+  discard_cleanups (old_chain);
 }
 
 static struct symtab_and_line
@@ -11820,14 +11839,25 @@ catch_assert_command (char *arg, int from_tty,
   struct symtab_and_line sal;
   char *addr_string = NULL;
   const struct breakpoint_ops *ops = NULL;
+  struct itset *trigger_set;
+  struct itset *stop_set;
+  struct cleanup *old_chain;
 
   tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
 
+  parse_breakpoint_args (&arg, &trigger_set, &stop_set);
+
+  old_chain = make_cleanup_itset_free (trigger_set);
+  make_cleanup_itset_free (stop_set);
+
   if (!arg)
     arg = "";
   sal = ada_decode_assert_location (arg, &addr_string, &ops);
-  create_ada_exception_catchpoint (gdbarch, sal, addr_string,
+  create_ada_exception_catchpoint (trigger_set, stop_set,
+				   gdbarch, sal, addr_string,
 				   NULL, ops, tempflag, from_tty);
+
+  discard_cleanups (old_chain);
 }
                                 /* Operators */
 /* Information about operators given special treatment in functions
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index ec1565d..effb53b 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -34,6 +34,7 @@
 #include "value.h"
 #include "command.h"
 #include "inferior.h"
+#include "itset.h"
 #include "gdbthread.h"
 #include "target.h"
 #include "language.h"
@@ -3049,6 +3050,21 @@ hardware_watchpoint_inserted_in_range (struct address_space *aspace,
   return 0;
 }
 
+/* Test whether this stopping thread is in the I/T set for this
+   breakpoint.  */
+
+static int
+bpstat_check_trigger_set (const struct breakpoint *b, struct thread_info *thread)
+{
+  if (b->trigger_set == NULL)
+    return 1;
+
+  if (itset_contains_thread (b->trigger_set, thread))
+    return 1;
+
+  return 0;
+}
+
 /* breakpoint_thread_match (PC, PTID) returns true if the breakpoint at
    PC is valid for process/thread PTID.  */
 
@@ -3058,7 +3074,7 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
 {
   struct bp_location *bl, **blp_tmp;
   /* The thread and task IDs associated to PTID, computed lazily.  */
-  int thread = -1;
+  struct thread_info *thread = NULL;
   int task = 0;
   
   ALL_BP_LOCATIONS (bl, blp_tmp)
@@ -3080,9 +3096,9 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
 	  /* This is a thread-specific breakpoint.  Check that ptid
 	     matches that thread.  If thread hasn't been computed yet,
 	     it is now time to do so.  */
-	  if (thread == -1)
-	    thread = pid_to_thread_id (ptid);
-	  if (bl->owner->thread != thread)
+	  if (thread == NULL)
+	    thread = find_thread_ptid (ptid);
+	  if (bl->owner->thread != thread->num)
 	    continue;
 	}
 
@@ -3097,6 +3113,16 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
 	    continue;
         }
 
+      if (bl->owner->trigger_set != NULL)
+	{
+	  /* A breakpoint with a trigger itset.  Check that ptid
+	     matches that set.  */
+	  if (thread == NULL)
+	    thread = find_thread_ptid (ptid);
+	  if (!bpstat_check_trigger_set (bl->owner, thread))
+	    continue;
+	}
+
       if (overlay_debugging 
 	  && section_is_overlay (bl->section)
 	  && !section_is_mapped (bl->section))
@@ -3988,7 +4014,7 @@ bpstat_check_watchpoint (bpstat bs)
 static void
 bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
 {
-  int thread_id = pid_to_thread_id (ptid);
+  struct thread_info *thread = find_thread_ptid (ptid);
   const struct bp_location *bl;
   struct breakpoint *b;
 
@@ -4090,7 +4116,12 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
 	{
 	  bs->stop = 0;
 	}
-      else if (b->thread != -1 && b->thread != thread_id)
+      else if (b->thread != -1 && b->thread != thread->num)
+	{
+	  bs->stop = 0;
+	}
+      else if (b->trigger_set != NULL
+	       && !bpstat_check_trigger_set (b, thread))
 	{
 	  bs->stop = 0;
 	}
@@ -4518,6 +4549,34 @@ bpstat_what (bpstat bs_head)
   return retval;
 }
 
+/* Tell us what we should suspend.  Returns a union of the stop sets
+   of all breakpoints that caused the stop.  */
+
+struct itset *
+bpstat_stop_set (bpstat bs_head)
+{
+  bpstat bs;
+  struct itset *stop_set = itset_create_empty ();
+
+  for (bs = bs_head; bs != NULL; bs = bs->next)
+    {
+      if (bs->breakpoint_at == NULL)
+	{
+	  /* I suspect this can happen if it was a momentary
+	     breakpoint which has since been deleted.  */
+	  continue;
+	}
+
+      if (!bs->stop)
+	continue;
+
+      if (bs->breakpoint_at->stop_set != NULL)
+	itset_add_set (stop_set, bs->breakpoint_at->stop_set);
+    }
+
+  return stop_set;
+}
+
 /* Nonzero if we should step constantly (e.g. watchpoints on machines
    without hardware support).  This isn't related to a specific bpstat,
    just to things like whether watchpoints are set.  */
@@ -4917,7 +4976,27 @@ print_one_breakpoint_location (struct breakpoint *b,
       ui_out_field_int (uiout, "thread", b->thread);
       ui_out_text (uiout, "\n");
     }
-  
+
+  if (!part_of_multiple && b->trigger_set != NULL)
+    {
+      ui_out_text (uiout, "\tstop only in trigger-set: [");
+      if (itset_name (b->trigger_set) != NULL)
+	ui_out_field_string (uiout, "trigger-set", itset_name (b->trigger_set));
+      else
+	ui_out_field_string (uiout, "trigger-set", itset_spec (b->trigger_set));
+      ui_out_text (uiout, "]\n");
+    }
+
+  if (!part_of_multiple && b->stop_set != NULL)
+    {
+      ui_out_text (uiout, "\tsuspend all in stop-set: [");
+      if (itset_name (b->stop_set) != NULL)
+	ui_out_field_string (uiout, "stop-set", itset_name (b->stop_set));
+      else
+	ui_out_field_string (uiout, "stop-set", itset_spec (b->stop_set));
+      ui_out_text (uiout, "]\n");
+    }
+
   if (!part_of_multiple && b->hit_count)
     {
       /* FIXME should make an annotation for this.  */
@@ -5744,6 +5823,8 @@ init_raw_breakpoint_without_location (struct breakpoint *b,
   b->language = current_language->la_language;
   b->input_radix = input_radix;
   b->thread = -1;
+  b->trigger_set = NULL;
+  b->stop_set = NULL;
   b->enable_state = bp_enabled;
   b->next = 0;
   b->silent = 0;
@@ -6753,6 +6834,8 @@ syscall_catchpoint_p (struct breakpoint *b)
 
 static void
 init_catchpoint (struct breakpoint *b,
+		 struct itset *trigger_set,
+		 struct itset *stop_set,
 		 struct gdbarch *gdbarch, int tempflag,
 		 char *cond_string,
 		 const struct breakpoint_ops *ops)
@@ -6764,6 +6847,9 @@ init_catchpoint (struct breakpoint *b,
 
   init_raw_breakpoint (b, gdbarch, sal, bp_catchpoint, ops);
 
+  b->trigger_set = trigger_set;
+  b->stop_set = stop_set;
+
   b->cond_string = (cond_string == NULL) ? NULL : xstrdup (cond_string);
   b->disposition = tempflag ? disp_del : disp_donttouch;
 }
@@ -6782,13 +6868,16 @@ install_breakpoint (int internal, struct breakpoint *b, int update_gll)
 }
 
 static void
-create_fork_vfork_event_catchpoint (struct gdbarch *gdbarch,
+create_fork_vfork_event_catchpoint (struct itset *trigger_set,
+				    struct itset *stop_set,
+				    struct gdbarch *gdbarch,
 				    int tempflag, char *cond_string,
                                     const struct breakpoint_ops *ops)
 {
   struct fork_catchpoint *c = XNEW (struct fork_catchpoint);
 
-  init_catchpoint (&c->base, gdbarch, tempflag, cond_string, ops);
+  init_catchpoint (&c->base, trigger_set, stop_set,
+		   gdbarch, tempflag, cond_string, ops);
 
   c->forked_inferior_pid = null_ptid;
 
@@ -6917,14 +7006,17 @@ print_recreate_catch_exec (struct breakpoint *b, struct ui_file *fp)
 static struct breakpoint_ops catch_exec_breakpoint_ops;
 
 static void
-create_syscall_event_catchpoint (int tempflag, VEC(int) *filter,
-                                 const struct breakpoint_ops *ops)
+create_syscall_event_catchpoint (struct itset *trigger_set,
+				 struct itset *stop_set,
+				 int tempflag, VEC(int) *filter,
+				 const struct breakpoint_ops *ops)
 {
   struct syscall_catchpoint *c;
   struct gdbarch *gdbarch = get_current_arch ();
 
   c = XNEW (struct syscall_catchpoint);
-  init_catchpoint (&c->base, gdbarch, tempflag, NULL, ops);
+  init_catchpoint (&c->base, trigger_set, stop_set,
+		   gdbarch, tempflag, NULL, ops);
   c->syscalls_to_be_caught = filter;
 
   install_breakpoint (0, &c->base, 1);
@@ -7248,6 +7340,7 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
 		     struct symtabs_and_lines sals, char *addr_string,
 		     char *filter, char *cond_string,
 		     enum bptype type, enum bpdisp disposition,
+		     struct itset *trigger_set, struct itset *stop_set,
 		     int thread, int task, int ignore_count,
 		     const struct breakpoint_ops *ops, int from_tty,
 		     int enabled, int internal, int display_canonical)
@@ -7290,6 +7383,8 @@ init_breakpoint_sal (struct breakpoint *b, struct gdbarch *gdbarch,
 	  init_raw_breakpoint (b, gdbarch, sal, type, ops);
 	  b->thread = thread;
 	  b->task = task;
+	  b->trigger_set = trigger_set;
+	  b->stop_set = stop_set;
   
 	  b->cond_string = cond_string;
 	  b->ignore_count = ignore_count;
@@ -7369,6 +7464,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
 		       struct symtabs_and_lines sals, char *addr_string,
 		       char *filter, char *cond_string,
 		       enum bptype type, enum bpdisp disposition,
+		       struct itset *trigger_set, struct itset *stop_set,
 		       int thread, int task, int ignore_count,
 		       const struct breakpoint_ops *ops, int from_tty,
 		       int enabled, int internal, int display_canonical)
@@ -7392,6 +7488,7 @@ create_breakpoint_sal (struct gdbarch *gdbarch,
 		       sals, addr_string,
 		       filter, cond_string,
 		       type, disposition,
+		       trigger_set, stop_set,
 		       thread, task, ignore_count,
 		       ops, from_tty,
 		       enabled, internal, display_canonical);
@@ -7420,6 +7517,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 			struct linespec_result *canonical,
 			char *cond_string,
 			enum bptype type, enum bpdisp disposition,
+			struct itset *trigger_set, struct itset *stop_set,
 			int thread, int task, int ignore_count,
 			const struct breakpoint_ops *ops, int from_tty,
 			int enabled, int internal)
@@ -7445,6 +7543,7 @@ create_breakpoints_sal (struct gdbarch *gdbarch,
 			     addr_string,
 			     filter_string,
 			     cond_string, type, disposition,
+			     trigger_set, stop_set,
 			     thread, task, ignore_count, ops,
 			     from_tty, enabled, internal,
 			     canonical->special_display);
@@ -7468,7 +7567,8 @@ parse_breakpoint_sals (char **address,
 
   /* If no arg given, or if first arg is 'if ', use the default
      breakpoint.  */
-  if ((*address) == NULL
+  if (*address == NULL
+      || **address == '\0'
       || (strncmp ((*address), "if", 2) == 0 && isspace ((*address)[2])))
     {
       /* The last displayed codepoint, if it's valid, is our default breakpoint
@@ -7687,6 +7787,48 @@ decode_static_tracepoint_spec (char **arg_p)
   return sals;
 }
 
+void
+parse_breakpoint_args (char **args,
+		       struct itset **trigger_set,
+		       struct itset **stop_set)
+{
+  *trigger_set = itset_reference (current_itset);
+  if (non_stop)
+    *stop_set = itset_create_empty ();
+  else
+    *stop_set = itset_reference (*trigger_set);
+
+  if (*args != NULL)
+    {
+      char *arg = *args;
+
+      while (*arg)
+	{
+	  char *p;
+
+	  arg = skip_spaces (arg);
+	  p = skip_to_space (arg);
+
+	  if (strncmp (arg, "-stop", p - arg) == 0)
+	    {
+	      p = skip_spaces (p);
+	      itset_free (*stop_set);
+	      *stop_set = itset_create (&p);
+	      arg = p;
+	    }
+	  else if (strcmp (arg, "--") == 0)
+	    {
+	      arg += 2;
+	      break;
+	    }
+	  else
+	    break;
+	}
+
+      *args = arg;
+    }
+}
+
 /* Set a breakpoint.  This function is shared between CLI and MI
    functions for setting a breakpoint.  This function has two major
    modes of operations, selected by the PARSE_CONDITION_AND_THREAD
@@ -7714,15 +7856,24 @@ create_breakpoint (struct gdbarch *gdbarch,
   struct linespec_result canonical;
   struct cleanup *old_chain;
   struct cleanup *bkpt_chain = NULL;
+  struct cleanup *args_cleanup;
   int i;
   int pending = 0;
   int task = 0;
   int prev_bkpt_count = breakpoint_count;
+  char *p;
+  struct itset *trigger_set;
+  struct itset *stop_set;
 
   gdb_assert (ops != NULL);
 
   init_linespec_result (&canonical);
 
+  parse_breakpoint_args (&arg, &trigger_set, &stop_set);
+
+  args_cleanup = make_cleanup_itset_free (trigger_set);
+  make_cleanup_itset_free (stop_set);
+
   if (type_wanted == bp_static_tracepoint && is_marker_spec (arg))
     {
       int i;
@@ -7796,6 +7947,8 @@ create_breakpoint (struct gdbarch *gdbarch,
 
   done:
 
+  discard_cleanups (args_cleanup);
+
   /* Create a chain of things that always need to be cleaned up.  */
   old_chain = make_cleanup_destroy_linespec_result (&canonical);
 
@@ -7805,6 +7958,9 @@ create_breakpoint (struct gdbarch *gdbarch,
      then the memory is not reclaimed.  */
   bkpt_chain = make_cleanup (null_cleanup, 0);
 
+  make_cleanup_itset_free (trigger_set);
+  make_cleanup_itset_free (stop_set);
+
   /* Resolve all line numbers to PC's and verify that the addresses
      are ok for the target.  */
   if (!pending)
@@ -7887,7 +8043,7 @@ create_breakpoint (struct gdbarch *gdbarch,
 				   addr_string, NULL,
 				   cond_string, type_wanted,
 				   tempflag ? disp_del : disp_donttouch,
-				   thread, task, ignore_count, ops,
+				   NULL, NULL, thread, task, ignore_count, ops,
 				   from_tty, enabled, internal,
 				   canonical.special_display);
 	      /* Given that its possible to have multiple markers with
@@ -7907,6 +8063,7 @@ create_breakpoint (struct gdbarch *gdbarch,
 	create_breakpoints_sal (gdbarch, &canonical, cond_string,
 				type_wanted,
 				tempflag ? disp_del : disp_donttouch,
+				trigger_set, stop_set,
 				thread, task, ignore_count, ops, from_tty,
 				enabled, internal);
     }
@@ -9043,6 +9200,14 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
   int use_mask = 0;
   CORE_ADDR mask = 0;
   struct watchpoint *w;
+  struct itset *trigger_set;
+  struct itset *stop_set;
+  struct cleanup *old_chain;
+
+  parse_breakpoint_args (&arg, &trigger_set, &stop_set);
+
+  old_chain = make_cleanup_itset_free (trigger_set);
+  make_cleanup_itset_free (stop_set);
 
   /* Make sure that we actually have parameters to parse.  */
   if (arg != NULL && arg[0] != '\0')
@@ -9248,6 +9413,8 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
   else
     init_raw_breakpoint_without_location (b, NULL, bp_type,
 					  &watchpoint_breakpoint_ops);
+  b->trigger_set = trigger_set;
+  b->stop_set = stop_set;
   b->thread = thread;
   b->disposition = disp_donttouch;
   b->pspace = current_program_space;
@@ -9325,6 +9492,7 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
       throw_exception (e);
     }
 
+  discard_cleanups (old_chain);
   install_breakpoint (internal, b, 1);
 }
 
@@ -9654,6 +9822,9 @@ catch_fork_command_1 (char *arg, int from_tty,
   char *cond_string = NULL;
   catch_fork_kind fork_kind;
   int tempflag;
+  struct itset *trigger_set;
+  struct itset *stop_set;
+  struct cleanup *old_chain;
 
   fork_kind = (catch_fork_kind) (uintptr_t) get_cmd_context (command);
   tempflag = (fork_kind == catch_fork_temporary
@@ -9663,6 +9834,11 @@ catch_fork_command_1 (char *arg, int from_tty,
     arg = "";
   arg = skip_spaces (arg);
 
+  parse_breakpoint_args (&arg, &trigger_set, &stop_set);
+
+  old_chain = make_cleanup_itset_free (trigger_set);
+  make_cleanup_itset_free (stop_set);
+
   /* The allowed syntax is:
      catch [v]fork
      catch [v]fork if <cond>
@@ -9679,18 +9855,22 @@ catch_fork_command_1 (char *arg, int from_tty,
     {
     case catch_fork_temporary:
     case catch_fork_permanent:
-      create_fork_vfork_event_catchpoint (gdbarch, tempflag, cond_string,
+      create_fork_vfork_event_catchpoint (trigger_set, stop_set,
+					  gdbarch, tempflag, cond_string,
                                           &catch_fork_breakpoint_ops);
       break;
     case catch_vfork_temporary:
     case catch_vfork_permanent:
-      create_fork_vfork_event_catchpoint (gdbarch, tempflag, cond_string,
+      create_fork_vfork_event_catchpoint (trigger_set, stop_set,
+					  gdbarch, tempflag, cond_string,
                                           &catch_vfork_breakpoint_ops);
       break;
     default:
       error (_("unsupported or unknown fork kind; cannot catch it"));
       break;
     }
+
+  discard_cleanups (old_chain);
 }
 
 static void
@@ -9701,6 +9881,9 @@ catch_exec_command_1 (char *arg, int from_tty,
   struct gdbarch *gdbarch = get_current_arch ();
   int tempflag;
   char *cond_string = NULL;
+  struct itset *trigger_set;
+  struct itset *stop_set;
+  struct cleanup *old_chain;
 
   tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
 
@@ -9708,6 +9891,11 @@ catch_exec_command_1 (char *arg, int from_tty,
     arg = "";
   arg = skip_spaces (arg);
 
+  parse_breakpoint_args (&arg, &trigger_set, &stop_set);
+
+  old_chain = make_cleanup_itset_free (trigger_set);
+  make_cleanup_itset_free (stop_set);
+
   /* The allowed syntax is:
      catch exec
      catch exec if <cond>
@@ -9719,10 +9907,13 @@ catch_exec_command_1 (char *arg, int from_tty,
     error (_("Junk at end of arguments."));
 
   c = XNEW (struct exec_catchpoint);
-  init_catchpoint (&c->base, gdbarch, tempflag, cond_string,
+  init_catchpoint (&c->base, trigger_set, stop_set,
+		   gdbarch, tempflag, cond_string,
 		   &catch_exec_breakpoint_ops);
   c->exec_pathname = NULL;
 
+  discard_cleanups (old_chain);
+
   install_breakpoint (0, &c->base, 1);
 }
 
@@ -9893,6 +10084,8 @@ catch_throw_command (char *arg, int from_tty, struct cmd_list_element *command)
 
 void
 init_ada_exception_breakpoint (struct breakpoint *b,
+			       struct itset *trigger_set,
+			       struct itset *stop_set,
 			       struct gdbarch *gdbarch,
 			       struct symtab_and_line sal,
 			       char *addr_string,
@@ -9920,6 +10113,8 @@ init_ada_exception_breakpoint (struct breakpoint *b,
 
   init_raw_breakpoint (b, gdbarch, sal, bp_breakpoint, ops);
 
+  b->trigger_set = trigger_set;
+  b->stop_set = stop_set;
   b->enable_state = bp_enabled;
   b->disposition = tempflag ? disp_del : disp_donttouch;
   b->addr_string = addr_string;
@@ -9985,6 +10180,9 @@ catch_syscall_command_1 (char *arg, int from_tty,
   VEC(int) *filter;
   struct syscall s;
   struct gdbarch *gdbarch = get_current_arch ();
+  struct cleanup *old_chain;
+  struct itset *trigger_set;
+  struct itset *stop_set;
 
   /* Checking if the feature if supported.  */
   if (gdbarch_get_syscall_number_p (gdbarch) == 0)
@@ -9993,8 +10191,12 @@ this architecture yet."));
 
   tempflag = get_cmd_context (command) == CATCH_TEMPORARY;
 
+  parse_breakpoint_args (&arg, &trigger_set, &stop_set);
   arg = skip_spaces (arg);
 
+  old_chain = make_cleanup_itset_free (trigger_set);
+  make_cleanup_itset_free (stop_set);
+
   /* We need to do this first "dummy" translation in order
      to get the syscall XML file loaded or, most important,
      to display a warning to the user if there's no XML file
@@ -10012,8 +10214,11 @@ this architecture yet."));
   else
     filter = NULL;
 
-  create_syscall_event_catchpoint (tempflag, filter,
+  create_syscall_event_catchpoint (trigger_set, stop_set,
+				   tempflag, filter,
 				   &catch_syscall_breakpoint_ops);
+
+  discard_cleanups (old_chain);
 }
 
 static void
@@ -10822,6 +11027,8 @@ base_breakpoint_dtor (struct breakpoint *self)
   xfree (self->addr_string);
   xfree (self->filter);
   xfree (self->addr_string_range_end);
+  itset_free (self->trigger_set);
+  itset_free (self->stop_set);
 }
 
 static struct bp_location *
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ddf1881..22e7f0d 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -32,6 +32,7 @@ struct get_number_or_range_state;
 struct thread_info;
 struct bpstats;
 struct bp_location;
+struct itset;
 
 /* This is the maximum number of bytes a breakpoint instruction can
    take.  Feel free to increase it.  It's just used in a few places to
@@ -611,6 +612,15 @@ struct breakpoint
        or 0 if don't care.  */
     int task;
 
+    /* I/T set controlling where this breakpoint will stop.  */
+    struct itset *trigger_set;
+
+    /* The set that should be suspended once the breakpoint has
+       triggered.  If empty, defaults to consulting the "non-stop"
+       setting: if that is on, just the triggering thread should stop;
+       otherwise, all threads in the TRIGGER_SET set stop.  */
+    struct itset *stop_set;
+
     /* Count of the number of times this breakpoint was taken, dumped
        with the info, but not used for anything else.  Useful for
        seeing how many times you hit a break prior to the program
@@ -862,6 +872,9 @@ enum print_stop_action
 
 /* Tell what to do about this bpstat.  */
 struct bpstat_what bpstat_what (bpstat);
+
+/* Tell us what we should suspend.  */
+extern struct itset *bpstat_stop_set (bpstat);
 \f
 /* Find the bpstat associated with a breakpoint.  NULL otherwise.  */
 bpstat bpstat_find_breakpoint (bpstat, struct breakpoint *);
@@ -1091,6 +1104,8 @@ extern void
 
 extern void
   init_ada_exception_breakpoint (struct breakpoint *b,
+				 struct itset *trigger_set,
+				 struct itset *stop_set,
 				 struct gdbarch *gdbarch,
 				 struct symtab_and_line sal,
 				 char *addr_string,
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 50f1289..19e72a7 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -38,6 +38,7 @@
 #include "ada-lang.h"
 #include "gdbthread.h"
 #include "exceptions.h"
+#include "itset.h"
 
 /* If we can't find a function's name from its address,
    we print this instead.  */
@@ -796,6 +797,12 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
        dummy_id to form the frame ID's stack address.  */
     bpt = set_momentary_breakpoint (gdbarch, sal, dummy_id, bp_call_dummy);
     bpt->disposition = disp_del;
+
+    bpt->trigger_set = itset_reference (current_itset);
+    if (non_stop)
+      bpt->stop_set = itset_create_empty ();
+    else
+      bpt->stop_set = itset_reference (bpt->trigger_set);
   }
 
   /* Create a breakpoint in std::terminate.
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 324329f..7f6d643 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -26,6 +26,7 @@
 #include "symtab.h"
 #include "frame.h"
 #include "inferior.h"
+#include "itset.h"
 #include "exceptions.h"
 #include "breakpoint.h"
 #include "gdb_wait.h"
@@ -2640,6 +2641,8 @@ struct execution_control_state
   char *stop_func_name;
   int new_thread_event;
   int wait_some_more;
+
+  struct itset *stop_set;
 };
 
 static void handle_inferior_event (struct execution_control_state *ecs);
@@ -2655,6 +2658,20 @@ static void stop_stepping (struct execution_control_state *ecs);
 static void prepare_to_wait (struct execution_control_state *ecs);
 static void keep_going (struct execution_control_state *ecs);
 
+static void
+ecs_destroy (struct execution_control_state *ecs)
+{
+  itset_free (ecs->stop_set);
+}
+
+static void
+ecs_destroy_cleanup (void *arg)
+{
+  struct execution_control_state *ecs = arg;
+
+  ecs_destroy (ecs);
+}
+
 /* Callback for iterate over threads.  If the thread is stopped, but
    the user/frontend doesn't know about that yet, go through
    normal_stop, as if the thread had just stopped now.  ARG points at
@@ -2685,6 +2702,8 @@ infrun_thread_stop_requested_callback (struct thread_info *info, void *arg)
 
       switch_to_thread (info->ptid);
 
+      make_cleanup (ecs_destroy_cleanup, ecs);
+
       /* Go through handle_inferior_event/normal_stop, so we always
 	 have consistent output as if the stop event had been
 	 reported.  */
@@ -2966,6 +2985,7 @@ prepare_for_detach (void)
 
   while (!ptid_equal (displaced->step_ptid, null_ptid))
     {
+      struct cleanup *ecs_chain;
       struct cleanup *old_chain_2;
       struct execution_control_state ecss;
       struct execution_control_state *ecs;
@@ -2973,6 +2993,8 @@ prepare_for_detach (void)
       ecs = &ecss;
       memset (ecs, 0, sizeof (*ecs));
 
+      ecs_chain = make_cleanup (ecs_destroy_cleanup, ecs);
+
       overlay_cache_invalid = 1;
 
       ecs->ptid = do_target_wait (pid_ptid, &ecs->ws, 0);
@@ -3008,12 +3030,14 @@ prepare_for_detach (void)
 	  discard_cleanups (old_chain_1);
 	  error (_("Program exited while detaching"));
 	}
+
+      do_cleanups (ecs_chain);
     }
 
   discard_cleanups (old_chain_1);
 }
 
-static void stop_all_threads (void);
+static void stop_all_threads (struct itset *stop_set);
 static int adjust_pc_after_break (struct thread_info *thread,
 				  struct target_waitstatus *ws);
 
@@ -3087,7 +3111,7 @@ select_event_thread (struct execution_control_state *ecs)
 {
   if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed)
     {
-      stop_all_threads ();
+      stop_all_threads (NULL);
 
       if (ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
 	  && ecs->ws.kind != TARGET_WAITKIND_EXITED
@@ -3207,8 +3231,6 @@ void
 wait_for_inferior (void)
 {
   struct cleanup *old_cleanups;
-  struct execution_control_state ecss;
-  struct execution_control_state *ecs;
 
   if (debug_infrun)
     fprintf_unfiltered
@@ -3217,12 +3239,18 @@ wait_for_inferior (void)
   old_cleanups =
     make_cleanup (delete_step_thread_step_resume_breakpoint_cleanup, NULL);
 
-  ecs = &ecss;
-  memset (ecs, 0, sizeof (*ecs));
-
   while (1)
     {
+      struct execution_control_state ecss;
+      struct execution_control_state *ecs;
+      struct cleanup *ecs_chain;
       struct cleanup *old_chain;
+      int wait_some_more;
+
+      ecs = &ecss;
+      memset (ecs, 0, sizeof (*ecs));
+
+      ecs_chain = make_cleanup (ecs_destroy_cleanup, ecs);
 
       overlay_cache_invalid = 1;
 
@@ -3256,7 +3284,10 @@ wait_for_inferior (void)
       /* No error, don't finish the state yet.  */
       discard_cleanups (old_chain);
 
-      if (!ecs->wait_some_more)
+      wait_some_more = ecs->wait_some_more;
+      do_cleanups (ecs_chain);
+
+      if (!wait_some_more)
 	break;
     }
 
@@ -3284,6 +3315,8 @@ fetch_inferior_event (void *client_data)
 
   memset (ecs, 0, sizeof (*ecs));
 
+  make_cleanup (ecs_destroy_cleanup, ecs);
+
   /* We're handling a live event, so make sure we're doing live
      debugging.  If we're looking at traceframes while the target is
      running, we're going to need to get back to that mode after
@@ -3725,8 +3758,20 @@ wait_one (ptid_t wait_ptid, struct target_waitstatus *ws)
   return event_ptid;
 }
 
+static int
+stop_set_match (struct itset *stop_set, struct thread_info *t)
+{
+  if (stop_set == NULL || itset_is_empty_set (stop_set))
+    return !non_stop;
+
+  if (itset_contains_thread (stop_set, t))
+    return 1;
+
+  return 0;
+}
+
 static void
-stop_all_threads (void)
+stop_all_threads (struct itset *stop_set)
 {
   /* We may need multiple passes to discover all threads.  */
   int pass;
@@ -3756,6 +3801,7 @@ stop_all_threads (void)
 	  /* Go through all threads looking for threads that we need
 	     to tell the target to stop.  */
 	  ALL_LIVE_THREADS (t)
+	    if (stop_set_match (stop_set, t))
 	    {
 	      if (t->executing)
 		{
@@ -3800,6 +3846,11 @@ stop_all_threads (void)
 	    pass = -1;
 
 	  event_ptid = wait_one (minus_one_ptid, &ws);
+
+	  /* FIXME: if EVENT_PTID isn't in the trigger or stop sets,
+	     we'll need to re-resume or handle the event
+	     afterwards.  */
+
 	  if (ws.kind == TARGET_WAITKIND_NO_RESUMED)
 	    /* All resumed threads exited.  */
 	    ;
@@ -5276,7 +5327,11 @@ process_event_stop_test:
 	/* We are about to nuke the step_resume_breakpointt via the
 	   cleanup chain, so no need to worry about it here.  */
 
+	ecs->stop_set
+	  = bpstat_stop_set (ecs->event_thread->control.stop_bpstat);
 	stop_stepping (ecs);
+	itset_free (ecs->stop_set);
+	ecs->stop_set = NULL;
 	return;
 
       case BPSTAT_WHAT_STOP_SILENT:
@@ -5287,7 +5342,11 @@ process_event_stop_test:
 	/* We are about to nuke the step_resume_breakpoin via the
 	   cleanup chain, so no need to worry about it here.  */
 
+	ecs->stop_set
+	  = bpstat_stop_set (ecs->event_thread->control.stop_bpstat);
 	stop_stepping (ecs);
+	itset_free (ecs->stop_set);
+	ecs->stop_set = NULL;
 	return;
 
       case BPSTAT_WHAT_HP_STEP_RESUME:
@@ -6304,13 +6363,19 @@ stop_stepping (struct execution_control_state *ecs)
   /* Let callers know we don't want to wait for the inferior anymore.  */
   ecs->wait_some_more = 0;
 
-  if (!non_stop
-      && target_is_non_stop_p ()
-      && stop_only_if_needed)
+  if (target_is_non_stop_p () && stop_only_if_needed)
     {
       struct thread_info *t;
 
-      stop_all_threads ();
+      if (ecs->stop_set == NULL)
+	{
+	  if (non_stop)
+	    ecs->stop_set = itset_create_empty ();
+	  else
+	    ecs->stop_set = itset_reference (current_itset);
+	}
+
+      stop_all_threads (ecs->stop_set);
 
       cancel_breakpoints ();
 
@@ -6318,7 +6383,8 @@ stop_stepping (struct execution_control_state *ecs)
 	 are now stopped until a new resume action is sent
 	 over.  */
       ALL_LIVE_THREADS (t)
-	t->control.resumed = 0;
+	if (stop_set_match (ecs->stop_set, t))
+	  t->control.resumed = 0;
     }
 }
 
@@ -6377,7 +6443,7 @@ keep_going (struct execution_control_state *ecs)
 	      /* Since we can't do a displaced step, we have to remove
 		 the breakpoint while we step it.  To keep things
 		 simple, we remove them all.  */
-	      stop_all_threads ();
+	      stop_all_threads (NULL);
 
 	      cancel_breakpoints ();
 	      /* In all-stop, from the core's perspective, all threads


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

* [RFC/WIP PATCH v2 13/12] Comment out new info breakpoints output, in order to not break the test suite
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (5 preceding siblings ...)
  2011-12-17  3:05 ` [RFC/WIP PATCH v2 08/12] I/T set support for breakpoints - trigger set, and stop set Pedro Alves
@ 2011-12-17  3:07 ` Pedro Alves
  2011-12-17  3:08 ` [RFC/WIP PATCH v2 12/12] Default "set schedule-multiple" to on Pedro Alves
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  3:07 UTC (permalink / raw)
  To: gdb-patches

On Saturday 17 December 2011 02:59:04, Pedro Alves wrote:
> a bunch of changes and fixing to v1 brought the series close to being regression free.

You'll need this patch to run the testsuite though.  As the
new "info breakpoints" output breaks many tests.

Note that this time, I haven't pushed this patch out to 
http://github.com/palves/gdb/tree/itsets-v2 .

-- 
Pedro Alves

Comment out new info breakpoints output, in order to not break the test suite

---

 gdb/breakpoint.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 92929db..98d2279 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -4977,6 +4977,7 @@ print_one_breakpoint_location (struct breakpoint *b,
       ui_out_text (uiout, "\n");
     }
 
+#if 0 // disabled in order to be able to run the testsuite
   if (!part_of_multiple && b->trigger_set != NULL)
     {
       ui_out_text (uiout, "\tstop only in trigger-set: [");
@@ -4996,6 +4997,7 @@ print_one_breakpoint_location (struct breakpoint *b,
 	ui_out_field_string (uiout, "stop-set", itset_spec (b->stop_set));
       ui_out_text (uiout, "]\n");
     }
+#endif
 
   if (!part_of_multiple && b->hit_count)
     {


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

* [RFC/WIP PATCH v2 12/12] Default "set schedule-multiple" to on
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (6 preceding siblings ...)
  2011-12-17  3:07 ` [RFC/WIP PATCH v2 13/12] Comment out new info breakpoints output, in order to not break the test suite Pedro Alves
@ 2011-12-17  3:08 ` Pedro Alves
  2011-12-17  8:49 ` [RFC/WIP PATCH v2 14/12] experiment with "scope" Pedro Alves
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  3:08 UTC (permalink / raw)
  To: gdb-patches

See description of previous patch.
---
 gdb/infrun.c |    8 ++++----
 1 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index f77ecd7..c3e6d0d 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1646,10 +1646,10 @@ set_schedlock_func (char *args, int from_tty, struct cmd_list_element *c)
     }
 }
 
-/* True if execution commands resume all threads of all processes by
-   default; otherwise, resume only threads of the current inferior
-   process.  */
-int sched_multi = 0;
+/* True if execution commands resume all threads of all processes in
+   the current focus by default; otherwise, resume only threads of the
+   current inferior process.  */
+int sched_multi = 1;
 
 /* Try to setup for software single stepping over the specified location.
    Return 1 if target_resume() should use hardware single step.


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

* [RFC/WIP PATCH v2 14/12] experiment with "scope"
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (7 preceding siblings ...)
  2011-12-17  3:08 ` [RFC/WIP PATCH v2 12/12] Default "set schedule-multiple" to on Pedro Alves
@ 2011-12-17  8:49 ` Pedro Alves
  2011-12-17 12:35 ` [RFC/WIP PATCH v2 02/14] Implement all-stop on top of a target running non-stop mode Pedro Alves
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17  8:49 UTC (permalink / raw)
  To: gdb-patches

On Saturday 17 December 2011 02:59:04, Pedro Alves wrote:
> The name of this new concept is still up for grabs.  Let me repeat a
> bit of the discussion on the v1 series on the topic here:
> 
>   Tromey wrote:
>   > Pedro> IPTC sets are a collection of arbitrarily many processes,
>   > Pedro> threads, and cores.
>   > 
>   > I wish we had a better name.
>   > 
>   > The current name seems to gain a letter with every new thread on the
>   > topic :-)
> 
>   That was kind of on purpose.  :-)
> 
>   The best name I could think of is "scope".  If we call these things
>   scopes, we can also rename the itfocus command to "scope", which can
>   be abbreviated as "sc".  ("s" is already step, obviously).  It's spelled
>   itfocus currently because "focus" was already taken by the TUI.
> 
>   It looks like this:
> 
>   curinf> scope t3 break -stop i1 main
>   Breakpoint 2 at 0x40068c: file threads.c, line 35.
>   curinf> scope i1
>   Current inferior is 1.
>   i1> sc all
>   Current inferior is 1.
>   all>
>   
>   WDYT?

I wrote this patch to try out how it feels to use "scope" for things.
Also not in git.

-- 
Pedro Alves

---

 gdb/infrun.c |    2 +-
 gdb/itset.c  |   20 ++++++++++----------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index c3e6d0d..89cb51a 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1647,7 +1647,7 @@ set_schedlock_func (char *args, int from_tty, struct cmd_list_element *c)
 }
 
 /* True if execution commands resume all threads of all processes in
-   the current focus by default; otherwise, resume only threads of the
+   the current scope by default; otherwise, resume only threads of the
    current inferior process.  */
 int sched_multi = 1;
 
diff --git a/gdb/itset.c b/gdb/itset.c
index d65cc37..8ea89aa 100644
--- a/gdb/itset.c
+++ b/gdb/itset.c
@@ -1521,7 +1521,7 @@ make_cleanup_itset_free (struct itset *itset)
 static struct itset_elt *parse_neg (char **spec);
 static struct itset_elt *parse_parens_set (char **spec);
 static struct itset_elt *parse_itset_one (char **spec);
-static struct itset_elt *parse_current_focus (char **spec);
+static struct itset_elt *parse_current_scope (char **spec);
 
 static int
 valid_spec_end (char *spec)
@@ -1554,7 +1554,7 @@ parse_elem (char **spec)
   if (elt != NULL)
     return elt;
 
-  elt = parse_current_focus (spec);
+  elt = parse_current_scope (spec);
   if (elt != NULL)
     return elt;
 
@@ -1581,7 +1581,7 @@ parse_neg (char **spec)
 }
 
 static struct itset_elt *
-parse_current_focus (char **spec)
+parse_current_scope (char **spec)
 {
   struct itset_elt_itset *itset_elt;
 
@@ -2026,7 +2026,7 @@ switch_to_itset (struct itset *itset)
   int inf_count;
 
   /* For now, set a current inferior from the first element of the
-     focus set.  */
+     scope set.  */
   inf = iterate_over_itset_inferiors (itset, first_inferior, NULL);
   if (inf == NULL)
     {
@@ -2073,7 +2073,7 @@ switch_to_itset (struct itset *itset)
 }
 
 static void
-itfocus_command (char *spec, int from_tty)
+scope_command (char *spec, int from_tty)
 {
   struct itset *itset;
   struct cleanup *old_chain;
@@ -2081,11 +2081,11 @@ itfocus_command (char *spec, int from_tty)
   if (spec == NULL)
     {
       if (current_itset)
-	printf_filtered (_("Focus is `%s' (current inferior is %d)"),
+	printf_filtered (_("Scope is `%s' (current inferior is %d)"),
 			 current_itset->spec,
 			 current_inferior ()->num);
       else
-	printf_filtered (_("No focus has been set. (current inferior is %d)"),
+	printf_filtered (_("No scope has been set. (current inferior is %d)"),
 			 current_inferior ()->num);
       printf_filtered ("\n");
       return;
@@ -2111,7 +2111,7 @@ itfocus_command (char *spec, int from_tty)
     }
 
   if (itset_is_empty (itset))
-    warning (_("focus set is empty"));
+    warning (_("scope set is empty"));
 
   discard_cleanups (old_chain);
 
@@ -2120,7 +2120,7 @@ itfocus_command (char *spec, int from_tty)
 
   switch_to_itset (itset);
 
-  /* Confirm the choice of focus.  */
+  /* Confirm the choice of scope.  */
   printf_filtered (_("Current inferior is %d.\n"), current_inferior ()->num);
 }
 
@@ -2515,7 +2515,7 @@ _initialize_itset (void)
 
   current_itset = itset_reference (curinf_itset);
 
-  add_com ("itfocus", no_class, itfocus_command, _("\
+  add_com ("scope", no_class, scope_command, _("\
 Change the set of current inferiors/threads."));
 
   add_com ("defset", no_class, defset_command, _("\


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

* [RFC/WIP PATCH v2 02/14] Implement all-stop on top of a target running non-stop mode
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (8 preceding siblings ...)
  2011-12-17  8:49 ` [RFC/WIP PATCH v2 14/12] experiment with "scope" Pedro Alves
@ 2011-12-17 12:35 ` Pedro Alves
  2011-12-17 12:37 ` [RFC/WIP PATCH v2 05/14] watchthreads-reorder.exp: Don't assume there's no infrun output after the prompt Pedro Alves
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17 12:35 UTC (permalink / raw)
  To: gdb-patches

This implements all-stop on top of a target running in non-stop mode.
From the user's perspective, the all-stop mode is really just a
special case of being able to stop and resume specific sets of
threads, so it makes sense to do this step first.

This starts by adding a new target method "target_is_non_stop_p",
where targets that can do non-stop can always return true, even if
"set non-stop" is off.  With this, even in all-stop, the target is no
longer in charge of stopping all threads before reporting an event to
the core -- the core takes care of it when it sees fit.  For example,
when "next"- or "step"-ing, we can avoid stopping and resuming every
thread at each internal single-step, but instead stop all threads when
we're about to present the stop to the user.  While developing this, I
progressed in stages.  First I made wait_for_inferior stop all threads
right after the target returning an event, as that's the least
invasive change.  When I got that regression free, I added the
`stop_only_if_needed' global, which when true, means what I mentioned
above -- we only stop all threads when stop_stepping is called, which
means we're reporting an event to the user.  This flag will probably
go away at a later stage.

This forces always-inserted on for such targets that can always do
non-stop.  I think it would make sense to make the "breakpoint
always-inserted" auto mode actually remove breakpoints from the
inferior when all is stopped.  That way, we'd get the classic
behavior, that is a bit safer in that is avoids leaving breakpoints
planted on the target if gdb crashes in most cases, but, it'd also
leave breakpoints always inserted whenever there's a chance a thread
is running.  That'd get us the best of both worlds.


This is in need of a bit of cleanup and commenting throughout.  The
logic around target_pass_signals in do_target_resume may need a bit
more care.  Particularly after the later patch in the series that adds
itsets support to execution commands (apply_execution_command).  All
other !ptrace && !remote targets will need adjustment similar to
inf_ptrace_attach.


v2:

- fixes a bunch of regressions.

 I had the "force target-async on" patch after this one in the
 previous iteration, so I had been testing this patch in a weird mode
 with non-stop mode on, and with async off...  When I reversed the
 patches order, a bunch of problems appeared.

gdb/
2011-11-16  Pedro Alves  <pedro@codesourcery.com>

	* breakpoint.c (breakpoints_always_inserted_mode): Check
	target_is_non_stop_p instead of non_stop.
	(update_global_location_list): Ditto.
	* gdbthread.h (ALL_THREADS, ALL_LIVE_THREADS): New.
	(thread_list): Declare.
	(struct thread_control_state): New field `resumed'.
	(struct thread_suspend_state): New fields `waitstatus' and
	`waitstatus_pending_p'.
	* inf-ptrace.c (inf_ptrace_attach): Set the main thread's resumed
	flag.
	* infcall.c (run_inferior_call): Set the thread's state to
	THREAD_STOPPED while calling clear_proceed_status.
	* infrun.c: Include event-loop.h.
	(infrun_async_inferior_event_token): New global.
	(use_displaced_stepping): Check target_is_non_stop_p instead of
	non_stop.
	(displaced_step_fixup): Change return type to int.  Factor out
	bits into ...
	(displaced_step_next): ... this.  Use do_target_resume.
	(queued_for_displaced_step): New.
	(do_target_resume): New.
	(resume): Return early is the thread has a pending status.  Resume
	all threads if placed in the displaced stepping queue.  Use
	do_target_resume.
	(clear_proceed_status_thread): Bail out if the thread is running.
	(print_target_wait_results): Print the whole ptid.
	(do_target_wait): New.
	(prepare_for_detach): Use do_target_wait.
	(cancel_breakpoints): New.
	(set_executing_from_state): New.
	(stop_only_if_needed): New global.
	(select_event_thread): New.
	(wait_for_inferior): Use do_target_wait.  Call
	set_executing_from_state.  Add the event thread to the thread list
	if not already there.  Stop all threads and select the event
	thread.
	(fetch_inferior_event): Ditto.
	(adjust_pc_after_break): Change prototype.  Use
	target_is_non_stop_p.  Return an indication of whether adjustment
	was necessary.
	(wait_one): New.
	(stop_all_threads): New.
	(handle_inferior_event): Adjust to interface changes.  Use
	target_is_non_stop.
	(stop_stepping): Stop all threads if necessary.
	(keep_going): Stop all threads if starting a step-over.
	(infrun_async_inferior_event_handler): New.
	(_initialize_infrun): Install event source in the event-loop.
	* linux-nat.c (get_pending_status, linux_nat_detach): Use
	target_is_non_stop_p.
	(linux_handle_extended_wait): Set the resumed flag of the new lwp.
	Use target_is_non_stop_p.
	(linux_nat_wait_1, linux_nat_wait): Use target_is_non_stop_p.
	(linux_nat_is_non_stop_p): New.
	(linux_nat_stop): Use target_is_non_stop_p.
	(linux_nat_add_target): Install linux_nat_is_non_stop_p.
	* remote.c (remote_start_remote, extended_remote_attach_1): Set
	the resumed flag of the main thread.
	* target.c (update_current_target): INHERIT and de_fault
	to_is_non_stop_p.
	(target_wait): Return TARGET_WAITKIND_NO_RESUMED by default.
	* target.h (struct target_ops) <to_is_non_stop_p>: New field.
	(target_is_non_stop_p): New.
	* thread.c (thread_list): Make extern.
	(new_thread): Clear the suspend.waitstatus.kind field.
---
 gdb/breakpoint.c |    4 
 gdb/gdbthread.h  |   22 +
 gdb/inf-ptrace.c |    4 
 gdb/infcall.c    |   10 +
 gdb/infrun.c     |  892 ++++++++++++++++++++++++++++++++++++++++++++++++++----
 gdb/linux-nat.c  |   26 +-
 gdb/remote.c     |   10 -
 gdb/target.c     |    6 
 gdb/target.h     |    4 
 gdb/thread.c     |    3 
 10 files changed, 899 insertions(+), 82 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 783b797..ec1565d 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -379,7 +379,7 @@ int
 breakpoints_always_inserted_mode (void)
 {
   return ((always_inserted_mode == always_inserted_on
-	   || (always_inserted_mode == always_inserted_auto && non_stop))
+	   || (always_inserted_mode == always_inserted_auto && target_is_non_stop_p ()))
 	  && !RECORD_IS_USED);
 }
 
@@ -10555,7 +10555,7 @@ update_global_location_list (int should_insert)
 
       if (!found_object)
 	{
-	  if (removed && non_stop
+	  if (removed && target_is_non_stop_p ()
 	      && breakpoint_address_is_meaningful (old_loc->owner)
 	      && !is_hardware_watchpoint (old_loc->owner))
 	    {
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index af987f2..97ded57 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -38,6 +38,15 @@ enum thread_state
   THREAD_EXITED,
 };
 
+#define ALL_THREADS(T) \
+  for ((T) = thread_list; (T); (T) = (T)->next)
+
+#define ALL_LIVE_THREADS(T) \
+  for ((T) = thread_list; (T); (T) = (T)->next) \
+    if ((T)->state != THREAD_EXITED)
+
+extern struct thread_info *thread_list;
+
 /* Inferior thread specific part of `struct infcall_control_state'.
 
    Inferior process counterpart is `struct inferior_control_state'.  */
@@ -114,6 +123,14 @@ struct thread_control_state
   /* Chain containing status of breakpoint(s) the thread stopped
      at.  */
   bpstat stop_bpstat;
+
+  /* Non-zero if this thread will be/has been resumed.  Note that a
+     thread can be marked both as stopped and resumed at the same
+     time.  This happens if we try to resume a thread that has a wait
+     status pending.  We shouldn't let the thread run until that wait
+     status has been processed, but we should not report that wait
+     status if GDB didn't try to let the thread run.  */
+  int resumed;
 };
 
 /* Inferior thread specific part of `struct infcall_suspend_state'.
@@ -124,6 +141,11 @@ struct thread_suspend_state
 {
   /* Last signal that the inferior received (why it stopped).  */
   enum target_signal stop_signal;
+
+  /* The waitstatus for this thread's last event.  */
+  struct target_waitstatus waitstatus;
+  /* If true WAITSTATUS hasn't been handled yet.  */
+  int waitstatus_pending_p;
 };
 
 struct thread_info
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 110e825..0617631 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -194,6 +194,7 @@ inf_ptrace_attach (struct target_ops *ops, char *args, int from_tty)
   char *exec_file;
   pid_t pid;
   struct inferior *inf;
+  struct thread_info *tp;
 
   /* Do not change either targets above or the same target if already present.
      The reason is the target stack is shared across multiple inferiors.  */
@@ -243,7 +244,8 @@ inf_ptrace_attach (struct target_ops *ops, char *args, int from_tty)
 
   /* Always add a main thread.  If some target extends the ptrace
      target, it should decorate the ptid later with more info.  */
-  add_thread_silent (inferior_ptid);
+  tp = add_thread_silent (inferior_ptid);
+  tp->control.resumed = 1;
 
   if (! ops_already_pushed)
     discard_cleanups (back_to);
diff --git a/gdb/infcall.c b/gdb/infcall.c
index b8b7ff5..50f1289 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -389,11 +389,21 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
   volatile struct gdb_exception e;
   int saved_in_infcall = call_thread->control.in_infcall;
   ptid_t call_thread_ptid = call_thread->ptid;
+  int state = call_thread->state;
 
   call_thread->control.in_infcall = 1;
 
+  /* Fudge the thread's state so clear_proceed_status doesn't ignore
+     it.  We may be running an infcall with a THREAD_RUNNING but
+     !executing thread, e.g., when evaluating a breakpoint's
+     condition.  */
+  state = call_thread->state;
+  call_thread->state = THREAD_STOPPED;
+
   clear_proceed_status ();
 
+  call_thread->state = state;
+
   disable_watchpoints_before_interactive_call_start ();
 
   /* We want stop_registers, please...  */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5a0f1d2..324329f 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -57,6 +57,12 @@
 #include "continuations.h"
 #include "interps.h"
 #include "skip.h"
+#include "event-loop.h"
+
+/* Asynchronous signal handle registered as event loop source for
+   when we have pending events ready to be passed to the core.  */
+
+static struct async_event_handler *infrun_async_inferior_event_token;
 
 /* Prototypes for local functions */
 
@@ -1219,7 +1225,7 @@ static int
 use_displaced_stepping (struct gdbarch *gdbarch)
 {
   return (((can_use_displaced_stepping == can_use_displaced_stepping_auto
-	    && non_stop)
+	    && target_is_non_stop_p ())
 	   || can_use_displaced_stepping == can_use_displaced_stepping_on)
 	  && gdbarch_displaced_step_copy_insn_p (gdbarch)
 	  && !RECORD_IS_USED);
@@ -1413,21 +1419,24 @@ displaced_step_restore (struct displaced_step_inferior_state *displaced,
 				  displaced->step_copy));
 }
 
-static void
+static void do_target_resume (ptid_t ptid, int step, enum target_signal signo);
+
+static int
 displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
 {
   struct cleanup *old_cleanups;
   struct displaced_step_inferior_state *displaced
     = get_displaced_stepping_state (ptid_get_pid (event_ptid));
+  int ret;
 
   /* Was any thread of this process doing a displaced step?  */
   if (displaced == NULL)
-    return;
+    return 0;
 
   /* Was this event for the pid we displaced?  */
   if (ptid_equal (displaced->step_ptid, null_ptid)
       || ! ptid_equal (displaced->step_ptid, event_ptid))
-    return;
+    return 0;
 
   old_cleanups = make_cleanup (displaced_step_clear_cleanup, displaced);
 
@@ -1442,6 +1451,7 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
                                     displaced->step_original,
                                     displaced->step_copy,
                                     get_thread_regcache (displaced->step_ptid));
+      ret = 1;
     }
   else
     {
@@ -1452,12 +1462,25 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
 
       pc = displaced->step_original + (pc - displaced->step_copy);
       regcache_write_pc (regcache, pc);
+      ret = -1;
     }
 
   do_cleanups (old_cleanups);
 
   displaced->step_ptid = null_ptid;
 
+  return ret;
+}
+
+static void
+displaced_step_next (ptid_t event_ptid)
+{
+  struct displaced_step_inferior_state *displaced
+    = get_displaced_stepping_state (ptid_get_pid (event_ptid));
+
+  if (displaced == NULL)
+    return;
+
   /* Are there any pending displaced stepping requests?  If so, run
      one now.  Leave the state object around, since we're likely to
      need it again soon.  */
@@ -1469,6 +1492,7 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
       struct gdbarch *gdbarch;
       CORE_ADDR actual_pc;
       struct address_space *aspace;
+      struct thread_info *tp;
 
       head = displaced->step_request_queue;
       ptid = head->ptid;
@@ -1481,6 +1505,11 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
       actual_pc = regcache_read_pc (regcache);
       aspace = get_regcache_aspace (regcache);
 
+      tp = inferior_thread ();
+
+      gdb_assert (!tp->control.resumed);
+      gdb_assert (!tp->executing);
+
       if (breakpoint_here_p (aspace, actual_pc))
 	{
 	  if (debug_displaced)
@@ -1505,9 +1534,9 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
 
 	  if (gdbarch_displaced_step_hw_singlestep (gdbarch,
 						    displaced->step_closure))
-	    target_resume (ptid, 1, TARGET_SIGNAL_0);
+	    do_target_resume (ptid, 1, TARGET_SIGNAL_0);
 	  else
-	    target_resume (ptid, 0, TARGET_SIGNAL_0);
+	    do_target_resume (ptid, 0, TARGET_SIGNAL_0);
 
 	  /* Done, we're stepping a thread.  */
 	  break;
@@ -1526,10 +1555,10 @@ displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
 
 	  if (debug_displaced)
 	    fprintf_unfiltered (gdb_stdlog,
-				"breakpoint is gone %s: step(%d)\n",
+				"displaced: breakpoint is gone: %s, step(%d)\n",
 				target_pid_to_str (tp->ptid), step);
 
-	  target_resume (ptid, step, TARGET_SIGNAL_0);
+	  do_target_resume (ptid, step, TARGET_SIGNAL_0);
 	  tp->suspend.stop_signal = TARGET_SIGNAL_0;
 
 	  /* This request was discarded.  See if there's any other
@@ -1677,6 +1706,212 @@ user_visible_resume_ptid (int step)
   return resume_ptid;
 }
 
+static int
+queued_for_displaced_step (ptid_t ptid)
+{
+  struct displaced_step_inferior_state *displaced
+    = get_displaced_stepping_state (ptid_get_pid (ptid));
+  struct displaced_step_request *head;
+
+  if (displaced != NULL)
+    for (head = displaced->step_request_queue; head != NULL; head = head->next)
+      if (ptid_equal (head->ptid, ptid))
+	return 1;
+
+  return 0;
+}
+
+static void
+do_target_resume (ptid_t ptid, int step, enum target_signal signo)
+{
+  int resume_many;
+  struct thread_info *tp;
+  struct regcache *regcache = get_current_regcache ();
+  struct gdbarch *gdbarch = get_regcache_arch (regcache);
+  int want_all_signals;
+  int can_wildcard;
+  int any_to_resume;
+  ptid_t leader_ptid = inferior_ptid;
+
+  /* A specific PTID means `step only this process id'.  */
+  resume_many = (ptid_equal (minus_one_ptid, ptid)
+		 || ptid_is_pid (ptid));
+
+  /* See if it's the current inferior that should be handled
+     specially.  */
+  if (resume_many)
+    tp = find_thread_ptid (inferior_ptid);
+  else
+    tp = find_thread_ptid (ptid);
+  gdb_assert (tp != NULL);
+  gdb_assert (tp->state != THREAD_EXITED);
+
+  /* Advise target which signals may be handled silently.  If we have
+     removed breakpoints because we are stepping over one (which can
+     happen only if we are not using displaced stepping), we need to
+     receive all signals to avoid accidentally skipping a breakpoint
+     during execution of a signal handler.  */
+  want_all_signals = ((step || singlestep_breakpoints_inserted_p)
+		      && tp->control.trap_expected
+		      && !use_displaced_stepping (gdbarch));
+  if (want_all_signals)
+    target_pass_signals (0, NULL);
+  else
+    target_pass_signals ((int) TARGET_SIGNAL_LAST, signal_pass);
+
+  can_wildcard = 1;
+
+  ALL_LIVE_THREADS (tp)
+    {
+      if (ptid_match (tp->ptid, ptid))
+	{
+	  if (tp->executing)
+	    {
+	      if (debug_infrun)
+		fprintf_unfiltered (gdb_stdlog,
+				    "infrun: do_target_resume: executing\n");
+
+	      /* Need to prevent wildcard; the target may want to read
+		 PC for example.  And, we may have set EXECUTING
+		 temporarily true, while the thread really isn't
+		 executing, which would mean it's not safe to assume
+		 the target ignores this thread.  */
+	      can_wildcard = 0;
+	      continue;
+	    }
+
+	  if (queued_for_displaced_step (tp->ptid))
+	    {
+	      if (debug_infrun)
+		fprintf_unfiltered (gdb_stdlog,
+				    "infrun: do_target_resume: queued for displaced step\n");
+	      can_wildcard = 0;
+	      continue;
+	    }
+
+	  /* Avoid confusing the next resume, if the next stop/resume
+	     happens to apply to another thread.  */
+	  tp->suspend.stop_signal = TARGET_SIGNAL_0;
+	  tp->control.resumed = 1;
+
+	  if (tp->suspend.waitstatus_pending_p)
+	    {
+	      /* FIXME: What should we do if we are supposed to
+		 continue this thread with a signal?  */
+	      gdb_assert (signo == TARGET_SIGNAL_0);
+
+	      if (!step
+		  && tp->suspend.waitstatus.kind == TARGET_WAITKIND_STOPPED
+		  && !want_all_signals
+		  && signal_pass[signo])
+		{
+		  if (debug_infrun)
+		    {
+		      char *statstr;
+
+		      statstr = target_waitstatus_to_string (&tp->suspend.waitstatus);
+		      fprintf_unfiltered (gdb_stdlog,
+					  "infrun: Not short circuiting for ignored "
+					  "status %s\n",
+					  statstr);
+		      xfree (statstr);
+		    }
+
+		  signo = tp->suspend.waitstatus.value.sig;
+		  tp->suspend.waitstatus_pending_p = 0;
+		}
+	      else
+		{
+		  can_wildcard = 0;
+
+		  if (target_can_async_p ())
+		    {
+		      target_async (inferior_event_handler, 0);
+
+		      /* Tell the event loop we have something to
+			 process.  */
+		      mark_async_event_handler (infrun_async_inferior_event_token);
+		    }
+		  return; /// XXX why?
+		}
+	    }
+
+	  if (!tp->suspend.waitstatus_pending_p)
+	    any_to_resume = 1;
+	}
+    }
+
+  if (can_wildcard)
+    {
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog,
+			    "infrun: managed a wildcard target_resume\n");
+      target_resume (ptid, step, signo);
+    }
+  else
+    {
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog,
+			    "infrun: resuming threads individually\n");
+      ALL_LIVE_THREADS (tp)
+        {
+	  if (ptid_match (tp->ptid, ptid))
+	    {
+	      if (tp->executing)
+		{
+		  if (debug_infrun)
+		    fprintf_unfiltered (gdb_stdlog,
+					"infrun: Not resuming %s (executing)\n",
+					target_pid_to_str (tp->ptid));
+		}
+	      else if (queued_for_displaced_step (tp->ptid))
+		{
+		  if (debug_infrun)
+		    fprintf_unfiltered (gdb_stdlog,
+					"infrun: Not resuming %s (queued for displaced step)\n",
+					target_pid_to_str (tp->ptid));
+		}
+	      else if (!tp->suspend.waitstatus_pending_p)
+		{
+		  if (debug_infrun)
+		    fprintf_unfiltered (gdb_stdlog,
+					"infrun: resuming %s (no pending status)\n",
+					target_pid_to_str (tp->ptid));
+		  switch_to_thread (tp->ptid);
+
+		  if (ptid_equal (tp->ptid, leader_ptid))
+		    target_resume (tp->ptid, step, signo);
+		  else
+		    target_resume (tp->ptid, 0, TARGET_SIGNAL_0);
+		}
+	      else
+		{
+		  if (debug_infrun)
+		    {
+		      char *statstr;
+
+		      statstr = target_waitstatus_to_string (&tp->suspend.waitstatus);
+		      fprintf_unfiltered (gdb_stdlog,
+					  "infrun: not resuming %s, has pending status %s\n",
+					  target_pid_to_str (tp->ptid), statstr);
+		      xfree (statstr);
+		    }
+		}
+	    }
+	}
+
+      set_running (ptid, 1);
+      clear_inline_frame_state (ptid);
+    }
+
+  if (target_can_async_p ())
+    {
+      target_async (inferior_event_handler, 0);
+      /* Tell the event loop we have something to process.  */
+      mark_async_event_handler (infrun_async_inferior_event_token);
+    }
+}
+
 /* Resume the inferior, but allow a QUIT.  This is useful if the user
    wants to interrupt some lengthy single-stepping operation
    (for child processes, the SIGINT goes to the inferior, and so
@@ -1698,6 +1933,26 @@ resume (int step, enum target_signal sig)
 
   QUIT;
 
+  if (tp->suspend.waitstatus_pending_p)
+    {
+      /* Avoid confusing the next resume, if the next stop/resume
+	 happens to apply to another thread.  */
+      tp->suspend.stop_signal = TARGET_SIGNAL_0;
+      tp->control.resumed = 1;
+
+      set_running (inferior_ptid, 1);
+      clear_inline_frame_state (inferior_ptid);
+      discard_cleanups (old_cleanups);
+
+      if (target_can_async_p ())
+	{
+	  target_async (inferior_event_handler, 0);
+	  /* Tell the event loop we have something to process.  */
+	  mark_async_event_handler (infrun_async_inferior_event_token);
+	}
+      return;
+    }
+
   if (current_inferior ()->waiting_for_vfork_done)
     {
       /* Don't try to single-step a vfork parent that is waiting for
@@ -1761,14 +2016,34 @@ a command like `return' or `jump' to continue execution."));
 
       if (!displaced_step_prepare (inferior_ptid))
 	{
+	  ptid_t resume_ptid;
+
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog,
+				"Got placed in displaced stepping queue\n");
+
 	  /* Got placed in displaced stepping queue.  Will be resumed
 	     later when all the currently queued displaced stepping
 	     requests finish.  The thread is not executing at this point,
 	     and the call to set_executing will be made later.  But we
 	     need to call set_running here, since from frontend point of view,
 	     the thread is running.  */
+
+	  tp->suspend.stop_signal = TARGET_SIGNAL_0;
 	  set_running (inferior_ptid, 1);
+	  clear_inline_frame_state (inferior_ptid);
 	  discard_cleanups (old_cleanups);
+
+	  resume_ptid = user_visible_resume_ptid (step);
+
+	  gdb_assert (!tp->control.resumed);
+
+	  /* Resume others.  */
+	  tp->executing = 1;
+	  do_target_resume (resume_ptid, 0, TARGET_SIGNAL_0);
+	  tp->executing = 0;
+
+	  gdb_assert (!tp->control.resumed);
 	  return;
 	}
 
@@ -1900,23 +2175,7 @@ a command like `return' or `jump' to continue execution."));
       /* Install inferior's terminal modes.  */
       target_terminal_inferior ();
 
-      /* Avoid confusing the next resume, if the next stop/resume
-	 happens to apply to another thread.  */
-      tp->suspend.stop_signal = TARGET_SIGNAL_0;
-
-      /* Advise target which signals may be handled silently.  If we have
-	 removed breakpoints because we are stepping over one (which can
-	 happen only if we are not using displaced stepping), we need to
-	 receive all signals to avoid accidentally skipping a breakpoint
-	 during execution of a signal handler.  */
-      if ((step || singlestep_breakpoints_inserted_p)
-	  && tp->control.trap_expected
-	  && !use_displaced_stepping (gdbarch))
-	target_pass_signals (0, NULL);
-      else
-	target_pass_signals ((int) TARGET_SIGNAL_LAST, signal_pass);
-
-      target_resume (resume_ptid, step, sig);
+      do_target_resume (resume_ptid, step, sig);
     }
 
   discard_cleanups (old_cleanups);
@@ -1930,6 +2189,15 @@ a command like `return' or `jump' to continue execution."));
 static void
 clear_proceed_status_thread (struct thread_info *tp)
 {
+  if (tp->state == THREAD_RUNNING)
+    {
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog,
+			    "infrun: clear_proceed_status_thread (%s): ignored, thread is running\n",
+			    target_pid_to_str (tp->ptid));
+      return;
+    }
+
   if (debug_infrun)
     fprintf_unfiltered (gdb_stdlog,
 			"infrun: clear_proceed_status_thread (%s)\n",
@@ -2558,14 +2826,20 @@ print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid,
      is set.  */
 
   fprintf_unfiltered (tmp_stream,
-		      "infrun: target_wait (%d", PIDGET (waiton_ptid));
+		      "infrun: target_wait (%d.%ld.%ld",
+		      ptid_get_pid (waiton_ptid),
+		      ptid_get_lwp (waiton_ptid),
+		      ptid_get_tid (waiton_ptid));
   if (PIDGET (waiton_ptid) != -1)
     fprintf_unfiltered (tmp_stream,
 			" [%s]", target_pid_to_str (waiton_ptid));
   fprintf_unfiltered (tmp_stream, ", status) =\n");
   fprintf_unfiltered (tmp_stream,
-		      "infrun:   %d [%s],\n",
-		      PIDGET (result_ptid), target_pid_to_str (result_ptid));
+		      "infrun:   %d.%ld.%ld [%s],\n",
+		      ptid_get_pid (result_ptid),
+		      ptid_get_lwp (result_ptid),
+		      ptid_get_tid (result_ptid),
+		      target_pid_to_str (result_ptid));
   fprintf_unfiltered (tmp_stream,
 		      "infrun:   %s\n",
 		      status_string);
@@ -2581,6 +2855,88 @@ print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid,
   ui_file_delete (tmp_stream);
 }
 
+static ptid_t
+do_target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
+{
+  ptid_t event_ptid;
+  struct thread_info *tp;
+
+  /* First check if there is a thread with a wait status pending.  */
+  if (ptid_equal (ptid, minus_one_ptid) || ptid_is_pid (ptid))
+    {
+      ALL_LIVE_THREADS (tp)
+        {
+	  /* Only report a pending wait status if we pretend that this
+	     has indeed been resumed.  */
+	  if (ptid_match (tp->ptid, ptid)
+	      && tp->control.resumed
+	      && tp->suspend.waitstatus_pending_p)
+	    {
+	      gdb_assert (!tp->executing);
+	      break;
+	    }
+	}
+    }
+  else
+    {
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog,
+			    "infrun: Waiting for specific thread %s.\n",
+			    target_pid_to_str (ptid));
+
+      /* We have a specific thread to check.  */
+      tp = find_thread_ptid (ptid);
+      gdb_assert (tp);
+      if (!tp->suspend.waitstatus_pending_p)
+	tp = NULL;
+    }
+
+  if (tp)
+    {
+      if (debug_infrun)
+	{
+	  char *statstr;
+
+	  statstr = target_waitstatus_to_string (&tp->suspend.waitstatus);
+	  fprintf_unfiltered (gdb_stdlog,
+			      "infrun: Using pending wait status %s for %s.\n",
+			      statstr,
+			      target_pid_to_str (tp->ptid));
+	  xfree (statstr);
+	}
+
+      *status = tp->suspend.waitstatus;
+      tp->suspend.waitstatus_pending_p = 0;
+      return tp->ptid;
+    }
+
+  /* But if we don't find one, we'll have to wait.  */
+
+  if (deprecated_target_wait_hook)
+    event_ptid = deprecated_target_wait_hook (ptid, status, options);
+  else
+    event_ptid = target_wait (ptid, status, options);
+
+  if (status->kind != TARGET_WAITKIND_EXITED
+      && status->kind != TARGET_WAITKIND_SIGNALLED
+      /* If a thread not the main thread execl's, we'll see the event
+	 happening on the main thread, even if it was stopped.  It's
+	 as if the pre-exec thread changes its thread id (well, it
+	 really is that way on the Linux kernel).  */
+      && status->kind != TARGET_WAITKIND_EXECD
+      && status->kind != TARGET_WAITKIND_IGNORE)
+    {
+      tp = find_thread_ptid (event_ptid);
+      if (tp)
+	{
+	  gdb_assert (tp->control.resumed);
+	  tp->suspend.waitstatus = *status;
+	}
+    }
+
+  return event_ptid;
+}
+
 /* Prepare and stabilize the inferior for detaching it.  E.g.,
    detaching while a thread is displaced stepping is a recipe for
    crashing it, as nothing would readjust the PC out of the scratch
@@ -2619,10 +2975,7 @@ prepare_for_detach (void)
 
       overlay_cache_invalid = 1;
 
-      if (deprecated_target_wait_hook)
-	ecs->ptid = deprecated_target_wait_hook (pid_ptid, &ecs->ws, 0);
-      else
-	ecs->ptid = target_wait (pid_ptid, &ecs->ws, 0);
+      ecs->ptid = do_target_wait (pid_ptid, &ecs->ws, 0);
 
       if (debug_infrun)
 	print_target_wait_results (pid_ptid, ecs->ptid, &ecs->ws);
@@ -2660,6 +3013,189 @@ prepare_for_detach (void)
   discard_cleanups (old_chain_1);
 }
 
+static void stop_all_threads (void);
+static int adjust_pc_after_break (struct thread_info *thread,
+				  struct target_waitstatus *ws);
+
+static void
+cancel_breakpoints (void)
+{
+  struct thread_info *t;
+
+  /* Cancel any breakpoints in other threads that have hit a
+     breakpoint.  If we do not do this, then we run the risk that the
+     user will delete or disable the breakpoint, but the thread will
+     have already tripped on it.  */
+  ALL_LIVE_THREADS (t)
+    if (t->control.resumed
+	&& t->suspend.waitstatus_pending_p)
+      {
+	if (adjust_pc_after_break (t, &t->suspend.waitstatus))
+	  {
+	    /* Throw away the trap.  The thread will re-trap if
+	       re-resumed and the breakpoint is still
+	       installed.  */
+	    t->suspend.waitstatus_pending_p = 0;
+	    t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
+
+	    /* Need to call displaced_step_fixup here too, for SSS
+	       targets?  */
+	  }
+	else if (t->suspend.waitstatus.kind == TARGET_WAITKIND_STOPPED
+		 && displaced_step_fixup (t->ptid,
+					  t->suspend.waitstatus.value.sig) > 0)
+	  {
+	    /* Throw away the trap.  */
+	    t->suspend.waitstatus_pending_p = 0;
+	    t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
+	  }
+	else if (t->suspend.waitstatus.kind == TARGET_WAITKIND_STOPPED
+		 && t->suspend.waitstatus.value.sig == TARGET_SIGNAL_TRAP
+		 && currently_stepping (t))
+	  {
+	    /* Throw away the step trap.  */
+	    t->suspend.waitstatus_pending_p = 0;
+	    t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
+	  }
+      }
+}
+
+static void
+set_executing_from_state (struct execution_control_state *ecs)
+{
+  if (ecs->ws.kind == TARGET_WAITKIND_IGNORE
+      || ecs->ws.kind == TARGET_WAITKIND_NO_RESUMED)
+    ;
+  else if (ecs->ws.kind == TARGET_WAITKIND_SIGNALLED
+	   || ecs->ws.kind == TARGET_WAITKIND_EXITED)
+    {
+      int exited_pid = ptid_get_pid (ecs->ptid);
+      struct thread_info *t;
+
+      ALL_THREADS (t)
+	if (ptid_get_pid (t->ptid) == exited_pid)
+	  t->executing = 0;
+    }
+  else
+    set_executing (ecs->ptid, 0);
+}
+
+int stop_only_if_needed = 1;
+
+static void
+select_event_thread (struct execution_control_state *ecs)
+{
+  if (!non_stop && target_is_non_stop_p () && !stop_only_if_needed)
+    {
+      stop_all_threads ();
+
+      if (ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
+	  && ecs->ws.kind != TARGET_WAITKIND_EXITED
+	  && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
+	  && (ptid_equal (waiton_ptid, minus_one_ptid)
+	      || ptid_is_pid (waiton_ptid)))
+	{
+	  /* If we're not waiting for a specific thread, choose an
+	     event thread from among those that have had events.
+	     Giving equal priority to all threads that have had events
+	     helps prevent starvation.  */
+
+	  int num_events = 0;
+	  int random_selector;
+	  struct thread_info *event_tp;
+
+	  ecs->event_thread = find_thread_ptid (ecs->ptid);
+	  /* Record the wait status for the original thread.  */
+	  ecs->event_thread->suspend.waitstatus = ecs->ws;
+
+	  /* Give preference to any thread that is being
+	     single-stepped.  */
+	  ALL_LIVE_THREADS (event_tp)
+	    if (ptid_match (event_tp->ptid, waiton_ptid)
+		&& event_tp->control.resumed
+		&& event_tp->suspend.waitstatus_pending_p
+		&& currently_stepping (event_tp))
+	      break;
+
+	  if (event_tp != NULL)
+	    {
+	      if (debug_infrun)
+		fprintf_unfiltered (gdb_stdlog,
+				    "infrun: Select single-step %s\n",
+				    target_pid_to_str (event_tp->ptid));
+	    }
+	  else
+	    {
+	      /* No single-stepping thread.  Select one at random,
+		 out of those which have had events.
+
+		 First see how many events we have.  Count only
+		 resumed threads that have an event pending.  */
+	      ALL_LIVE_THREADS (event_tp)
+		if (ptid_match (event_tp->ptid, waiton_ptid)
+		    && event_tp->control.resumed
+		    && event_tp->suspend.waitstatus_pending_p)
+		  num_events++;
+
+	      /* Now randomly pick a LWP out of those that have had a SIGTRAP.  */
+	      random_selector = (int)
+		((num_events * (double) rand ()) / (RAND_MAX + 1.0));
+
+	      if (debug_infrun && num_events > 1)
+		fprintf_unfiltered (gdb_stdlog,
+				    "infrun: Found %d events, selecting #%d\n",
+				    num_events, random_selector);
+
+	      /* Select the Nth thread that has had an event.  */
+	      ALL_LIVE_THREADS (event_tp)
+		if (ptid_match (event_tp->ptid, waiton_ptid)
+		    && event_tp->control.resumed
+		    && event_tp->suspend.waitstatus_pending_p)
+		  if (random_selector-- == 0)
+		    break;
+	    }
+
+	  if (event_tp != NULL)
+	    {
+	      /* Switch the event thread.  */
+	      ecs->ptid = event_tp->ptid;
+	      ecs->event_thread = event_tp;
+	      ecs->ws = event_tp->suspend.waitstatus;
+	    }
+
+	  /* Flush the wait status for the event thread.  */
+	  ecs->event_thread->suspend.waitstatus_pending_p = 0;
+	}
+
+      {
+	struct thread_info *t;
+
+	/* Now that we've selected our final event thread, cancel
+	   any breakpoints in other threads that have hit a
+	   breakpoint.  If we do not do this, then we run the risk
+	   that the user will delete or disable the breakpoint,
+	   but the thread will have already tripped on it.  */
+	cancel_breakpoints ();
+
+	/* In all-stop, from the core's perspective, all threads
+	   are now stopped until a new resume action is sent
+	   over.  */
+	ALL_LIVE_THREADS (t)
+	  t->control.resumed = 0;
+      }
+    }
+  else
+    {
+      if (ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
+	  && ecs->ws.kind != TARGET_WAITKIND_EXITED
+	  && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
+	{
+	  ecs->event_thread = find_thread_ptid (ecs->ptid);
+	  ecs->event_thread->control.resumed = 0;
+	}
+    }
+}
+
 /* Wait for control to return from inferior to debugger.
 
    If inferior gets a signal, we may decide to start it up again
@@ -2690,14 +3226,13 @@ wait_for_inferior (void)
 
       overlay_cache_invalid = 1;
 
-      if (deprecated_target_wait_hook)
-	ecs->ptid = deprecated_target_wait_hook (waiton_ptid, &ecs->ws, 0);
-      else
-	ecs->ptid = target_wait (waiton_ptid, &ecs->ws, 0);
+      ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, 0);
 
       if (debug_infrun)
 	print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
 
+      set_executing_from_state (ecs);
+
       /* If an error happens while handling the event, propagate GDB's
 	 knowledge of the executing state to the frontend/user running
 	 state.  */
@@ -2707,6 +3242,14 @@ wait_for_inferior (void)
           || ecs->ws.kind == TARGET_WAITKIND_SYSCALL_RETURN)
         ecs->ws.value.syscall_number = UNKNOWN_SYSCALL;
 
+      if (ecs->ws.kind != TARGET_WAITKIND_EXITED
+	  && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
+	  && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
+	  && !in_thread_list (ecs->ptid))
+	add_thread (ecs->ptid);
+
+      select_event_thread (ecs);
+
       /* Now figure out what to do with the result of the result.  */
       handle_inferior_event (ecs);
 
@@ -2763,15 +3306,23 @@ fetch_inferior_event (void *client_data)
   make_cleanup_restore_integer (&execution_direction);
   execution_direction = target_execution_direction ();
 
-  if (deprecated_target_wait_hook)
-    ecs->ptid =
-      deprecated_target_wait_hook (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
-  else
-    ecs->ptid = target_wait (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
+  ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
 
   if (debug_infrun)
     print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
 
+  if (ecs->ws.kind != TARGET_WAITKIND_EXITED
+      && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
+      && ecs->ws.kind != TARGET_WAITKIND_IGNORE
+      && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
+      && !in_thread_list (ecs->ptid))
+    add_thread (ecs->ptid);
+
+  set_executing_from_state (ecs);
+
+  if (ecs->ws.kind != TARGET_WAITKIND_IGNORE)
+    select_event_thread (ecs);
+
   if (non_stop
       && ecs->ws.kind != TARGET_WAITKIND_IGNORE
       && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
@@ -2897,13 +3448,15 @@ context_switch (ptid_t ptid)
   switch_to_thread (ptid);
 }
 
-static void
-adjust_pc_after_break (struct execution_control_state *ecs)
+static int
+adjust_pc_after_break (struct thread_info *thread,
+		       struct target_waitstatus *ws)
 {
   struct regcache *regcache;
   struct gdbarch *gdbarch;
   struct address_space *aspace;
   CORE_ADDR breakpoint_pc;
+  int ret = 0;
 
   /* If we've hit a breakpoint, we'll normally be stopped with SIGTRAP.  If
      we aren't, just return.
@@ -2926,11 +3479,11 @@ adjust_pc_after_break (struct execution_control_state *ecs)
      target with both of these set in GDB history, and it seems unlikely to be
      correct, so gdbarch_have_nonsteppable_watchpoint is not checked here.  */
 
-  if (ecs->ws.kind != TARGET_WAITKIND_STOPPED)
-    return;
+  if (ws->kind != TARGET_WAITKIND_STOPPED)
+    return 0;
 
-  if (ecs->ws.value.sig != TARGET_SIGNAL_TRAP)
-    return;
+  if (ws->value.sig != TARGET_SIGNAL_TRAP)
+    return 0;
 
   /* In reverse execution, when a breakpoint is hit, the instruction
      under it has already been de-executed.  The reported PC always
@@ -2959,14 +3512,14 @@ adjust_pc_after_break (struct execution_control_state *ecs)
      INSN1 hadn't been de-executed yet.  Doing nothing is the correct
      behaviour.  */
   if (execution_direction == EXEC_REVERSE)
-    return;
+    return 0;
 
   /* If this target does not decrement the PC after breakpoints, then
      we have nothing to do.  */
-  regcache = get_thread_regcache (ecs->ptid);
+  regcache = get_thread_regcache (thread->ptid);
   gdbarch = get_regcache_arch (regcache);
   if (gdbarch_decr_pc_after_break (gdbarch) == 0)
-    return;
+    return 0;
 
   aspace = get_regcache_aspace (regcache);
 
@@ -2984,7 +3537,8 @@ adjust_pc_after_break (struct execution_control_state *ecs)
      SIGTRAPs, we keep a list of such breakpoint locations for a bit,
      and retire them after a number of stop events are reported.  */
   if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc)
-      || (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
+      || (target_is_non_stop_p ()
+	  && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
     {
       struct cleanup *old_cleanups = NULL;
 
@@ -3010,14 +3564,19 @@ adjust_pc_after_break (struct execution_control_state *ecs)
 	 we also need to back up to the breakpoint address.  */
 
       if (singlestep_breakpoints_inserted_p
-	  || !ptid_equal (ecs->ptid, inferior_ptid)
-	  || !currently_stepping (ecs->event_thread)
-	  || ecs->event_thread->prev_pc == breakpoint_pc)
-	regcache_write_pc (regcache, breakpoint_pc);
+	  || !ptid_equal (thread->ptid, inferior_ptid)
+	  || !currently_stepping (thread)
+	  || thread->prev_pc == breakpoint_pc)
+	{
+	  regcache_write_pc (regcache, breakpoint_pc);
+	  ret = 1;
+	}
 
       if (RECORD_IS_USED)
 	do_cleanups (old_cleanups);
     }
+
+  return ret;
 }
 
 void
@@ -3136,6 +3695,165 @@ fill_in_stop_func (struct gdbarch *gdbarch,
     }
 }
 
+static ptid_t
+wait_one (ptid_t wait_ptid, struct target_waitstatus *ws)
+{
+  struct cleanup *old_chain;
+  ptid_t event_ptid;
+
+  /* We have to invalidate the registers BEFORE calling target_wait
+     because they can be loaded from the target while in target_wait.
+     This makes remote debugging a bit more efficient for those
+     targets that provide critical registers as part of their normal
+     status mechanism.  */
+
+  overlay_cache_invalid = 1;
+  registers_changed ();
+
+  if (deprecated_target_wait_hook)
+    event_ptid = deprecated_target_wait_hook (wait_ptid, ws, 0);
+  else
+    event_ptid = target_wait (wait_ptid, ws, 0);
+
+  if (debug_infrun)
+    print_target_wait_results (wait_ptid, event_ptid, ws);
+
+  if (ws->kind == TARGET_WAITKIND_SYSCALL_ENTRY
+      || ws->kind == TARGET_WAITKIND_SYSCALL_RETURN)
+    ws->value.syscall_number = UNKNOWN_SYSCALL;
+
+  return event_ptid;
+}
+
+static void
+stop_all_threads (void)
+{
+  /* We may need multiple passes to discover all threads.  */
+  int pass;
+  int iterations = 0;
+
+  if (debug_infrun)
+    fprintf_unfiltered (gdb_stdlog, "infrun.c: stop_all_threads\n");
+
+  /* Stop threads in two passes since threads could be spawning as we
+     go through the first pass.  In the second pass, we will stop such
+     spawned threads.  */
+  for (pass = 0; pass < 2; pass++, iterations++)
+    {
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog,
+			    "infrun.c: stop_all_threads, pass=%d, iterations=%d\n",
+			    pass, iterations);
+      while (1)
+	{
+	  ptid_t event_ptid;
+	  struct target_waitstatus ws;
+	  int need_wait = 0;
+	  struct thread_info *t = NULL;
+
+	  update_thread_list ();
+
+	  /* Go through all threads looking for threads that we need
+	     to tell the target to stop.  */
+	  ALL_LIVE_THREADS (t)
+	    {
+	      if (t->executing)
+		{
+		  /* If already stopping, don't request a stop again.
+		     We just haven't seen the notification yet.  */
+		  if (!t->stop_requested)
+		    {
+		      if (debug_infrun)
+			fprintf_unfiltered (gdb_stdlog,
+					    "infrun.c:   %s executing, need stop\n",
+					    target_pid_to_str (t->ptid));
+		      target_stop (t->ptid);
+		      t->stop_requested = 1;
+		    }
+		  else
+		    {
+		      if (debug_infrun)
+			fprintf_unfiltered (gdb_stdlog,
+					    "infrun.c:   %s executing, already stopping\n",
+					    target_pid_to_str (t->ptid));
+		    }
+
+		  if (t->stop_requested)
+		    need_wait = 1;
+		}
+	      else
+		{
+		  if (debug_infrun)
+		    fprintf_unfiltered (gdb_stdlog,
+					"infrun.c:   %s not executing\n",
+					target_pid_to_str (t->ptid));
+		}
+	    }
+
+	  if (!need_wait)
+	    break;
+
+	  /* If we find new threads on the second iteration, restart
+	     over.  We want to see two iterations in a row with all
+	     threads stopped.  */
+	  if (pass > 0)
+	    pass = -1;
+
+	  event_ptid = wait_one (minus_one_ptid, &ws);
+	  if (ws.kind == TARGET_WAITKIND_NO_RESUMED)
+	    /* All resumed threads exited.  */
+	    ;
+	  else if (ws.kind == TARGET_WAITKIND_EXITED
+	      || ws.kind == TARGET_WAITKIND_SIGNALLED)
+	    warning ("exited while stopping threads\n");
+	  else
+	    {
+	      if (!in_thread_list (event_ptid))
+		t = add_thread (event_ptid);
+	      else
+		t = find_thread_ptid (event_ptid);
+
+	      t->stop_requested = 0;
+	      t->executing = 0;
+
+	      if (ws.kind == TARGET_WAITKIND_STOPPED && ws.value.sig == TARGET_SIGNAL_0)
+		{
+		  /* We caught the event that we intended to catch, so
+		     there's no event pending.  */
+		  t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
+		  t->suspend.waitstatus_pending_p = 0;
+		}
+	      else
+		{
+		  if (debug_infrun)
+		    {
+		      char *statstr;
+
+		      statstr = target_waitstatus_to_string (&ws);
+		      fprintf_unfiltered (gdb_stdlog,
+					  "infrun: target_wait %s, saving "
+					  "status for %d.%ld.%ld\n",
+					  statstr,
+					  ptid_get_pid (t->ptid),
+					  ptid_get_lwp (t->ptid),
+					  ptid_get_tid (t->ptid));
+		      xfree (statstr);
+		    }
+
+		  /* Record for later.  */
+		  t->suspend.waitstatus = ws;
+		  t->suspend.waitstatus_pending_p = 1;
+		}
+
+	      set_running (event_ptid, 0);
+	    }
+	}
+    }
+
+  if (debug_infrun)
+    fprintf_unfiltered (gdb_stdlog, "infrun.c: stop_all_threads done\n");
+}
+
 /* Given an execution control state that has been freshly filled in
    by an event from the inferior, figure out what it means and take
    appropriate action.  */
@@ -3226,7 +3944,7 @@ handle_inferior_event (struct execution_control_state *ecs)
   ecs->event_thread = find_thread_ptid (ecs->ptid);
 
   /* Dependent on valid ECS->EVENT_THREAD.  */
-  adjust_pc_after_break (ecs);
+  adjust_pc_after_break (ecs->event_thread, &ecs->ws);
 
   /* Dependent on the current PC value modified by adjust_pc_after_break.  */
   reinit_frame_cache ();
@@ -3266,7 +3984,7 @@ handle_inferior_event (struct execution_control_state *ecs)
      we're handling a process exit in non-stop mode, there's nothing
      to do, as threads of the dead process are gone, and threads of
      any other process were left running.  */
-  if (!non_stop)
+  if (!non_stop && !(target_is_non_stop_p () && stop_only_if_needed))
     set_executing (minus_one_ptid, 0);
   else if (ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
 	   && ecs->ws.kind != TARGET_WAITKIND_EXITED)
@@ -3463,7 +4181,8 @@ handle_inferior_event (struct execution_control_state *ecs)
 	       has been done.  Perform cleanup for parent process here.  Note
 	       that this operation also cleans up the child process for vfork,
 	       because their pages are shared.  */
-	    displaced_step_fixup (ecs->ptid, TARGET_SIGNAL_TRAP);
+	    if (displaced_step_fixup (ecs->ptid, TARGET_SIGNAL_TRAP))
+	      displaced_step_next (ecs->ptid);
 
 	    if (ecs->ws.kind == TARGET_WAITKIND_FORKED)
 	      {
@@ -3698,7 +4417,7 @@ handle_inferior_event (struct execution_control_state *ecs)
 
   if (ecs->new_thread_event)
     {
-      if (non_stop)
+      if (target_is_non_stop_p ())
 	/* Non-stop assumes that the target handles adding new threads
 	   to the thread list.  */
 	internal_error (__FILE__, __LINE__,
@@ -3725,8 +4444,9 @@ handle_inferior_event (struct execution_control_state *ecs)
       /* Do we need to clean up the state of a thread that has
 	 completed a displaced single-step?  (Doing so usually affects
 	 the PC, so do it here, before we set stop_pc.)  */
-      displaced_step_fixup (ecs->ptid,
-			    ecs->event_thread->suspend.stop_signal);
+      if (displaced_step_fixup (ecs->ptid,
+				ecs->event_thread->suspend.stop_signal))
+	displaced_step_next (ecs->ptid);
 
       /* If we either finished a single-step or hit a breakpoint, but
 	 the user wanted this thread to be stopped, pretend we got a
@@ -4600,7 +5320,7 @@ process_event_stop_test:
 
   /* In all-stop mode, if we're currently stepping but have stopped in
      some other thread, we need to switch back to the stepped thread.  */
-  if (!non_stop)
+  if (!target_is_non_stop_p ())
     {
       struct thread_info *tp;
 
@@ -5583,6 +6303,23 @@ stop_stepping (struct execution_control_state *ecs)
 
   /* Let callers know we don't want to wait for the inferior anymore.  */
   ecs->wait_some_more = 0;
+
+  if (!non_stop
+      && target_is_non_stop_p ()
+      && stop_only_if_needed)
+    {
+      struct thread_info *t;
+
+      stop_all_threads ();
+
+      cancel_breakpoints ();
+
+      /* In all-stop, from the core's perspective, all threads
+	 are now stopped until a new resume action is sent
+	 over.  */
+      ALL_LIVE_THREADS (t)
+	t->control.resumed = 0;
+    }
 }
 
 /* This function handles various cases where we need to continue
@@ -5634,10 +6371,23 @@ keep_going (struct execution_control_state *ecs)
 	  struct regcache *thread_regcache = get_thread_regcache (ecs->ptid);
 
 	  if (!use_displaced_stepping (get_regcache_arch (thread_regcache)))
-	    /* Since we can't do a displaced step, we have to remove
-	       the breakpoint while we step it.  To keep things
-	       simple, we remove them all.  */
-	    remove_breakpoints ();
+	    {
+	      struct thread_info *t;
+
+	      /* Since we can't do a displaced step, we have to remove
+		 the breakpoint while we step it.  To keep things
+		 simple, we remove them all.  */
+	      stop_all_threads ();
+
+	      cancel_breakpoints ();
+	      /* In all-stop, from the core's perspective, all threads
+		 are now stopped until a new resume action is sent
+		 over.  */
+	      ALL_LIVE_THREADS (t)
+		t->control.resumed = 0;
+
+	      remove_breakpoints ();
+	    }
 	}
       else
 	{
@@ -7060,12 +7810,22 @@ show_schedule_multiple (struct ui_file *file, int from_tty,
 			    "of all processes is %s.\n"), value);
 }
 
+static void
+infrun_async_inferior_event_handler (gdb_client_data data)
+{
+  inferior_event_handler (INF_REG_EVENT, NULL);
+}
+
 void
 _initialize_infrun (void)
 {
   int i;
   int numsigs;
 
+  /* Register extra event sources in the event loop.  */
+  infrun_async_inferior_event_token
+    = create_async_event_handler (infrun_async_inferior_event_handler, NULL);
+
   add_info ("signals", signals_info, _("\
 What debugger does when program gets various signals.\n\
 Specify a signal as argument to print info on that signal only."));
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 1cbfc44..3383609 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1652,13 +1652,13 @@ get_pending_status (struct lwp_info *lp, int *status)
     signo = TARGET_SIGNAL_0; /* a pending ptrace event, not a real signal.  */
   else if (lp->status)
     signo = target_signal_from_host (WSTOPSIG (lp->status));
-  else if (non_stop && !is_executing (lp->ptid))
+  else if (target_is_non_stop_p () && !is_executing (lp->ptid))
     {
       struct thread_info *tp = find_thread_ptid (lp->ptid);
 
       signo = tp->suspend.stop_signal;
     }
-  else if (!non_stop)
+  else if (!target_is_non_stop_p ())
     {
       struct target_waitstatus last;
       ptid_t last_ptid;
@@ -1810,7 +1810,7 @@ linux_nat_detach (struct target_ops *ops, char *args, int from_tty)
 	 available.  */
       linux_fork_detach (args, from_tty);
 
-      if (non_stop && target_can_async_p ())
+      if (target_is_non_stop_p ())
  	target_async (inferior_event_handler, 0);
     }
   else
@@ -2288,7 +2288,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
 		status = 0;
 	    }
 
-	  if (non_stop)
+	  if (target_is_non_stop_p ())
 	    {
 	      /* Add the new thread to GDB's lists as soon as possible
 		 so that:
@@ -2312,6 +2312,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
 		{
 		  set_running (new_lp->ptid, 1);
 		  set_executing (new_lp->ptid, 1);
+		  find_thread_ptid (new_lp->ptid)->control.resumed = 1;
 		  /* thread_db_attach_lwp -> lin_lwp_attach_lwp forced
 		     resume_stop.  */
 		  new_lp->last_resume_kind = resume_continue;
@@ -3685,7 +3686,7 @@ retry:
 			 core before this one is handled.  All-stop
 			 always cancels breakpoint hits in all
 			 threads.  */
-		      if (non_stop
+		      if (target_is_non_stop_p ()
 			  && linux_nat_lp_status_is_event (lp)
 			  && cancel_breakpoint (lp))
 			{
@@ -3844,7 +3845,7 @@ retry:
 	  goto retry;
 	}
 
-      if (!non_stop)
+      if (!target_is_non_stop_p ())
 	{
 	  /* Only do the below in all-stop, as we currently use SIGINT
 	     to implement target_stop (see linux_nat_stop) in
@@ -3872,7 +3873,7 @@ retry:
     fprintf_unfiltered (gdb_stdlog, "LLW: Candidate event %s in %s.\n",
 			status_to_str (status), target_pid_to_str (lp->ptid));
 
-  if (!non_stop)
+  if (!target_is_non_stop_p ())
     {
       /* Now stop all other LWP's ...  */
       iterate_over_lwps (minus_one_ptid, stop_callback, NULL);
@@ -4019,7 +4020,7 @@ linux_nat_wait (struct target_ops *ops,
      specific_process, for example, see linux_nat_wait_1), and
      meanwhile the event became uninteresting.  Don't bother resuming
      LWPs we're not going to wait for if they'd stop immediately.  */
-  if (non_stop)
+  if (target_is_non_stop_p ())
     iterate_over_lwps (minus_one_ptid, resume_stopped_resumed_lwps, &ptid);
 
   event_ptid = linux_nat_wait_1 (ops, ptid, ourstatus, target_options);
@@ -5478,6 +5479,12 @@ linux_nat_supports_non_stop (void)
   return 1;
 }
 
+static int
+linux_nat_is_non_stop_p (void)
+{
+  return 1;
+}
+
 /* True if we want to support multi-process.  To be removed when GDB
    supports multi-exec.  */
 
@@ -5700,7 +5707,7 @@ linux_nat_stop_lwp (struct lwp_info *lwp, void *data)
 static void
 linux_nat_stop (ptid_t ptid)
 {
-  if (non_stop)
+  if (target_is_non_stop_p ())
     iterate_over_lwps (ptid, linux_nat_stop_lwp, NULL);
   else
     linux_ops->to_stop (ptid);
@@ -5858,6 +5865,7 @@ linux_nat_add_target (struct target_ops *t)
   t->to_can_async_p = linux_nat_can_async_p;
   t->to_is_async_p = linux_nat_is_async_p;
   t->to_supports_non_stop = linux_nat_supports_non_stop;
+  t->to_is_non_stop_p = linux_nat_is_non_stop_p;
   t->to_async = linux_nat_async;
   t->to_terminal_inferior = linux_nat_terminal_inferior;
   t->to_terminal_ours = linux_nat_terminal_ours;
diff --git a/gdb/remote.c b/gdb/remote.c
index 9bfebd2..a9f8f6f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3255,6 +3255,8 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
 
   if (!non_stop)
     {
+      struct thread_info *tp;
+
       if (rs->buf[0] == 'W' || rs->buf[0] == 'X')
 	{
 	  if (!extended_p)
@@ -3289,7 +3291,8 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
       remote_add_inferior (ptid_get_pid (inferior_ptid), -1);
 
       /* Always add the main thread.  */
-      add_thread_silent (inferior_ptid);
+      tp = add_thread_silent (inferior_ptid);
+      tp->control.resumed = 1;
 
       /* init_wait_for_inferior should be called before get_offsets in order
 	 to manage `inserted' flag in bp loc in a correct state.
@@ -4302,11 +4305,14 @@ extended_remote_attach_1 (struct target_ops *target, char *args, int from_tty)
     }
   else
     {
+      struct thread_info *tp;
+
       /* Now, if we have thread information, update inferior_ptid.  */
       inferior_ptid = remote_current_thread (inferior_ptid);
 
       /* Add the main thread to the thread list.  */
-      add_thread_silent (inferior_ptid);
+      tp = add_thread_silent (inferior_ptid);
+      tp->control.resumed = 1;
     }
 
   /* Next, if the target can specify a description, read it.  We do
diff --git a/gdb/target.c b/gdb/target.c
index a6e146d..ca00912 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -659,6 +659,8 @@ update_current_target (void)
       INHERIT (to_can_async_p, t);
       INHERIT (to_is_async_p, t);
       INHERIT (to_async, t);
+      /* Do not inherit to_supports_non_stop.  */
+      INHERIT (to_is_non_stop_p, t);
       INHERIT (to_find_memory_regions, t);
       INHERIT (to_make_corefile_notes, t);
       INHERIT (to_get_bookmark, t);
@@ -927,6 +929,7 @@ update_current_target (void)
 	    (struct traceframe_info * (*) (void))
 	    tcomplain);
   de_fault (to_execution_direction, default_execution_direction);
+  de_fault (to_is_non_stop_p, return_zero);
 
 #undef de_fault
 
@@ -2619,7 +2622,8 @@ target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
 	}
     }
 
-  noprocess ();
+  status->kind = TARGET_WAITKIND_NO_RESUMED;
+  return minus_one_ptid;
 }
 
 char *
diff --git a/gdb/target.h b/gdb/target.h
index fd488d6..6e079e8 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -534,6 +534,7 @@ struct target_ops
     int (*to_is_async_p) (void);
     void (*to_async) (void (*) (enum inferior_event_type, void *), void *);
     int (*to_supports_non_stop) (void);
+    int (*to_is_non_stop_p) (void);
     /* find_memory_regions support method for gcore */
     int (*to_find_memory_regions) (find_memory_region_ftype func, void *data);
     /* make_corefile_notes support method for gcore */
@@ -1292,6 +1293,9 @@ extern int target_async_permitted;
 
 int target_supports_non_stop (void);
 
+/* Is the target in non-stop mode?  */
+#define target_is_non_stop_p() (current_target.to_is_non_stop_p ())
+
 /* Put the target in async mode with the specified callback function.  */
 #define target_async(CALLBACK,CONTEXT) \
      (current_target.to_async ((CALLBACK), (CONTEXT)))
diff --git a/gdb/thread.c b/gdb/thread.c
index 8cca83d..0777887 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -55,7 +55,7 @@ void _initialize_thread (void);
 
 /* Prototypes for local functions.  */
 
-static struct thread_info *thread_list = NULL;
+struct thread_info *thread_list = NULL;
 static int highest_thread_num;
 
 static void thread_command (char *tidstr, int from_tty);
@@ -174,6 +174,7 @@ new_thread (ptid_t ptid)
   /* Nothing to follow yet.  */
   tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
   tp->state = THREAD_STOPPED;
+  tp->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE;
 
   return tp;
 }


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

* [RFC/WIP PATCH v2 06/14] Add base itsets support
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (10 preceding siblings ...)
  2011-12-17 12:37 ` [RFC/WIP PATCH v2 05/14] watchthreads-reorder.exp: Don't assume there's no infrun output after the prompt Pedro Alves
@ 2011-12-17 12:37 ` Pedro Alves
  2011-12-17 12:38 ` [RFC/WIP PATCH v2 09/14] Add I/T set support to most execution commands Pedro Alves
  2011-12-17 14:05 ` [RFC/WIP PATCH v2 10/14] Parallel steps, wait for all threads to reach their goal Pedro Alves
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17 12:37 UTC (permalink / raw)
  To: gdb-patches

This adds the base itsets support.  v2 implements some of the ideas
mentioned in the v1 posts.  Particularly, it drifts the spec syntax
away from HPD.  All kinds of objects you can specify on a spec have
the same weight.  Object types have a prefix letter followed by a id
or id range.  `i' for inferior, `t' for thread, `a' for Ada task, `c'
for core, and others may be added latter (e.g., process or target
thread id).  `.' is the intersection operator.  `,' is the union
operator.  `,' (union) has precedence over `.' (intersect).  `(' and
`)' can be used in the natural way to defeat precedence.  `~' is the
negating operator.  `!' specifies a static set.  Sets are dynamic by
default.  `$' represents the current focus set.

This allows for example using just "c1" to mean "everything on core
1"; "foobar.c2" (or c2.foobar) to mean "all threads in the foobar
group that are in core 2"; "workers.~(i2,i3)" to mean all workers not
in inferior 2 or 3.

The current syntax follows this grammar:

  ELEM = RANGE | NEG | PARENS_SET | '$' | NAME
  NEG = '~' ELEM
  PARENS_SET = '(' ITSET_ONE ')'
  INTERS = ELEM ('.' ELEM)*
  ITSET_ONE = INTERS (',' INTERS)*
  ITSET = | ('!' ITSET_ONE) | ITSET_ONE

  E.g.,:

  c1-3.~i1.(t1,other),foo is transformed to a tree like:

   UNION
     foo
     INTERS
       c1-3
       NEG
         i1-1
       UNION
         t1-1
	 other

Currently:

 - Supports user-specified named sets, and built-in sets.  The
   built-in sets are currently: all (all threads, all inferiors);
   running (set of running threads); stopped (set of stopped threads);
   curinf (the current inferior); empty (the empty set); $ (the
   current focus set); and a special exec(FILENAME) set, that takes a
   program file name (exec) as argument, to match all inferiors
   running a given program.

 - An "itfocus" command.  This serves two related purposes.  If passed
   an I/T set and nothing else, as in `itfocus SET', e.g.,

     (gdb) itfocus i2

   it sets the current focus to SET.  In this example, the focus was
   set to inferior 2.

   If in addition, a command is passed after the SET, as in,
   `itfocus SET CMD`, then CMD is executed with the current focus
   switched to SET temporarily.  E.g.,

     (gdb) itfocus i3 break foo

   the breakpoint is set on inferior 3.  This is the same as:

     (gdb) itfocus i3
     (gdb) break foo
     (gdb) itfocus what_ever_was_the_current_set_before

 - a "defset" command.  This is used to define named itsets.  E.g.,

     (gdb) defset firstthree t1-3

   defines the set `firstthree', as being the threads 1, 2 and 3.  You
   can then use the set wherever an itset is allowed.  E.g., with a
   later patch, you'll be able to interrupt all threads in a specific
   set with:

     (gdb) interrupt -p firstthree

 - an "undefset" command.  Used to undefine a set defined by itset.
   "undefset -all" undefines all user-defined named sets.

 - an "info itsets" command.  Lists all named sets, in GDB "info"
   style.

 - A "whichsets" command.  This shows which sets does a thread belong
   to.

 - A "viewset" command.  This shows which inferiors and threads are
   included in a specified itset.  It's the reverse of "whichsets".

The current default current set is this patch is `all'.  I'm thinking
it may make sense to default to `curinf' instead, and thus get rid of
"set schedule-multiple" (see the patch about execution commands
further in the series).

Examples:

all> defset test t1,t2
all> info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 6128) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0
* 2    Thread 0x7ffff7829700 (LWP 6127) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 6124) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6
all> info itsets
Num     Name           What
1       test           t1,t2
all> viewset test
test contains:
  inferiors: 1
  threads: 2, 1
  cores: 5, 0
all> whichsets t1
i1.t1 (Thread 0x7ffff7fcb720 (LWP 6124)) is in: all, stopped, curinf, test
all> undefset test


I haven't had much chance yet of playing with the syntax and think of
many real use cases; I'm sure there are holes to fill.  Perhaps we may
even find out this syntax doesn't work, though it looks promising to
me.


v2:

  - Dropped [] everywhere; forbids spaces in specs.
  - Replaced the [] prefix command the "itfocus" command taking an
    optional command to execute.
  - object types in spec require prefix letter.
  - Parsing rewritten as a simple recursing descent parser.  ()'s
    support for grouping added.
  - Added `empty' and `$' sets.
  - Many cleanups and bugfixes.

2011-12-16  Pedro Alves  <pedro@codesourcery.com>
	    Tom Tromey  <tromey@redhat.com>

	* Makefile.in (SFILES): Add itset.c.
	(COMMON_OBS): Add itset.o.
	* gdbthread.h (get_thread_inferior): Declare.
	* thread.c (get_thread_inferior): New.
	* itset.h, itset.c: New.
---
 gdb/Makefile.in |    4 
 gdb/gdbthread.h |    4 
 gdb/itset.c     | 2549 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/itset.h     |  122 +++
 gdb/thread.c    |    9 
 5 files changed, 2686 insertions(+), 2 deletions(-)
 create mode 100644 gdb/itset.c
 create mode 100644 gdb/itset.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index b71db33..16c5578 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -742,7 +742,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
 	valarith.c valops.c valprint.c value.c varobj.c vec.c \
 	wrapper.c \
 	xml-tdesc.c xml-support.c \
-	inferior.c gdb_usleep.c \
+	inferior.c itset.c gdb_usleep.c \
 	record.c gcore.c \
 	jit.c \
 	xml-syscall.c \
@@ -915,7 +915,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
 	prologue-value.o memory-map.o memrange.o \
 	xml-support.o xml-syscall.o xml-utils.o \
 	target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
-	inferior.o osdata.o gdb_usleep.o record.o gcore.o \
+	inferior.o itset.o osdata.o gdb_usleep.o record.o gcore.o \
 	jit.o progspace.o skip.o \
 	common-utils.o buffer.o ptid.o gdb-dlfcn.o
 
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 97ded57..33fefcf 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -411,4 +411,8 @@ extern struct thread_info* inferior_thread (void);
 
 extern void update_thread_list (void);
 
+/* Return a pointer to the inferior of thread THR.  */
+
+extern struct inferior *get_thread_inferior (struct thread_info *thr);
+
 #endif /* GDBTHREAD_H */
diff --git a/gdb/itset.c b/gdb/itset.c
new file mode 100644
index 0000000..dfa2c5f
--- /dev/null
+++ b/gdb/itset.c
@@ -0,0 +1,2549 @@
+/* itset.c - Inferior/Thread sets.
+   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "itset.h"
+#include "vec.h"
+#include "bfd.h"
+#include "inferior.h"
+#include "progspace.h"
+#include "gdb_string.h"
+#include "cli/cli-utils.h"
+#include "gdbthread.h"
+#include "command.h"
+#include <ctype.h>
+#include "gdbcmd.h"
+
+/* Rather than creating/destroying these dynamic itsets when
+   necessary, keep global copies around (itsets are refcounted).  */
+static struct itset *all_itset;
+static struct itset *empty_itset;
+static struct itset *running_itset;
+static struct itset *stopped_itset;
+static struct itset *curinf_itset;
+
+/* Forward declaration of the base class.  */
+
+struct itset_elt;
+
+/* An element of an I/T set is a class with some virtual methods,
+   defined here.  */
+
+struct itset_elt_vtable
+{
+  /* Destroy the contents of this element.  If the element does not
+     require any special cleanup, this can be NULL.  This should not
+     free the element itself; that is done by the caller.  */
+
+  void (*destroy) (struct itset_elt *);
+
+  /* Return true if the element contains the inferior.  The element
+     and the inferior are passed as arguments.  */
+
+  int (*contains_inferior) (struct itset_elt *elt, struct inferior *inf);
+
+  /* Return true if the element contains the thread.  The element and
+     the thread are passed as arguments.  */
+
+  int (*contains_thread) (struct itset_elt *elt, struct thread_info *thr);
+
+  /* Return true if the element is empty.  */
+
+  int (*is_empty) (struct itset_elt *elt);
+};
+
+/* The base class of all I/T set elements.  */
+
+struct itset_elt
+{
+  const struct itset_elt_vtable *vtable;
+};
+
+static void
+itset_elt_free (struct itset_elt *elt)
+{
+  if (elt->vtable->destroy != NULL)
+    elt->vtable->destroy (elt);
+  xfree (elt);
+}
+
+static void
+itset_elt_free_cleanup (void *arg)
+{
+  struct itset_elt *elt = arg;
+
+  itset_elt_free (elt);
+}
+
+struct cleanup *
+make_cleanup_itset_elt_free (struct itset_elt *elt)
+{
+  return make_cleanup (itset_elt_free_cleanup, elt);
+}
+
+typedef struct itset_elt *itset_elt_ptr;
+DEF_VEC_P (itset_elt_ptr);
+
+struct itset
+{
+  /* The itset's name.  May be NULL.  */
+  char *name;
+
+  /* The original specification of the set.  */
+  char *spec;
+
+  /* The reference count.  */
+  int refc;
+
+  /* The elements making up the set.  */
+  VEC (itset_elt_ptr) *elements;
+};
+
+const char *
+itset_name (const struct itset *itset)
+{
+  return itset->name;
+}
+
+const char *
+itset_spec (const struct itset *itset)
+{
+  return itset->spec;
+}
+
+int
+itset_is_empty_set (struct itset *set)
+{
+  return VEC_empty (itset_elt_ptr, set->elements);
+}
+
+\f
+
+/* An element in the list of named itsets, which can be either
+   debugger built-in (all, stopped, running, etc.), or user
+   defined.  */
+
+struct named_itset
+{
+  /* Pointer to next in linked list.  */
+  struct named_itset *next;
+
+  /* Unique identifier.  Positive if user defined, negative if
+     internal and built-in.  */
+  int number;
+
+  /* The I/T set.  */
+  struct itset *set;
+};
+
+/* The head of the list of named I/T sets.  */
+
+static struct named_itset *named_itsets;
+
+/* Number of last named itset made.  */
+
+static int named_itset_count;
+
+/* Number of last internal named itset made.  */
+
+static int internal_named_itset_count;
+
+/* Traverse all named itsets.  */
+
+#define ALL_NAMED_ITSETS(E) \
+  for ((E) = named_itsets; (E); (E) = (E)->next)
+
+/* Add IT at the end of the named itset chain.  */
+
+static void
+add_to_named_itset_chain (struct named_itset *it)
+{
+  struct named_itset *it1;
+
+  /* Add this itset to the end of the chain so that a list of itsets
+     will come out in order of increasing numbers.  */
+
+  it1 = named_itsets;
+  if (it1 == 0)
+    named_itsets = it;
+  else
+    {
+      while (it1->next)
+	it1 = it1->next;
+      it1->next = it;
+    }
+}
+
+static struct named_itset *
+get_named_itset (char *name)
+{
+  struct named_itset *it;
+
+  for (it = named_itsets; it != NULL; it = it->next)
+    if (strcmp (it->set->name, name) == 0)
+      return it;
+  return NULL;
+}
+
+\f
+
+/* A helper function that returns true if all elements in the ELEMENTS
+   set are empty.  */
+
+static int
+set_is_empty (VEC (itset_elt_ptr) *elements)
+{
+  int ix;
+  struct itset_elt *elt;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+    if (!elt->vtable->is_empty (elt))
+      return 0;
+
+  return 1;
+}
+
+/* A helper function that returns true if the inferior INF is
+   contained by the set ELEMENTS.  */
+
+static int
+set_contains_inferior (VEC (itset_elt_ptr) *elements, struct inferior *inf)
+{
+  int ix;
+  struct itset_elt *elt;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+    {
+      if (elt->vtable->contains_inferior (elt, inf))
+	return 1;
+    }
+
+  return 0;
+}
+
+/* A helper function that returns true if the thread THR is contained
+   by the set ELEMENTS.  */
+
+static int
+set_contains_thread (VEC (itset_elt_ptr) *elements, struct thread_info *thr)
+{
+  int ix;
+  struct itset_elt *elt;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+    {
+      if (elt->vtable->contains_thread (elt, thr))
+	return 1;
+    }
+
+  return 0;
+}
+
+/* A helper function to destroy all the elements in the set ELEMENTS.
+   This also destroys ELEMENTS itself.  */
+
+static void
+set_free (VEC (itset_elt_ptr) *elements)
+{
+  int ix;
+  struct itset_elt *elt;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, elements, ix, elt); ++ix)
+    itset_elt_free (elt);
+
+  VEC_free (itset_elt_ptr, elements);
+}
+
+\f
+
+/* An I/T set element representing all inferiors using a certain
+   executable.  */
+
+struct itset_elt_exec
+{
+  struct itset_elt base;
+
+  /* The name of the executable.  */
+  char *exec_name;
+};
+
+/* Implementation of `destroy' method.  */
+
+static void
+exec_destroy (struct itset_elt *base)
+{
+  struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+
+  xfree (exec->exec_name);
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+exec_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+
+  /* FIXME: smarter compare.  */
+  return (inf->pspace->ebfd != NULL
+	  && strcmp (exec->exec_name,
+		     bfd_get_filename (inf->pspace->ebfd)) == 0);
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+exec_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+  struct inferior *inf = get_thread_inferior (thr);
+
+  /* FIXME: smarter compare.  */
+  return (inf->pspace->ebfd != NULL
+	  && strcmp (exec->exec_name,
+		     bfd_get_filename (inf->pspace->ebfd)) == 0);
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+exec_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_exec *exec = (struct itset_elt_exec *) base;
+  struct inferior *inf;
+
+  ALL_INFERIORS (inf)
+    if (exec_contains_inferior (base, inf))
+      return 0;
+
+  return 1;
+}
+
+static const struct itset_elt_vtable exec_vtable =
+{
+  exec_destroy,
+  exec_contains_inferior,
+  exec_contains_thread,
+  exec_is_empty
+};
+
+/* Create a new `exec' I/T set element.  */
+
+static struct itset_elt *
+create_exec_itset (char *arg)
+{
+  struct itset_elt_exec *elt;
+
+  elt = XNEW (struct itset_elt_exec);
+  elt->base.vtable = &exec_vtable;
+  elt->exec_name = arg;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* The value representing any inferior or thread.  */
+
+#define WILDCARD -1
+
+/* An I/T set element representing a range of inferiors.  */
+
+struct itset_elt_inferior_range
+{
+  struct itset_elt base;
+
+  /* The first and last inferiors in this range.  If FIRST is
+     WILDCARD, then LAST is unused.  */
+  int inf_first, inf_last;
+};
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+inferior_range_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_inferior_range *range
+    = (struct itset_elt_inferior_range *) base;
+
+  if (range->inf_first == WILDCARD
+      || (range->inf_first <= inf->num && inf->num <= range->inf_last))
+    return 1;
+
+  return 0;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+inferior_range_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_inferior_range *range
+    = (struct itset_elt_inferior_range *) base;
+  struct inferior *inf;
+
+  if (range->inf_first == WILDCARD)
+    return 1;
+
+  inf = get_thread_inferior (thr);
+  return range->inf_first <= inf->num && inf->num <= range->inf_last;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+inferior_range_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_inferior_range *range
+    = (struct itset_elt_inferior_range *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_INFERIORS (inf)
+    {
+      if (inferior_range_contains_inferior (base, inf))
+	return 0;
+    }
+
+  ALL_THREADS (thr)
+    {
+      if (inferior_range_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable inferior_range_vtable =
+{
+  NULL,
+  inferior_range_contains_inferior,
+  inferior_range_contains_thread,
+  inferior_range_is_empty
+};
+
+/* Create a new `range' I/T set element.  */
+
+static struct itset_elt *
+create_inferior_range_itset (int inf_first, int inf_last)
+{
+  struct itset_elt_inferior_range *elt;
+
+  elt = XNEW (struct itset_elt_inferior_range);
+  elt->base.vtable = &inferior_range_vtable;
+  elt->inf_first = inf_first;
+  elt->inf_last = inf_last;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing a range of threads.  */
+
+struct itset_elt_thread_range
+{
+  struct itset_elt base;
+
+  /* The first and last threads in this range.  If FIRST is WILDCARD,
+     then LAST is unused.  */
+  int thr_first, thr_last;
+};
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+thread_range_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_thread_range *range
+    = (struct itset_elt_thread_range *) base;
+  struct thread_info *thr;
+  int pid;
+
+  /* If there are no threads in the inferior, INF can't be part of any
+     thread range.  */
+  if (inf->pid == 0)
+    return 0;
+
+  /* If range is a wildcard, this inferior is part of the range, given
+     that it must have at least one thread.  */
+  if (range->thr_first == WILDCARD)
+    return 1;
+
+  /* Walk threads of INF, check if the range contains any of those.
+     If so, then the range contains the inferior.  */
+  ALL_THREADS (thr)
+    if (ptid_get_pid (thr->ptid) == inf->pid)
+      {
+	if (range->thr_first <= thr->num && thr->num <= range->thr_last)
+	  return 1;
+      }
+
+  return 0;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+thread_range_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_thread_range *range
+    = (struct itset_elt_thread_range *) base;
+
+  if (range->thr_first == WILDCARD
+      || (range->thr_first <= thr->num && thr->num <= range->thr_last))
+    return 1;
+
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+thread_range_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_inferior_range *range
+    = (struct itset_elt_inferior_range *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_INFERIORS (inf)
+    {
+      if (thread_range_contains_inferior (base, inf))
+	return 0;
+    }
+
+  ALL_THREADS (thr)
+    {
+      if (thread_range_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable thread_range_vtable =
+{
+  NULL,
+  thread_range_contains_inferior,
+  thread_range_contains_thread,
+  thread_range_is_empty
+};
+
+/* Create a new `range' I/T set element.  */
+
+static struct itset_elt *
+create_thread_range_itset (int thr_first, int thr_last)
+{
+  struct itset_elt_thread_range *elt;
+
+  elt = XNEW (struct itset_elt_thread_range);
+  elt->base.vtable = &thread_range_vtable;
+  elt->thr_first = thr_first;
+  elt->thr_last = thr_last;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing a range of cores.  */
+
+struct itset_elt_core_range
+{
+  struct itset_elt base;
+
+  /* The first and last cores in this range.  If CORE_FIRST is
+     WILDCARD, then CORE_LAST is unused.  */
+  int core_first, core_last;
+};
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+core_range_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_core_range *core_range = (struct itset_elt_core_range *) base;
+  int core;
+
+  if (core_range->core_first == WILDCARD)
+    return 1;
+
+  core = target_core_of_thread (thr->ptid);
+  if (core_range->core_first <= core && core <= core_range->core_last)
+    return 1;
+
+  return 0;
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+core_range_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_core_range *core_range = (struct itset_elt_core_range *) base;
+  struct thread_info *thr;
+
+  /* True if we find a thread of this inferior that is running on our
+     core range.  */
+  ALL_THREADS (thr)
+    {
+      /* It's cheaper to check the core range first, because looking
+	 up the a thread's inferior is O(n).  */
+      if (core_range_contains_thread (base, thr))
+	{
+	  struct inferior *thr_inf;
+
+	  thr_inf = get_thread_inferior (thr);
+	  if (thr_inf == inf)
+	    return 1;
+	}
+    }
+
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+core_range_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_core_range *core_range = (struct itset_elt_core_range *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_THREADS (thr)
+    {
+      if (core_range_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable core_range_vtable =
+{
+  NULL,
+  core_range_contains_inferior,
+  core_range_contains_thread,
+  core_range_is_empty
+};
+
+/* Create a new `core_range' I/T set element.  */
+
+static struct itset_elt *
+create_core_range_itset (int core_first, int core_last)
+{
+  struct itset_elt_core_range *elt;
+
+  elt = XNEW (struct itset_elt_core_range);
+  elt->base.vtable = &core_range_vtable;
+  elt->core_first = core_first;
+  elt->core_last = core_last;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing an intersection of sets.  */
+
+struct itset_elt_intersect
+{
+  struct itset_elt base;
+
+  /* The elements that will be intersected.  */
+  VEC (itset_elt_ptr) *elements;
+};
+
+/* Implementation of `destroy' method.  */
+
+static void
+intersect_destroy (struct itset_elt *base)
+{
+  struct itset_elt_intersect *set = (struct itset_elt_intersect *) base;
+
+  VEC_free (itset_elt_ptr, set->elements);
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+intersect_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+  struct itset_elt *elt;
+  int ix;
+
+  gdb_assert (!VEC_empty (itset_elt_ptr, intersect->elements));
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, intersect->elements, ix, elt); ++ix)
+    {
+      if (!elt->vtable->contains_inferior (elt, inf))
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+intersect_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+  struct itset_elt *elt;
+  int ix;
+
+  gdb_assert (!VEC_empty (itset_elt_ptr, intersect->elements));
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, intersect->elements, ix, elt); ++ix)
+    {
+      if (!elt->vtable->contains_thread (elt, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+intersect_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_intersect *intersect = (struct itset_elt_intersect *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_INFERIORS (inf)
+    {
+      if (intersect_contains_inferior (base, inf))
+	return 0;
+    }
+
+  ALL_THREADS (thr)
+    {
+      if (intersect_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable intersect_vtable =
+{
+  intersect_destroy,
+  intersect_contains_inferior,
+  intersect_contains_thread,
+  intersect_is_empty
+};
+
+/* Create a new `intersect' I/T set element.  */
+
+static struct itset_elt_intersect *
+create_intersect_itset (void)
+{
+  struct itset_elt_intersect *elt;
+
+  elt = XNEW (struct itset_elt_intersect);
+  elt->base.vtable = &intersect_vtable;
+  elt->elements = NULL;
+
+  return elt;
+}
+
+\f
+
+/* An I/T set element representing all inferiors.  */
+
+struct itset_elt_all
+{
+  struct itset_elt base;
+};
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+all_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  return 1;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+all_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  return 1;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+all_is_empty (struct itset_elt *base)
+{
+  /* There's always at least one inferior.  */
+  return 0;
+}
+
+static const struct itset_elt_vtable all_vtable =
+{
+  NULL,
+  all_contains_inferior,
+  all_contains_thread,
+  all_is_empty
+};
+
+static struct itset_elt *
+create_all_itset (void)
+{
+  struct itset_elt_all *elt;
+
+  elt = XNEW (struct itset_elt_all);
+  elt->base.vtable = &all_vtable;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing no inferiors.  */
+
+struct itset_elt_empty
+{
+  struct itset_elt base;
+};
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+empty_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  return 0;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+empty_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+empty_is_empty (struct itset_elt *base)
+{
+  /* Always empty.  */
+  return 1;
+}
+
+static const struct itset_elt_vtable empty_vtable =
+{
+  NULL,
+  empty_contains_inferior,
+  empty_contains_thread,
+  empty_is_empty
+};
+
+static struct itset_elt *
+create_empty_itset (void)
+{
+  struct itset_elt_empty *elt;
+
+  elt = XNEW (struct itset_elt_empty);
+  elt->base.vtable = &empty_vtable;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* An I/T set element representing an itset.  */
+
+struct itset_elt_itset
+{
+  struct itset_elt base;
+
+  /* The I/T set this element wraps.  */
+  struct itset *set;
+};
+
+static void
+itset_elt_itset_destroy (struct itset_elt *base)
+{
+  struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+
+  itset_free (iiset->set);
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+itset_elt_itset_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+  return itset_contains_inferior (iiset->set, inf);
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+itset_elt_itset_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+  return itset_contains_thread (iiset->set, thr);
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+itset_elt_itset_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_itset *iiset = (struct itset_elt_itset *) base;
+  return itset_is_empty (iiset->set);
+}
+
+static const struct itset_elt_vtable itset_elt_itset_vtable =
+{
+  itset_elt_itset_destroy,
+  itset_elt_itset_contains_inferior,
+  itset_elt_itset_contains_thread,
+  itset_elt_itset_is_empty
+};
+
+static struct itset_elt_itset *
+create_itset_elt_itset (struct itset *set)
+{
+  struct itset_elt_itset *elt;
+
+  elt = XNEW (struct itset_elt_itset);
+  elt->base.vtable = &itset_elt_itset_vtable;
+  elt->set = itset_reference (set);
+
+  return elt;
+}
+
+void
+itset_add_set (struct itset *to, struct itset *addme)
+{
+  struct itset_elt *elt;
+
+  elt = (struct itset_elt *) create_itset_elt_itset (addme);
+  VEC_safe_push (itset_elt_ptr, to->elements, elt);
+}
+
+\f
+
+/* An I/T set element representing a negated set.  */
+
+struct itset_elt_negated
+{
+  struct itset_elt base;
+
+  /* The negated element.  */
+  struct itset_elt *negated;
+};
+
+static void
+itset_elt_negated_destroy (struct itset_elt *base)
+{
+  struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+
+  if (elt->negated->vtable->destroy != NULL)
+    elt->negated->vtable->destroy (elt->negated);
+  xfree (elt->negated);
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+itset_elt_negated_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+  return !elt->negated->vtable->contains_inferior (elt->negated, inf);
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+itset_elt_negated_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+  return !elt->negated->vtable->contains_thread (elt->negated, thr);
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+itset_elt_negated_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_negated *elt = (struct itset_elt_negated *) base;
+  struct inferior *inf;
+  struct thread_info *thr;
+
+  ALL_INFERIORS (inf)
+    {
+      if (itset_elt_negated_contains_inferior (base, inf))
+	return 0;
+    }
+
+  ALL_THREADS (thr)
+    {
+      if (itset_elt_negated_contains_thread (base, thr))
+	return 0;
+    }
+
+  return 1;
+}
+
+static const struct itset_elt_vtable itset_elt_negated_vtable =
+{
+  itset_elt_negated_destroy,
+  itset_elt_negated_contains_inferior,
+  itset_elt_negated_contains_thread,
+  itset_elt_negated_is_empty
+};
+
+static struct itset_elt_negated *
+create_itset_elt_negated (void)
+{
+  struct itset_elt_negated *elt;
+
+  elt = XNEW (struct itset_elt_negated);
+  elt->base.vtable = &itset_elt_negated_vtable;
+  elt->negated = NULL;
+
+  return elt;
+}
+
+\f
+
+/* An I/T set element representing all inferiors of a given state.  */
+
+struct itset_elt_state
+{
+  struct itset_elt base;
+
+  int state;
+};
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+state_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_state *state = (struct itset_elt_state *) base;
+
+  return thr->state == state->state;
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+state_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_state *state = (struct itset_elt_state *) base;
+  struct thread_info *thr;
+
+  /* True if we find a thread of this inferior that is in the state
+     we're interested in.  */
+  ALL_THREADS (thr)
+    {
+      /* It's cheaper to check the state first, because looking up the
+	 a thread's inferior is O(n).  */
+      if (state_contains_thread (base, thr))
+	{
+	  struct inferior *thr_inf;
+
+	  thr_inf = get_thread_inferior (thr);
+	  if (thr_inf == inf)
+	    return 1;
+	}
+    }
+
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+state_is_empty (struct itset_elt *base)
+{
+  struct thread_info *thr;
+
+  ALL_THREADS (thr)
+    if (state_contains_thread (base, thr))
+      return 0;
+
+  return 1;
+}
+
+static const struct itset_elt_vtable state_vtable =
+{
+  NULL,
+  state_contains_inferior,
+  state_contains_thread,
+  state_is_empty
+};
+
+static struct itset_elt *
+create_state_itset (int thread_state)
+{
+  struct itset_elt_state *elt;
+
+  elt = XNEW (struct itset_elt_state);
+  elt->base.vtable = &state_vtable;
+  elt->state = thread_state;
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+current_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  return current_inferior () == inf;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+current_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct inferior *inf;
+
+  inf = get_thread_inferior (thr);
+  return current_inferior () == inf;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+current_is_empty (struct itset_elt *base)
+{
+  /* There's always a current inferior.  */
+  return 0;
+}
+
+static const struct itset_elt_vtable current_vtable =
+{
+  NULL,
+  current_contains_inferior,
+  current_contains_thread,
+  current_is_empty
+};
+
+/* Create a new I/T set element representing just the current
+   inferior.  */
+
+static struct itset_elt *
+create_current_itset (void)
+{
+  struct itset_elt *elt;
+
+  elt = XNEW (struct itset_elt);
+  elt->vtable = &current_vtable;
+
+  return elt;
+}
+
+\f
+
+/* An I/T set element representing a static list of inferiors.  */
+
+struct itset_elt_static
+{
+  struct itset_elt base;
+
+  /* The inferiors.  */
+  VEC (int) *inferiors;
+
+  /* The threads.  */
+  VEC (int) *threads;
+};
+
+/* Implementation of `destroy' method.  */
+
+static void
+static_destroy (struct itset_elt *base)
+{
+  struct itset_elt_static *st = (struct itset_elt_static *) base;
+
+  VEC_free (int, st->inferiors);
+  VEC_free (int, st->threads);
+}
+
+/* Helper function to compare two ints.  Returns true if the first
+   argument is strictly less than the second, useful for
+   VEC_lower_bound.  */
+
+static int
+static_lessthan (const int a, const int b)
+{
+  return a < b;
+}
+
+/* Implementation of `contains_inferior' method.  */
+
+static int
+static_contains_inferior (struct itset_elt *base, struct inferior *inf)
+{
+  struct itset_elt_static *st = (struct itset_elt_static *) base;
+  int idx;
+
+  idx = VEC_lower_bound (int, st->inferiors, inf->num, static_lessthan);
+  if (idx < VEC_length (int, st->inferiors)
+      && VEC_index (int, st->inferiors, idx) == inf->num)
+    return 1;
+  return 0;
+}
+
+/* Implementation of `contains_thread' method.  */
+
+static int
+static_contains_thread (struct itset_elt *base, struct thread_info *thr)
+{
+  struct itset_elt_static *st = (struct itset_elt_static *) base;
+  int idx;
+
+  idx = VEC_lower_bound (int, st->threads, thr->num, static_lessthan);
+  if (idx < VEC_length (int, st->threads)
+      && VEC_index (int, st->threads, idx) == thr->num)
+    return 1;
+  return 0;
+}
+
+/* Implementation of `is_empty' method.  */
+
+static int
+static_is_empty (struct itset_elt *base)
+{
+  struct itset_elt_static *st = (struct itset_elt_static *) base;
+  int idx;
+
+  return VEC_empty (int, st->inferiors);
+}
+
+static const struct itset_elt_vtable static_vtable =
+{
+  static_destroy,
+  static_contains_inferior,
+  static_contains_thread,
+  static_is_empty
+};
+
+\f
+
+/* Helper struct used to pass data through iterate_over_inferiors.  */
+
+struct iter_data
+{
+  /* The I/T set we are constructing.  */
+
+  struct itset_elt_static *st;
+
+  /* The elements of the original (dynamic) I/T set.  */
+
+  VEC (itset_elt_ptr) *elements;
+};
+
+/* A callback for iterate_over_inferiors that adds an inferior to the
+   result set, if it is in the source set.  */
+
+static int
+check_one_inferior (struct inferior *inf, void *datum)
+{
+  struct iter_data *id = datum;
+
+  if (set_contains_inferior (id->elements, inf))
+    VEC_safe_push (int, id->st->inferiors, inf->num);
+
+  /* Keep going.  */
+  return 0;
+}
+
+/* A callback for iterate_over_threads that adds a thread to the
+   result set, if it is in the source set.  */
+
+static int
+check_one_thread (struct thread_info *thr, void *datum)
+{
+  struct iter_data *id = datum;
+
+  if (set_contains_thread (id->elements, thr))
+    VEC_safe_push (int, id->st->threads, thr->num);
+
+  /* Keep going.  */
+  return 0;
+}
+
+/* Create a new static I/T set from the list of elements.  */
+
+static struct itset_elt *
+create_static_itset (VEC (itset_elt_ptr) *elements)
+{
+  struct itset_elt_static *elt;
+  struct iter_data datum;
+
+  elt = XNEW (struct itset_elt_static);
+  elt->base.vtable = &static_vtable;
+  elt->inferiors = NULL;
+  elt->threads = NULL;
+
+  datum.st = elt;
+  datum.elements = elements;
+
+  iterate_over_inferiors (check_one_inferior, &datum);
+  if (VEC_length (int, elt->inferiors) > 1)
+    qsort (VEC_address (int, elt->inferiors),
+	   VEC_length (int, elt->inferiors),
+	   sizeof (int), compare_positive_ints);
+
+  iterate_over_threads (check_one_thread, &datum);
+  if (VEC_length (int, elt->threads) > 1)
+    qsort (VEC_address (int, elt->threads),
+	   VEC_length (int, elt->threads),
+	   sizeof (int), compare_positive_ints);
+
+  return (struct itset_elt *) elt;
+}
+
+\f
+
+static int
+looks_like_range (char *spec)
+{
+  return isdigit (spec[0]) || spec[0] == '*';
+}
+
+/* Parse an I/T set range.  A range has the form F[:L][.T], where F is
+   the starting inferior, L is the ending inferior, and T is the
+   thread.  Updates RESULT with the new I/T set elements, and returns
+   an updated pointer into the spec.  Throws an exception on
+   error.  */
+
+static char *
+parse_range (char *spec, int *first, int *last)
+{
+  struct itset_elt *elt;
+
+  if (!looks_like_range (spec))
+    error (_("Invalid I/T syntax at `%s'.  Expected '*' or digit."), spec);
+
+  if (*spec == '*')
+    {
+      *first = WILDCARD;
+      *last = WILDCARD;
+      ++spec;
+    }
+  else
+    {
+      *first = strtol (spec, &spec, 10);
+      if (*spec == '-')
+	{
+	  ++spec;
+	  if (!isdigit (*spec))
+	    error (_("Expected digit in I/T set, at `%s'"), spec);
+	  *last = strtol (spec, &spec, 10);
+	}
+      else
+	*last = *first;
+    }
+  return spec;
+}
+
+static struct itset_elt *
+parse_inferior_range (char **spec)
+{
+  int first, last;
+
+  if ((*spec)[0] != 'i' || !looks_like_range ((*spec) + 1))
+    return NULL;
+
+  (*spec)++;
+  *spec = parse_range (*spec, &first, &last);
+  return create_inferior_range_itset (first, last);
+}
+
+static struct itset_elt *
+parse_thread_range (char **spec)
+{
+  int first, last;
+
+  if ((*spec)[0] != 't' || !looks_like_range ((*spec) + 1))
+    return NULL;
+
+  (*spec)++;
+  *spec = parse_range (*spec, &first, &last);
+  return create_thread_range_itset (first, last);
+}
+
+static struct itset_elt *
+parse_core_range (char **spec)
+{
+  int first, last;
+
+  if ((*spec)[0] != 'c' || !looks_like_range ((*spec) + 1))
+    return NULL;
+
+  (*spec)++;
+  *spec = parse_range (*spec, &first, &last);
+  return create_core_range_itset (first, last);
+}
+
+\f
+
+/* Parse a named I/T set.  Currently the only named sets which are
+   recognized are `exec (NAME)', and `current'.  Updates RESULT with
+   the new I/T set elements, and returns an updated pointer into the
+   spec.  Throws an exception on error.  */
+
+struct itset_elt *
+parse_named_or_throw (char **textp)
+{
+  struct itset_elt *elt;
+  char *text = *textp;
+  char *name = text;
+
+  for (text = name + 1; isalnum (*text) || *text == '_'; ++text)
+    ;
+
+  if (strncmp ("all", name, text - name) == 0)
+    elt = create_all_itset ();
+  else if (strncmp ("empty", name, text - name) == 0)
+    elt = create_empty_itset ();
+  else if (strncmp ("stopped", name, text - name) == 0)
+    elt = create_state_itset (THREAD_STOPPED);
+  else if (strncmp ("running", name, text - name) == 0)
+    elt = create_state_itset (THREAD_RUNNING);
+  else if (strncmp ("curinf", name, text - name) == 0)
+    elt = create_current_itset ();
+  else if (strncmp ("exec", name, text - name) == 0)
+    {
+      char *tem;
+      char *arg;
+
+      if (*text != '(')
+	error (_("'(' expected in I/T set after `exec'"));
+      text = skip_spaces (text + 1);
+      tem = strchr (text, ')');
+      if (!tem)
+	error (_("No closing ')' in I/T set for `exec'"));
+      if (tem - text == 0)
+	error (_("Empty argument to `exec' in I/T set"));
+      arg = xstrndup (text, tem - text);
+      text = tem + 1;
+      elt = create_exec_itset (arg);
+    }
+  else
+    {
+      struct named_itset *named_itset;
+      char *tem;
+
+      tem = alloca (text - name + 1);
+
+      memcpy (tem, name, text - name);
+      tem[text - name] = '\0';
+
+      named_itset = get_named_itset (tem);
+      if (named_itset == NULL)
+	error (_("Unknown named I/T set: `%s'"), tem);
+      elt = (struct itset_elt *) create_itset_elt_itset (named_itset->set);
+    }
+
+  *textp = text;
+  return elt;
+}
+
+/* A cleanup function that calls itset_free.  */
+
+static void
+itset_free_cleanup (void *arg)
+{
+  struct itset *itset = arg;
+  itset_free (itset);
+}
+
+struct cleanup *
+make_cleanup_itset_free (struct itset *itset)
+{
+  return make_cleanup (itset_free_cleanup, itset);
+}
+
+/*
+
+  ',' (union) has precedence over '.' (intersect).
+
+  ELEM = RANGE | NEG | PARENS_SET | '$' | NAME
+  NEG = '~' ELEM
+  PARENS_SET = '(' ITSET_ONE ')'
+  INTERS = ELEM ('.' ELEM)*
+  ITSET_ONE = INTERS (',' INTERS)*
+  ITSET = | ('!' ITSET_ONE) | ITSET_ONE
+
+  E.g.,:
+
+  c1-3.~i1.(t1,other),foo is transformed to a tree like:
+
+   UNION
+     foo
+     INTERS
+       c1-3
+       NEG
+         i1-1
+       UNION
+         t1-1
+	 other
+
+*/
+
+/* Forward declare.  The parser is recursive.  */
+static struct itset_elt *parse_neg (char **spec);
+static struct itset_elt *parse_parens_set (char **spec);
+static struct itset_elt *parse_itset_one (char **spec);
+static struct itset_elt *parse_current_focus (char **spec);
+
+static int
+valid_spec_end (char *spec)
+{
+  return *spec == '\0' || isspace (*spec);
+}
+
+static struct itset_elt *
+parse_elem (char **spec)
+{
+  struct itset_elt *elt;
+
+  elt = parse_inferior_range (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_thread_range (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_core_range (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_neg (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_parens_set (spec);
+  if (elt != NULL)
+    return elt;
+
+  elt = parse_current_focus (spec);
+  if (elt != NULL)
+    return elt;
+
+  return parse_named_or_throw (spec);
+}
+
+static struct itset_elt *
+parse_neg (char **spec)
+{
+  struct itset_elt_negated *neg_elt;
+  struct itset_elt *elt;
+
+  if (**spec != '~')
+    return NULL;
+
+  (*spec)++;
+
+  elt = parse_elem (spec);
+
+  neg_elt = create_itset_elt_negated ();
+  neg_elt->negated = elt;
+
+  return (struct itset_elt *) neg_elt;
+}
+
+static struct itset_elt *
+parse_current_focus (char **spec)
+{
+  struct itset_elt_itset *itset_elt;
+
+  if (**spec != '$')
+    return NULL;
+
+  (*spec)++;
+
+  itset_elt = create_itset_elt_itset (itset_reference (current_itset));
+  return (struct itset_elt *) itset_elt;
+}
+
+static struct itset_elt *
+parse_parens_set (char **spec)
+{
+  struct itset_elt_negated *neg_elt;
+  struct itset_elt *elt;
+  struct cleanup *old_chain;
+
+  if (**spec != '(')
+    return NULL;
+  (*spec)++;
+
+  elt = parse_itset_one (spec);
+  if (elt == NULL)
+    error (_("Invalid I/T syntax at `%s'"), *spec);
+
+  old_chain = make_cleanup_itset_elt_free (elt);
+  if (**spec != ')')
+    error (_("Invalid I/T syntax at `%s'"), *spec);
+
+  neg_elt = create_itset_elt_negated ();
+  neg_elt->negated = elt;
+
+  discard_cleanups (old_chain);
+
+  return (struct itset_elt *) neg_elt;
+}
+
+static struct itset_elt *
+parse_inters (char **spec)
+{
+  struct itset_elt *elt1, *elt2 = NULL;
+  struct itset_elt_intersect *intersect = NULL;
+  struct cleanup *old_chain;
+
+  elt1 = parse_elem (spec);
+  if (elt1 == NULL)
+    return NULL;
+
+  old_chain = make_cleanup_itset_elt_free (elt1);
+
+  if (**spec == '.')
+    {
+      intersect = create_intersect_itset ();
+      VEC_safe_push (itset_elt_ptr, intersect->elements, elt1);
+      elt1 = (struct itset_elt *) intersect;
+
+      discard_cleanups (old_chain);
+      old_chain = make_cleanup_itset_elt_free (elt1);
+    }
+
+  while (**spec == '.')
+    {
+      (*spec)++;
+
+      elt2 = parse_elem (spec);
+      if (elt2 == NULL)
+	{
+	  do_cleanups (old_chain);
+	  return NULL;
+	}
+      VEC_safe_push (itset_elt_ptr, intersect->elements, elt2);
+    }
+
+  discard_cleanups (old_chain);
+  return elt1;
+}
+
+static struct itset_elt *
+parse_itset_one (char **spec)
+{
+  struct itset_elt *inters1, *inters2 = NULL;
+  struct itset_elt_itset *un = NULL;
+  struct cleanup *old_chain;
+
+  inters1 = parse_inters (spec);
+  if (inters1 == NULL)
+    return NULL;
+  old_chain = make_cleanup_itset_elt_free (inters1);
+
+  if (**spec == ',')
+    {
+      struct itset *set;
+
+      set = XCNEW (struct itset);
+      set->refc = 1;
+
+      un = create_itset_elt_itset (set);
+
+      VEC_safe_push (itset_elt_ptr, set->elements, inters1);
+      inters1 = (struct itset_elt *) un;
+
+      discard_cleanups (old_chain);
+      old_chain = make_cleanup_itset_elt_free (inters1);
+    }
+
+  while (**spec == ',')
+    {
+      (*spec)++;
+
+      inters2 = parse_inters (spec);
+      if (inters2 == NULL)
+	{
+	  do_cleanups (old_chain);
+	  return NULL;
+	}
+      VEC_safe_push (itset_elt_ptr, un->set->elements, inters2);
+    }
+
+  discard_cleanups (old_chain);
+  return inters1;
+}
+
+/* Parse an I/T set specification and return a new I/T set.  Throws an
+   exception on error.  */
+
+struct itset *
+itset_create (char **specp)
+{
+  int is_static = 0;
+  struct itset *result;
+  struct itset_elt *elt;
+  struct cleanup *cleanups;
+  char *spec = *specp;
+  char *spec_start;
+
+  result = XCNEW (struct itset);
+  result->refc = 1;
+
+  cleanups = make_cleanup_itset_free (result);
+
+  spec = skip_spaces (spec);
+  spec_start = spec;
+
+  if (*spec == '!')
+    {
+      is_static = 1;
+      ++spec;
+    }
+
+  if (!valid_spec_end (spec))
+    {
+      elt = parse_itset_one (&spec);
+      VEC_safe_push (itset_elt_ptr, result->elements, elt);
+
+      if (!valid_spec_end (spec))
+	error (_("Invalid I/T syntax at `%s'"), spec);
+    }
+
+  result->spec = xstrndup (spec_start, spec - spec_start);
+  *specp = spec;
+
+  if (is_static)
+    {
+      struct itset_elt *st = create_static_itset (result->elements);
+
+      set_free (result->elements);
+      result->elements = NULL;
+      VEC_safe_push (itset_elt_ptr, result->elements, st);
+    }
+
+  discard_cleanups (cleanups);
+
+  return result;
+}
+
+struct itset *
+itset_create_empty (void)
+{
+  char *spec = "";
+
+  return itset_create (&spec);
+}
+
+/* Create a new I/T set which represents the current inferior and all
+   its threads.  */
+
+static struct itset *
+itset_create_curinf (void)
+{
+  char *spec = "curinf";
+
+  return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_all (void)
+{
+  char *spec = "all";
+
+  return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_running (void)
+{
+  char *spec = "running";
+
+  return itset_create (&spec);
+}
+
+static struct itset *
+itset_create_stopped (void)
+{
+  char *spec = "stopped";
+
+  return itset_create (&spec);
+}
+
+/* Return 1 if SET contains INF, 0 otherwise.  */
+
+int
+itset_is_empty (const struct itset *set)
+{
+  return set_is_empty (set->elements);
+}
+
+/* Return 1 if SET contains INF, 0 otherwise.  */
+
+int
+itset_contains_inferior (struct itset *set, struct inferior *inf)
+{
+  return set_contains_inferior (set->elements, inf);
+}
+
+/* Return 1 if SET contains THR, 0 otherwise.  */
+
+int
+itset_contains_thread (struct itset *set, struct thread_info *thr)
+{
+  return set_contains_thread (set->elements, thr);
+}
+
+/* Acquire a new reference to an I/T set.  */
+
+struct itset *
+itset_reference (struct itset *itset)
+{
+  ++itset->refc;
+  return itset;
+}
+
+/* Destroy SET.  */
+
+void
+itset_free (struct itset *set)
+{
+  /* Like xfree, allow NULL.  */
+  if (set == NULL)
+    return;
+
+  if (--set->refc == 0)
+    {
+      set_free (set->elements);
+      xfree (set->name);
+      xfree (set->spec);
+      xfree (set);
+    }
+}
+
+/* Helper struct for iterate_over_itset.  */
+
+struct iterate_data
+{
+  /* The I/T set we are using.  */
+  struct itset *itset;
+
+  /* The original callback  */
+  int (*callback) (struct inferior *, void *);
+
+  /* The data passed in to iterate_over_itset.  */
+  void *client_data;
+};
+
+/* Callback function for iterate_over_inferiors, used by
+   iterate_over_itset.  */
+
+static int
+iter_callback (struct inferior *inf, void *d)
+{
+  struct iterate_data *data = d;
+
+  if (itset_contains_inferior (data->itset, inf))
+    return data->callback (inf, data->client_data);
+
+  /* Keep going.  */
+  return 0;
+}
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+   in ITSET.  */
+
+struct inferior *
+iterate_over_itset_inferiors (struct itset *itset,
+			      itset_inf_callback_func *callback,
+			      void *datum)
+{
+  struct iterate_data data;
+
+  data.itset = itset;
+  data.callback = callback;
+  data.client_data = datum;
+
+  return iterate_over_inferiors (iter_callback, &data);
+}
+
+/* Helper struct for iterate_over_itset.  */
+
+struct iterate_thr_data
+{
+  /* The I/T set we are using.  */
+  struct itset *itset;
+
+  /* The original callback  */
+  int (*callback) (struct thread_info *, void *);
+
+  /* The data passed in to iterate_over_itset_threads.  */
+  void *client_data;
+};
+
+/* Callback function for iterate_over_inferiors, used by
+   iterate_over_itset.  */
+
+static int
+iter_thr_callback (struct thread_info *thr, void *d)
+{
+  struct iterate_thr_data *data = d;
+
+  if (itset_contains_thread (data->itset, thr))
+    return data->callback (thr, data->client_data);
+
+  /* Keep going.  */
+  return 0;
+}
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+   in ITSET.  */
+
+struct thread_info *
+iterate_over_itset_threads (struct itset *itset,
+			    int (*callback) (struct thread_info *, void *),
+			    void *datum)
+{
+  struct iterate_thr_data data;
+
+  data.itset = itset;
+  data.callback = callback;
+  data.client_data = datum;
+
+  return iterate_over_threads (iter_thr_callback, &data);
+}
+
+struct itset *current_itset = NULL;
+
+/* A cleanups callback, helper for save_current_itset
+   below.  */
+
+static void
+restore_itset (void *arg)
+{
+  struct itset *saved_itset = arg;
+
+  current_itset = saved_itset;
+  itset_free (current_itset);
+}
+
+/* Save the current itset so that it may be restored by a later call
+   to do_cleanups.  Returns the struct cleanup pointer needed for
+   later doing the cleanup.  */
+
+static struct cleanup *
+save_current_itset (void)
+{
+  struct cleanup *old_chain
+    = make_cleanup (restore_itset,
+		    itset_reference (current_itset));
+
+  return old_chain;
+}
+
+static int
+first_inferior (struct inferior *inf, void *data)
+{
+  return 1;
+}
+
+static int
+count_inferiors (struct inferior *inf, void *data)
+{
+  int *count_p = (int *) data;
+  (*count_p)++;
+  return 0;
+}
+
+static int
+first_thread_of (struct thread_info *thr, void *data)
+{
+  int inf_pid = * (int *) data;
+
+  if (ptid_get_pid (thr->ptid) != inf_pid)
+    return 0;
+
+  if (thr->state != THREAD_EXITED)
+    return 1;
+  return 0;
+}
+
+struct count_threads_arg
+{
+  int pid;
+  int count;
+};
+
+static int
+count_threads_of (struct thread_info *thr, void *data)
+{
+  struct count_threads_arg *c = data;
+
+  if (ptid_get_pid (thr->ptid) != c->pid)
+    return 0;
+
+  c->count++;
+  return 0;
+}
+
+static void
+switch_to_itset (struct itset *itset)
+{
+  struct inferior *inf;
+  struct cleanup *old_chain;
+  int inf_count;
+
+  /* For now, set a current inferior from the first element of the
+     focus set.  */
+  inf = iterate_over_itset_inferiors (itset, first_inferior, NULL);
+  if (inf == NULL)
+    {
+      /* Empty set.  Switch to no thread selected.  There's no concept
+	 of no inferior selected, so just leave the current
+	 selected.  */
+      switch_to_thread (null_ptid);
+      set_current_program_space (current_inferior ()->pspace);
+      return;
+    }
+
+  inf_count = 0;
+  iterate_over_itset_inferiors (itset, count_inferiors, &inf_count);
+  if (inf_count > 1)
+    warning ("\
+%d inferiors in the current i/t set, using inf %d to get current exec",
+	     inf_count, inf->num);
+
+  if (inf->pid != 0)
+    {
+      struct count_threads_arg count_threads_arg;
+
+      count_threads_arg.pid = inf->pid;
+      count_threads_arg.count = 0;
+      iterate_over_itset_threads (itset, count_threads_of, &count_threads_arg);
+      if (count_threads_arg.count != 0)
+	{
+	  struct thread_info *thr;
+
+	  if (count_threads_arg.count > 1)
+	    warning (_("\
+%d threads for inferior %d in the current i/t set, switching to first"),
+		     count_threads_arg.count, inf->num);
+	  thr = iterate_over_itset_threads (itset, first_thread_of, &inf->pid);
+	  switch_to_thread (thr->ptid);
+	}
+    }
+  else
+    {
+      set_current_inferior (inf);
+      switch_to_thread (null_ptid);
+      set_current_program_space (inf->pspace);
+    }
+}
+
+static void
+itfocus_command (char *spec, int from_tty)
+{
+  struct itset *itset;
+  struct cleanup *old_chain;
+
+  if (spec == NULL)
+    {
+      if (current_itset)
+	printf_filtered (_("Focus is `%s' (current inferior is %d)"),
+			 current_itset->spec,
+			 current_inferior ()->num);
+      else
+	printf_filtered (_("No focus has been set. (current inferior is %d)"),
+			 current_inferior ()->num);
+      printf_filtered ("\n");
+      return;
+    }
+
+  itset = itset_create (&spec);
+  old_chain = make_cleanup_itset_free (itset);
+
+  spec = skip_spaces (spec);
+  if (*spec != '\0')
+    {
+      save_current_itset ();
+      current_itset = itset;
+
+      old_chain = make_cleanup_restore_current_thread ();
+
+      switch_to_itset (itset);
+
+      execute_command (spec, from_tty);
+
+      do_cleanups (old_chain);
+      return;
+    }
+
+  if (itset_is_empty (itset))
+    warning (_("focus set is empty"));
+
+  discard_cleanups (old_chain);
+
+  itset_free (current_itset);
+  current_itset = itset;
+
+  switch_to_itset (itset);
+
+  /* Confirm the choice of focus.  */
+  printf_filtered (_("Current inferior is %d.\n"), current_inferior ()->num);
+}
+
+static struct named_itset *
+make_itset_named_itset (struct itset *set, char *name, int internal)
+{
+  struct named_itset *named_itset;
+
+  itset_reference (set);
+  set->name = name;
+
+  named_itset = XCNEW (struct named_itset);
+  named_itset->set = set;
+
+  if (internal)
+    named_itset->number = --internal_named_itset_count;
+  else
+    named_itset->number = ++named_itset_count;
+
+  return named_itset;
+}
+
+static int
+itset_elt_is_static (struct itset_elt *elt)
+{
+  return elt->vtable == &static_vtable;
+}
+
+static int
+itset_is_static (struct itset *itset)
+{
+  struct itset_elt *elt;
+  int ix;
+
+  /* True if all elements are static.  */
+
+  if (VEC_empty (itset_elt_ptr, itset->elements))
+    return 0;
+
+  for (ix = 0; VEC_iterate (itset_elt_ptr, itset->elements, ix, elt); ++ix)
+    if (!itset_elt_is_static (elt))
+      return 0;
+
+  return 1;
+}
+
+static void
+defset_command (char *arg, int from_tty)
+{
+  char *endp;
+  char *name;
+  char *spec;
+  struct itset *itset;
+  struct named_itset *named_itset;
+  struct cleanup *old_chain;
+
+  if (arg == NULL || *arg == '\0')
+    error_no_arg (_("no args"));
+
+  arg = skip_spaces (arg);
+
+  endp = skip_to_space (arg);
+  spec = endp;
+
+  name = xstrndup (arg, endp - arg);
+  old_chain = make_cleanup (xfree, name);
+
+  named_itset = get_named_itset (name);
+  if (named_itset != NULL)
+    error (_("itset %s already exists"), name);
+
+  spec = skip_spaces (spec);
+
+  itset = itset_create (&spec);
+  make_cleanup_itset_free (itset);
+
+  if (itset_is_static (itset) && itset_is_empty (itset))
+    warning (_("static itset is empty"));
+
+  named_itset = make_itset_named_itset (itset, name, 0);
+  itset_free (itset);
+  discard_cleanups (old_chain);
+  add_to_named_itset_chain (named_itset);
+}
+
+static void
+free_named_itset (struct named_itset *it)
+{
+  itset_free (it->set);
+  xfree (it);
+}
+
+static void
+undefset_command (char *arg, int from_tty)
+{
+  char *name;
+  struct named_itset *it, **it_link;
+  int found;
+
+  if (arg == NULL || *arg == '\0')
+    error_no_arg (_("no args"));
+
+  name = skip_spaces (arg);
+
+  if (strcmp (name, "-all") == 0)
+    {
+      it = named_itsets;
+      it_link = &named_itsets;
+      while (it != NULL)
+	{
+	  if (it->number > 0)
+	    {
+	      *it_link = it->next;
+	      free_named_itset (it);
+	    }
+	  else
+	    it_link = &it->next;
+	  it = *it_link;
+	}
+      return;
+    }
+
+  found = 0;
+  it = named_itsets;
+  it_link = &named_itsets;
+  while (it != NULL)
+    {
+      if (strcmp (it->set->name, name) == 0)
+	{
+	  if (it->number < 0)
+	    error (_("cannot delete builtin I/T set"));
+
+	  *it_link = it->next;
+	  free_named_itset (it);
+	  found = 1;
+	  break;
+	}
+
+      it_link = &it->next;
+      it = *it_link;
+    }
+
+  if (!found)
+    warning (_("itset %s does not exist"), name);
+}
+
+static void
+itsets_info (char *arg, int allflag, int from_tty)
+{
+  struct named_itset *e;
+  int num_printable_entries;
+  struct cleanup *tbl_chain;
+
+  /* Compute the number of rows in the table.  */
+  num_printable_entries = 0;
+  ALL_NAMED_ITSETS (e)
+    if (allflag
+	|| (e->number > 0
+	    && (arg == NULL || number_is_in_list (arg, e->number))))
+      num_printable_entries++;
+
+  if (num_printable_entries == 0)
+    {
+      if (arg == NULL)
+	ui_out_message (current_uiout, 0, _("No named itsets.\n"));
+      else
+	ui_out_message (current_uiout, 0,
+			_("No named itset found with number %s.\n"), arg);
+
+      return;
+    }
+
+  tbl_chain
+    = make_cleanup_ui_out_table_begin_end (current_uiout, 3,
+					   num_printable_entries,
+					   "NamedItsetListTable");
+
+  ui_out_table_header (current_uiout, 7, ui_left, "number", "Num");      /* 1 */
+  ui_out_table_header (current_uiout, 14, ui_left, "name", "Name");      /* 2 */
+  ui_out_table_header (current_uiout, 40, ui_noalign, "what", "What");   /* 3 */
+  ui_out_table_body (current_uiout);
+
+  ALL_NAMED_ITSETS (e)
+    if (allflag
+	|| (e->number > 0
+	    && (arg == NULL || number_is_in_list (arg, e->number))))
+      {
+	struct cleanup *entry_chain;
+
+	QUIT;
+
+	entry_chain
+	  = make_cleanup_ui_out_tuple_begin_end (current_uiout, "named-itset");
+	ui_out_field_int (current_uiout, "number", e->number);             /* 1 */
+	ui_out_field_string (current_uiout, "name", e->set->name);         /* 2 */
+	ui_out_field_string (current_uiout, "what", e->set->spec);         /* 3 */
+	ui_out_text (current_uiout, "\n");
+	do_cleanups (entry_chain);
+      }
+
+  do_cleanups (tbl_chain);
+}
+
+static void
+info_itsets_command (char *arg, int from_tty)
+{
+  itsets_info (arg, 0, from_tty);
+}
+
+static void
+maintenance_info_itsets_command (char *arg, int from_tty)
+{
+  itsets_info (arg, 1, from_tty);
+}
+
+static int
+whichsets_callback (struct thread_info *thr, void *data)
+{
+  struct named_itset *named_itset;
+  struct inferior *inf = get_thread_inferior (thr);
+  int printed = 0;
+
+  ALL_NAMED_ITSETS(named_itset)
+    {
+      QUIT;
+
+      if (itset_contains_thread (named_itset->set, thr))
+	{
+	  if (!printed)
+	    {
+	      printf_filtered (_("i%d.t%d (%s) is in:"),
+			       inf->num, thr->num,
+			       target_pid_to_str (thr->ptid));
+	      printf_filtered (" %s", itset_name (named_itset->set));
+	      printed = 1;
+	    }
+	  else
+	    printf_filtered (", %s", itset_name (named_itset->set));
+	}
+    }
+
+  if (printed)
+    printf_filtered ("\n");
+
+  return 0;
+}
+
+static void
+whichsets_command (char *arg, int from_tty)
+{
+  struct named_itset *named_itset;
+  struct itset *itset;
+  struct cleanup *old_chain;
+
+  if (arg == NULL)
+    {
+      /* No arg means all threads.  */
+      itset = itset_reference (current_itset);
+    }
+  else
+    {
+      arg = skip_spaces (arg);
+      itset = itset_create (&arg);
+    }
+
+  old_chain = make_cleanup_itset_free (itset);
+  iterate_over_itset_threads (itset, whichsets_callback, NULL);
+  do_cleanups (old_chain);
+}
+
+static void
+viewset (struct itset *itset)
+{
+  struct inferior *inf;
+  struct thread_info *thr;
+  int printed;
+
+  printf_filtered ("%s contains:\n",
+		   itset_name (itset) ? itset_name (itset) : itset_spec (itset));
+
+  printed = 0;
+  ALL_INFERIORS (inf)
+    {
+      if (itset_contains_inferior (itset, inf))
+	{
+	  if (!printed)
+	    {
+	      printf_filtered (_("  inferiors: %d"), inf->num);
+	      printed = 1;
+	    }
+	  else
+	    printf_filtered (", %d", inf->num);
+
+	}
+    }
+  if (printed)
+    printf_filtered ("\n");
+
+  printed = 0;
+  ALL_THREADS (thr)
+    {
+      if (itset_contains_thread (itset, thr))
+	{
+	  if (!printed)
+	    {
+	      printf_filtered (_("  threads: %d"), thr->num);
+	      printed = 1;
+	    }
+	  else
+	    printf_filtered (", %d", thr->num);
+	}
+    }
+  if (printed)
+    printf_filtered ("\n");
+
+  printed = 0;
+  ALL_THREADS (thr)
+    {
+      if (itset_contains_thread (itset, thr))
+	{
+	  int core;
+
+	  core = target_core_of_thread (thr->ptid);
+	  if (core == -1)
+	    break;
+
+	  if (!printed)
+	    {
+	      printf_filtered (_("  cores: %d"), core);
+	      printed = 1;
+	    }
+	  else
+	    printf_filtered (", %d", core);
+	}
+    }
+  if (printed)
+    printf_filtered ("\n");
+}
+
+static void
+viewset_command (char *arg, int from_tty)
+{
+  struct named_itset *named_itset;
+
+  if (arg == NULL)
+    {
+      struct named_itset *e;
+      struct itset *itset;
+
+      /* No arg means all debugger- and user-defined sets.  */
+      ALL_NAMED_ITSETS (named_itset)
+	viewset (named_itset->set);
+    }
+  else
+    {
+      struct itset *itset;
+      struct cleanup *old_chain;
+
+      arg = skip_spaces (arg);
+      itset = itset_create (&arg);
+      old_chain = make_cleanup_itset_free (itset);
+      viewset (itset);
+      do_cleanups (old_chain);
+    }
+}
+
+static void
+make_internal_itset (struct itset *itset, const char *name)
+{
+  struct named_itset *named_itset;
+
+  named_itset = make_itset_named_itset (itset, xstrdup (name), 1);
+  add_to_named_itset_chain (named_itset);
+}
+
+void
+_initialize_itset (void)
+{
+  struct cmd_list_element *c = NULL;
+
+  all_itset = itset_create_all ();
+  empty_itset = itset_create_empty ();
+  running_itset = itset_create_running ();
+  stopped_itset = itset_create_stopped ();
+  curinf_itset = itset_create_curinf ();
+
+  make_internal_itset (all_itset, "all");
+  make_internal_itset (empty_itset, "empty");
+  make_internal_itset (running_itset, "running");
+  make_internal_itset (stopped_itset, "stopped");
+  make_internal_itset (curinf_itset, "curinf");
+
+  current_itset = itset_reference (all_itset);
+
+  add_com ("itfocus", no_class, itfocus_command, _("\
+Change the set of current inferiors/threads."));
+
+  add_com ("defset", no_class, defset_command, _("\
+Define a new named set.\n\
+Usage: defset NAME SPEC"));
+
+  add_com ("undefset", no_class, undefset_command, _("\
+Undefine an existing named set.\n\
+Usage: undefset NAME | -all"));
+
+  add_com ("whichsets", no_class, whichsets_command, _("\
+List all sets to which threads in a given set belong to.\n\
+Usage: whichsets SET.\n\
+Defaults to the current set."));
+
+  add_com ("viewset", no_class, viewset_command, _("\
+List the members of a set.\n\
+Usage: viewset SET.\n\
+Defaults to all named sets."));
+
+  add_info ("itsets", info_itsets_command, _("\
+Display the list of defined named itsets.\n\
+You can specify numbers (e.g. \"info itsets 1 3\"),\n\
+ranges (e.g. \"info itsets 4-8\"), or both (e.g. \"info itsets 1 3 4-8\").\n\n\
+If you don't specify any numbers or ranges, we'll show all itsets.\n\n\
+Usage: info itsets [NUMBERS AND/OR RANGES]\n"));
+
+  add_cmd ("itsets", class_maintenance, maintenance_info_itsets_command, _("\
+Display the list of all defined named itsets, user-defined and built-in.\n"),
+  &maintenanceinfolist);
+}
diff --git a/gdb/itset.h b/gdb/itset.h
new file mode 100644
index 0000000..79ebe5b
--- /dev/null
+++ b/gdb/itset.h
@@ -0,0 +1,122 @@
+/* itset.h - Inferior/Thread sets.
+   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef ITSET_H
+#define ITSET_H
+
+struct inferior;
+struct thread_info;
+struct cleanup;
+
+/* This is an opaque type representing an I/T set.  An I/T set is
+   simply a set of inferiors and/or threads.  A set may be dynamic
+   (the members are enumerated at the time of use) or static (the
+   members are enumerated at the time of construction); but this
+   distinction is hidden from the callers.  An I/T set object is
+   reference counted.  */
+
+struct itset;
+
+/* Create a new I/T set from a user specification.  The valid forms of
+   a specification are documented in the manual.  *SPEC is the input
+   specification, and it is updated to point to the first non-space
+   character after the end of the specification.  */
+
+struct itset *itset_create (char **spec);
+
+/* Add ADDME to the I/T set TO.  In other words, after the call, TO
+   will be the union set of TO at entry, and ADDME.  */
+
+void itset_add_set (struct itset *to, struct itset *addme);
+
+/* Create an empty I/T set.  Usually, an empty set is the set that
+   matches nothing.  In some contexts, though, an empty set represents
+   some default.  */
+
+struct itset *itset_create_empty (void);
+
+/* Returns true if SET is the empty set.  That is a set whose spec is
+   either "" or "empty".  */
+
+int itset_is_empty_set (struct itset *set);
+
+/* Returns true if ITSET is empty.  That is, the set contains no
+   inferiors, threads, etc.  */
+
+int itset_is_empty (const struct itset *itset);
+
+/* Create a new dynamic I/T set which represents the current inferior,
+   at the time the I/T set if consulted.  */
+
+struct itset *itset_create_current (void);
+
+/* Like itset_create, but if *SPEC does not appear to be the start of
+   an I/T set, it will call itset_create_current and return the
+   result.  */
+
+struct itset *itset_create_or_default (char **spec);
+
+/* Return true if the inferior is contained in the I/T set.  */
+
+int itset_contains_inferior (struct itset *itset, struct inferior *inf);
+
+/* Return true if the thread is contained in the I/T set.  */
+
+int itset_contains_thread (struct itset *itset, struct thread_info *inf);
+
+/* Return true if the inferior is contained in the I/T set.  */
+
+int itset_member (struct itset *itset, int inf_id, int thread_id);
+
+/* Return a pointer to the I/T set's name.  Unnamed I/T sets have a
+   NULL name.  */
+
+const char *itset_name (const struct itset *itset);
+
+/* Return a pointer to the I/T set's spec.  */
+
+const char *itset_spec (const struct itset *itset);
+
+/* Acquire a new reference to an I/T set.  Returns the I/T set, for
+   convenience.  */
+
+struct itset *itset_reference (struct itset *itset);
+
+/* Release a reference to an I/T set.  */
+
+void itset_free (struct itset *itset);
+
+struct cleanup *make_cleanup_itset_free (struct itset *itset);
+
+/* A cleanup function that calls itset_free.  */
+
+void itset_cleanup (void *itset);
+
+/* Like iterate_over_inferiors, but iterate over only those inferiors
+   in ITSET.  */
+
+typedef int (itset_inf_callback_func) (struct inferior *, void *);
+struct inferior *iterate_over_itset_inferiors (struct itset *itset,
+					       itset_inf_callback_func *callback,
+					       void *data);
+
+/* The current I/T set.  */
+
+extern struct itset *current_itset;
+
+#endif /* ITSET_H */
diff --git a/gdb/thread.c b/gdb/thread.c
index 0777887..b42c8c3 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -74,6 +74,15 @@ inferior_thread (void)
   return tp;
 }
 
+struct inferior *
+get_thread_inferior (struct thread_info *thr)
+{
+  int pid;
+
+  pid = ptid_get_pid (thr->ptid);
+  return find_inferior_pid (pid);
+}
+
 void
 delete_step_resume_breakpoint (struct thread_info *tp)
 {


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

* [RFC/WIP PATCH v2 05/14] watchthreads-reorder.exp: Don't assume there's no infrun output after the prompt
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (9 preceding siblings ...)
  2011-12-17 12:35 ` [RFC/WIP PATCH v2 02/14] Implement all-stop on top of a target running non-stop mode Pedro Alves
@ 2011-12-17 12:37 ` Pedro Alves
  2011-12-17 12:37 ` [RFC/WIP PATCH v2 06/14] Add base itsets support Pedro Alves
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17 12:37 UTC (permalink / raw)
  To: gdb-patches

Like in the test of the previous patch, infrun debug output can still
appear after the prompt.  We should also turn off infrun debug output
after needing it, and before starting a new iteration.

v2:

 - new in v2.

gdb/testsuite/
2011-12-16  Pedro Alves  <pedro@codesourcery.com>

	* gdb.threads/watchthreads-reorder.exp: Don't assume there's no
	infrun output after the prompt.  Turn off infrun debug output at
	the end of each iteration.
---
 gdb/testsuite/gdb.threads/watchthreads-reorder.exp |   16 +++++++++++++---
 1 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/gdb/testsuite/gdb.threads/watchthreads-reorder.exp b/gdb/testsuite/gdb.threads/watchthreads-reorder.exp
index 38e8b74..751658d 100644
--- a/gdb/testsuite/gdb.threads/watchthreads-reorder.exp
+++ b/gdb/testsuite/gdb.threads/watchthreads-reorder.exp
@@ -92,11 +92,21 @@ foreach reorder {0 1} {
 	     "Hardware read watchpoint \[0-9\]+: thread\[12\]_rwatch\r\n\r\nValue = 0\r\n0x\[0-9a-f\]+ in thread\[12\]_func .*" \
 	     "continue b"
 
-    # While the debug output itself is not checked in this testcase one bug was
-    # found in the DEBUG_INFRUN code path.
+    # While the debug output itself is not checked in this testcase
+    # one bug was found in the DEBUG_INFRUN code path.  Note we can't
+    # assume there's no infrun debug output after the prompt.
     gdb_test "set debug infrun 1"
 
-    gdb_continue_to_breakpoint "break-at-exit" ".*break-at-exit.*"
+    set test "continue to breakpoint: break-at-exit"
+    gdb_test_multiple "continue" $test {
+	-re "Breakpoint .* (at|in) .*break-at-exit.*\r\n$gdb_prompt " {
+	    pass $test
+	}
+	-re "$gdb_prompt " {
+	    fail $test
+	}
+    }
+    gdb_test "set debug infrun 0"
 
     set pf_prefix $prefix_test
 }


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

* [RFC/WIP PATCH v2 09/14] Add I/T set support to most execution commands
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (11 preceding siblings ...)
  2011-12-17 12:37 ` [RFC/WIP PATCH v2 06/14] Add base itsets support Pedro Alves
@ 2011-12-17 12:38 ` Pedro Alves
  2011-12-17 14:05 ` [RFC/WIP PATCH v2 10/14] Parallel steps, wait for all threads to reach their goal Pedro Alves
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17 12:38 UTC (permalink / raw)
  To: gdb-patches

This adds I/T set support to most execution commands.  For execution
commands, there are actually two sets in effect:

- The "apply set":

This is the set of threads the execution command is applied to.  For
example, for "step", the apply set is the set of threads that will be
stepped; for "finish", the apply set is the set of threads that will
the finished.

- The "run free set":

This is the set of threads that is allowed to run free while the
execution command is applied to the all threads in the "apply set".
The run free set is derived from the current set (either as set by
itfocus, or by using itfocus as prefix command), and to remain
backwards compatible, from "set scheduler-locking on/step", "set
schedule-multiple" and "set non-stop on" too.  Example, if the current
set is `all' (all threads), by default the run free set is the `all'
set too.  But if either scheduler-locking is on/step or "set non-stop"
is on, the run free set is set to the empty set, meaning no thread is
let run free.

With these two sets, a user that is not aware of GDBs new
capabilities, does not notice any user interface change.  For example,
by default, "(gdb) step" will still step the current thread, and let
all other threads in the current inferior run free.

The pointer them comes from being able to override these new sets to
do more complicated commands.

For example, making use of the %ITSET% prompt substitution patch,

$ gdb -ex "set prompt %ITSET%> " ~/gdb/tests/threads
[all]> start
Temporary breakpoint 1 at 0x40068c: file threads.c, line 35.
Starting program: /home/pedro/gdb/tests/threads
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Temporary breakpoint 1,
main () at threads.c:35
35          long i = 0;
all> info threads
  Id   Target Id         Frame
  3    Thread 0x7ffff7028700 (LWP 22403) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0
* 2    Thread 0x7ffff7829700 (LWP 22402) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 22323) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6
all> itfocus i1
warning: 3 threads for inferior 1 in the current i/t set, switching to first
Current inferior is 1.
i1> info threads
  Id   Target Id         Frame
* 3    Thread 0x7ffff7028700 (LWP 22403) "threads" 0x00007ffff7bcc78e in __lll_lock_wait_private () from /lib/x86_64-linux-gnu/libpthread.so.0
  2    Thread 0x7ffff7829700 (LWP 22402) "threads" thread_function0 (arg=0x0) at threads.c:63
  1    Thread 0x7ffff7fcb720 (LWP 22323) "threads" 0x00007ffff7910011 in clone () from /lib/x86_64-linux-gnu/libc.so.6
i1> step

This last "step" command steps thread 3 (the apply set, derived from
the selected thread), and, lets all other threads of inferior 1 run
free (derived from the current focus).

You can step more than one thread in parallel.  For that, you pass an
explicit apply set as argument to the execution command.  For example:

i1> step -p t1,t2

That steps threads 1 and 2, and lets all other threads in the current
focus run free.  Currently each of threads in the apply set steps
independently and unaware of other threads in the same apply set.
That is, say, thread 1 finishes stepping first, and we'll give back
the prompt to the user immediately, while thread 2 continues stepping
in the background (as if "thread 1; step&; thread 2; step&" had been
issued).  A later patch in the series will make all threasd in the the
apply set to finish what they were told to do before considering the
whole command done with (and giving the prompt back to the user, in
case of a foreground command).

If you want to step all threads in the current focus in parallel,
although you could, you don't need to spell out the current focus set
as the apply set explicitly as in

i1> step i1

Instead, you can pass the -a (from "all") flag to the execution
command, as in:

i1> step -a

That is, this makes the apply set be equal to the current set, thus
stepping all threads in parallel.  The "run free set" is still the
current set, but, naturally, there are no threads left to run free.

Note you can make use of the itfocus command as prefix command to
temporarily switch the current set, so you can for example have
defined a set named `workers', and then step them all with:

i1> itfocus workers step -a


Often you'll want to step only a given thread or set of threads, and
not let any other thread run free while stepping.  That is, run a step
as if "set scheduler-locking" was step or on.  You can do that already
with what I've presented above, with e.g.,

i1> itfocus t2 step -p t2

That is, set the current set temporarily to thread 2, and the apply
set is also set to thread 2.

Or, you may also do:

i1> itfocus empty step -p t2

That is, the focus is set temporarily to the empty set, and the apply
set is thread 2.

Or, you may also do:

i1> itfocus empty step

where the current set is set to the empty set, and the apply set is
derived from the current thread.  I've added yet a third option (-l,
from lock) to get the same explicit locking.  E.g., if the current
thread is thread 2, then these are equivalent:

t2> step
i1> itfocus t2 step
i1> itfocus empty step
i1> step -l

they all step thread 2, with scheduler locking on.

Lastly, it'd be useful to have a way to override the global "set
scheduler-locking on/step" or "set non-stop on" options.  That is done
with the "-c" (from current set) option.  Maybe that that instead be
"-u" for unlocked?

So this:

all> set scheduler-locking on
all> step

steps the currently selected thread, and doesn't let any other thread
run free.

And this:

all> set scheduler-locking on
all> step -c

steps the currently selected thread, and lets all threads in `all' run
free.  This is the equivalent of:

all> set scheduler-locking off
all> step
all> set scheduler-locking on

Lastly, all these switches are currently implemented in the following commands:

step, next, stepi, nexti, continue, until, jump, signal, finish and interrupt.


You'll note that several of the commands' implementations have split
in two passes - the first pass loops over all threads in the apply
set, validating if everything is okay to apply the command, thus
avoiding most cases of resuming thread 1..3, and then later throwing
an error resuming thread N.

A lot is yet missing:

 - It'd be great to be able to come up with a way to merge the run
   free sets and the apply sets, and the current thread/inferior, into
   a single concept, thus simplifying the interface, mostly for when
   wanting to parallel debug some threads (need to pass -p SET to all
   commands, which is a bit awkward if you alternate commands.  E.g.,
   step -p t2,t3; next -p t2,t3; step -p t2,t3; etc.).  Extending the
   "selected thread" concept to "selected set" would help (e.g., allow
   "thread SET" instead of just "thread N"., though I'm not sure
   that's a good idea.

 - This whole series is almost regression free, but not quite.
   There's a nasty bug somewhere that causes sometimes threads to
   report spurious SIGTRAPs.  Seems related to displaced stepping.
   Reveals itself more often in the manythreads.exp test.

 - make sure that we're not regressing against a target that doesn't
   support non-stop/async.  I haven't tried that yet.

 - MI support.

 - We're always resuming all threads individually.  This is not really
   a problem for native targets, but if you're debugging 1000 threads
   against the remote target, and you do a "s", we'll send 1
   vCont;sTID packet, plus 999 vCont;cTID packets, instead of a single
   vCont;sTID;c".  We could make remote_resume not really resume, but
   collect resume requests in a vector, and add a new
   target_apply_resume method that actually applies all pending resume
   requests, so we could compress the requests.  Native targets would
   still resume from within target_resume, and would implement
   target_apply_resume as doing nothing.  The trouble is in knowing
   whether the 999 continue requests do indeed map to "all threads",
   and so can be compressed.  vCont;c is not really the same as
   resuming all threads individually.  The former means that we'll get
   the pending events for threads we hadn't heard yet too.  This needs
   a bit more thought and experimentation.  I'm not sure I'll be able
   to get this far in this first round though, so I may end up leaving
   all this native only at first somehow.

 - Comments! Tests! Docs! Help!

 - Probably many other things I'm still not remembering.

v2:

  - Adjustment to new itset spec scheme.
  - Many bug fixes.  Fix testsuite regressions.
  - Adjust MI to preserve backwards compatibility.


2011-12-16  Pedro Alves  <pedro@codesourcery.com>

	* breakpoint.c (struct until_break_cmd_data, struct
	until_break_aec_callback_data): New.
	(itset_free_p): New.
	(until_break_command): Handle new command args.  Handle the apply
	and run free sets.
	(until_break_aec_callback): New.
	* gdbthread.h (struct thread_info) <reported_event, cmd_data>: New
	fields.
	* infcall.c (run_inferior_call): Handle the apply and run free
	sets.
	* infcmd.c: Include itset.h and cli/cli-utils.h.
	(current_thread_set): New.
	(apply_execution_command): New.
	(run_command_1): Use target_is_non_stop_p.
	(proceed_thread_callback): Access thread state directly.  Use
	clear_proceed_status_thread.
	(itset_free_p): New.
	(default_run_free_itset): New.
	(parse_execution_args): New.
	(continue_aec_callback): New.
	(continue_command): Handle new command args.  Handle the apply and
	run free sets.
	(struct step_1_args): New.
	(step_1_aec_callback): New.
	(ensure_runnable): New.
	(step_1): Handle new command args.  Handle the apply and run free
	sets.
	(step_1_1): Factored out from step_1.
	(struct step_1_continuation_args): Deleted.
	(step_1_continuation): Adjust.
	(struct jump_cmd_data): New.
	(struct jump_aec_callback_data): New.
	(jump_aec_callback): New.
	(jump_command): Handle new command args.  Handle the apply and run
	free sets.
	(struct signal_aec_callback_data): New.
	(signal_command): Handle new command args.  Handle the apply and
	run free sets.
	(signal_aec_callback): New.
	(until_next_command): New parameter `arg'.  Handle new command
	args.  Handle the apply and run free sets.
	(until_next_aec_callback): New.
	(advance_command): Handle new command args.  Handle the apply and
	run free sets.
	(struct finish_cmd_data, struct finish_aec_callback_data): New.
	(finish_command): Handle new command args.  Handle the apply and
	run free sets.
	(finish_aec_callback): New.
	(interrupt_target_1): Adjust to handle sets.
	(interrupt_target_command): Allow -a in all-stop.
	* inferior.h (clear_proceed_status_thread): Declare.
	(schedlock_off, schedlock_on, schedlock_step, scheduler_mode):
	Declare.
	* infrun.c (follow_fork): Split in two: follow_fork, and ...
	(prepare_to_follow_fork): ... this.
	(schedlock_off, schedlock_on, schedlock_step, scheduler_mode):
	Make extern.
	(user_visible_resume_ptid): Use target_is_non_stop_p.
	(do_target_resume): Make extern.  No longer resume all threads
	when placed in the displaced stepping queue.
	(clear_proceed_status_thread): Make extern.
	(clear_proceed_status): Use target_is_non_stop_p.
	(proceed): Adjust.  Use target_is_non_stop_p.
	(delete_step_thread_step_resume_breakpoint, prepare_for_detach):
	Use target_is_non_stop_p.
	(fetch_inferior_event): Always switch to the event thread.  Use
	target_is_non_stop_p.
	(handle_inferior_event): Clear event_thread when mourning.
	Adjust.  Use target_is_non_stop_p.
	(stop_stepping): Mark the even thread as having reported an event.
	* mi/mi-interp.c (mi_on_resume): Be backwards compatible.
	* thread.c (free_thread): Free cmd_data.

	gdb/testsuite/
	* gdb.base/jump.exp: Adjust.
---
 gdb/breakpoint.c                |  178 ++++++--
 gdb/gdbthread.h                 |    5 
 gdb/infcall.c                   |   22 +
 gdb/infcmd.c                    |  889 +++++++++++++++++++++++++++++++--------
 gdb/inferior.h                  |    7 
 gdb/infrun.c                    |  114 +++--
 gdb/mi/mi-interp.c              |    6 
 gdb/testsuite/gdb.base/jump.exp |    2 
 gdb/thread.c                    |    2 
 9 files changed, 960 insertions(+), 265 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index effb53b..9df16b6 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -9678,64 +9678,161 @@ until_break_command_continuation (void *arg, int err)
   delete_longjmp_breakpoint (a->thread_num);
 }
 
+struct until_break_cmd_data
+{
+  struct symtab_and_line sal;
+  struct frame_id breakpoint_frame_id;
+  struct frame_id selected_frame_id;
+};
+
+struct until_break_aec_callback_data
+{
+  int from_tty;
+};
+
+static void until_break_aec_callback (struct thread_info *thread, void *data);
+
+static void
+itset_free_p (void *arg)
+{
+  struct itset **itset_p = arg;
+
+  if (*itset_p)
+    itset_free (*itset_p);
+}
+
+char *parse_execution_args (char *args, int step,
+			    struct itset **apply_set,
+			    int *apply_set_explicit,
+			    struct itset **run_free_set);
+
+typedef void (*aec_callback_func) (struct thread_info *thr, void *data);
+void apply_execution_command (struct itset *apply_itset,
+			      struct itset *run_free_itset,
+			      aec_callback_func callback, void *callback_data);
+
+void ensure_runnable (struct thread_info *thr);
+
 void
 until_break_command (char *arg, int from_tty, int anywhere)
 {
-  struct symtabs_and_lines sals;
-  struct symtab_and_line sal;
-  struct frame_info *frame = get_selected_frame (NULL);
-  struct breakpoint *breakpoint;
-  struct breakpoint *breakpoint2 = NULL;
+  struct itset *apply_itset = NULL;
+  int apply_itset_explicit = 0;
+  struct itset *run_free_itset = NULL;
+  struct thread_info *thr;
   struct cleanup *old_chain;
-  int thread;
-  struct thread_info *tp;
+  int thr_count = 0;
+  struct until_break_aec_callback_data cb_data;
 
-  clear_proceed_status ();
+  old_chain = make_cleanup (itset_free_p, &apply_itset);
+  make_cleanup (itset_free_p, &run_free_itset);
 
-  /* Set a breakpoint where the user wants it and at return from
-     this function.  */
+  arg = parse_execution_args (arg, 0,
+			      &apply_itset, &apply_itset_explicit,
+			      &run_free_itset);
 
-  if (last_displayed_sal_is_valid ())
-    sals = decode_line_1 (&arg, DECODE_LINE_FUNFIRSTLINE,
-			  get_last_displayed_symtab (),
-			  get_last_displayed_line ());
-  else
-    sals = decode_line_1 (&arg, DECODE_LINE_FUNFIRSTLINE,
-			  (struct symtab *) NULL, 0);
+  ALL_THREADS (thr)
+    if (itset_contains_thread (apply_itset, thr))
+      {
+	struct symtabs_and_lines sals;
+	struct symtab_and_line sal;
+	struct frame_info *frame;
+	struct frame_id breakpoint_frame_id;
+	struct until_break_cmd_data *cmd_data;
 
-  if (sals.nelts != 1)
-    error (_("Couldn't get information on specified line."));
+	++thr_count;
 
-  sal = sals.sals[0];
-  xfree (sals.sals);	/* malloc'd, so freed.  */
+	ensure_runnable (thr);
 
-  if (*arg)
-    error (_("Junk at end of arguments."));
+	if (!ptid_equal (inferior_ptid, thr->ptid))
+	  switch_to_thread (thr->ptid);
 
-  resolve_sal_pc (&sal);
+	frame = get_selected_frame (NULL);
 
-  if (anywhere)
-    /* If the user told us to continue until a specified location,
-       we don't specify a frame at which we need to stop.  */
-    breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal,
-					   null_frame_id, bp_until);
-  else
-    /* Otherwise, specify the selected frame, because we want to stop
-       only at the very same frame.  */
-    breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal,
-					   get_stack_frame_id (frame),
-					   bp_until);
+	/* Set a breakpoint where the user wants it and at return from
+	   this function.  */
 
+	if (last_displayed_sal_is_valid ())
+	  sals = decode_line_1 (&arg, DECODE_LINE_FUNFIRSTLINE,
+				get_last_displayed_symtab (),
+				get_last_displayed_line ());
+	else
+	  sals = decode_line_1 (&arg, DECODE_LINE_FUNFIRSTLINE,
+				(struct symtab *) NULL, 0);
+
+	if (sals.nelts != 1)
+	  error (_("Couldn't get information on specified line."));
+
+	sal = sals.sals[0];
+	xfree (sals.sals);	/* malloc'd, so freed.  */
+
+	if (*arg)
+	  error (_("Junk at end of arguments."));
+
+	resolve_sal_pc (&sal);
+
+	if (anywhere)
+	  /* If the user told us to continue until a specified
+	     location, we don't specify a frame at which we need to
+	     stop.  */
+	  breakpoint_frame_id = null_frame_id;
+	else
+	  /* Otherwise, specify the selected frame, because we want to
+	     stop only at the very same frame.  */
+	  breakpoint_frame_id = get_stack_frame_id (frame);
+
+	cmd_data = XNEW (struct until_break_cmd_data);
+	cmd_data->sal = sal;
+	cmd_data->breakpoint_frame_id = breakpoint_frame_id;
+	cmd_data->selected_frame_id = get_frame_id (frame);
+	thr->cmd_data = cmd_data;
+      }
+
+  if (thr_count == 0)
+    {
+      if (apply_itset_explicit)
+	error (_("Set of threads to until is empty."));
+      else
+	error (_("The program is not being run."));
+    }
+
+  cb_data.from_tty = from_tty;
+  apply_execution_command (apply_itset, run_free_itset,
+			   until_break_aec_callback, NULL);
+
+  do_cleanups (old_chain);
+}
+
+static void
+until_break_aec_callback (struct thread_info *thread, void *data)
+{
+  struct breakpoint *breakpoint;
+  struct breakpoint *breakpoint2 = NULL;
+  int thread_num;
+  struct until_break_aec_callback_data *d = data;
+  struct frame_info *frame;
+  struct until_break_cmd_data *cmd_data = thread->cmd_data;
+  int ix;
+  struct cleanup *old_chain;
+
+  frame = frame_find_by_id (cmd_data->selected_frame_id);
+  select_frame (frame);
+
+  breakpoint = set_momentary_breakpoint (get_frame_arch (frame),
+					 cmd_data->sal,
+					 cmd_data->breakpoint_frame_id,
+					 bp_until);
   old_chain = make_cleanup_delete_breakpoint (breakpoint);
 
-  tp = inferior_thread ();
-  thread = tp->num;
+  thread_num = thread->num;
 
   /* Keep within the current frame, or in frames called by the current
      one.  */
 
   if (frame_id_p (frame_unwind_caller_id (frame)))
     {
+      struct symtab_and_line sal;
+
       sal = find_pc_line (frame_unwind_caller_pc (frame), 0);
       sal.pc = frame_unwind_caller_pc (frame);
       breakpoint2 = set_momentary_breakpoint (frame_unwind_caller_arch (frame),
@@ -9744,10 +9841,11 @@ until_break_command (char *arg, int from_tty, int anywhere)
 					      bp_until);
       make_cleanup_delete_breakpoint (breakpoint2);
 
-      set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame));
-      make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+      set_longjmp_breakpoint (thread, frame_unwind_caller_id (frame));
+      make_cleanup (delete_longjmp_breakpoint_cleanup, &thread_num);
     }
 
+  clear_proceed_status ();
   proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
 
   /* If we are running asynchronously, and proceed call above has
@@ -9762,7 +9860,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
 
       args->breakpoint = breakpoint;
       args->breakpoint2 = breakpoint2;
-      args->thread_num = thread;
+      args->thread_num = thread_num;
 
       discard_cleanups (old_chain);
       add_continuation (inferior_thread (),
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 33fefcf..4b628c9 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -188,6 +188,8 @@ struct thread_info
      call.  See `struct thread_suspend_state'.  */
   struct thread_suspend_state suspend;
 
+  int reported_event;
+
   int current_line;
   struct symtab *current_symtab;
 
@@ -241,6 +243,9 @@ struct thread_info
      which exceptions to intercept.  */
   struct frame_id initiating_frame;
 
+  /* Data used by the execution command in effect.  */
+  void *cmd_data;
+
   /* Private data used by the target vector implementation.  */
   struct private_thread_info *private;
 
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 19e72a7..27b3ab9 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -40,6 +40,13 @@
 #include "exceptions.h"
 #include "itset.h"
 
+typedef void (*aec_callback_func) (struct thread_info *thr, void *data);
+void apply_execution_command (struct itset *apply_itset,
+			      struct itset *run_free_itset,
+			      aec_callback_func callback, void *callback_data);
+
+struct itset *default_run_free_itset (struct itset *apply_itset, int step);
+
 /* If we can't find a function's name from its address,
    we print this instead.  */
 #define RAW_FUNCTION_ADDRESS_FORMAT "at 0x%s"
@@ -414,6 +421,21 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
     {
       proceed (real_pc, TARGET_SIGNAL_0, 0);
 
+      if (target_is_non_stop_p ())
+	{
+	  struct itset *apply_itset = itset_create_empty ();
+	  struct itset *run_free_itset
+	    = default_run_free_itset (apply_itset, 0);
+
+	  apply_execution_command (apply_itset, current_itset,
+				   NULL, NULL);
+
+	  itset_free (apply_itset);
+	  itset_free (run_free_itset);
+
+	  switch_to_thread (call_thread->ptid);
+	}
+
       /* Inferior function calls are always synchronous, even if the
 	 target supports asynchronous execution.  Do here what
 	 `proceed' itself does in sync mode.  */
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 5d94cd4..bec876e 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -58,6 +58,108 @@
 #include "inf-loop.h"
 #include "continuations.h"
 #include "linespec.h"
+#include "itset.h"
+#include "cli/cli-utils.h"
+
+#define ERROR_NO_INFERIOR \
+   if (!target_has_execution) error (_("The program is not being run."));
+
+static struct itset *
+current_thread_set (void)
+{
+  struct itset *set;
+  struct inferior *inf;
+  struct thread_info *tp;
+  char *b;
+
+  if (ptid_equal (inferior_ptid, null_ptid))
+    return itset_create_empty ();
+
+  inf = current_inferior ();
+  tp = inferior_thread ();
+
+  b = alloca (256);
+  sprintf (b, "t%d", tp->num);
+  return itset_create (&b);
+}
+
+void do_target_resume (ptid_t ptid, int step, enum target_signal signo);
+
+typedef void (*aec_callback_func) (struct thread_info *thr, void *data);
+
+int follow_fork (int should_resume);
+
+void
+apply_execution_command (struct itset *apply_itset,
+			 struct itset *run_free_itset,
+			 aec_callback_func callback, void *callback_data)
+{
+  if (target_is_non_stop_p ())
+    {
+      struct thread_info *t;
+      int followed_fork = 0;
+
+      /* See if there are threads we'd run free that are stopped at
+	 forks.  If so, follow the fork, and refuse to apply the
+	 execution command further.  */
+      ALL_THREADS (t)
+        {
+	  if (t->state == THREAD_STOPPED
+	      && itset_contains_thread (run_free_itset, t)
+	      && !itset_contains_thread (apply_itset, t))
+	    {
+	      if (t->suspend.waitstatus.kind == TARGET_WAITKIND_FORKED
+		  || t->suspend.waitstatus.kind == TARGET_WAITKIND_VFORKED)
+		{
+		  switch_to_thread (t->ptid);
+		  follow_fork (0);
+		  followed_fork = 1;
+		}
+	    }
+	}
+
+      if (followed_fork)
+	{
+	  normal_stop ();
+	  if (target_can_async_p ())
+	    inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+	  return;
+	}
+
+      ALL_THREADS (t)
+        {
+	  if (t->state == THREAD_STOPPED
+	      && itset_contains_thread (apply_itset, t))
+	    {
+	      switch_to_thread (t->ptid);
+	      (*callback) (t, callback_data);
+	    }
+	  else if (t->state == THREAD_STOPPED
+		   && itset_contains_thread (run_free_itset, t))
+	    {
+	      /* If T has reported an event before (rather than having
+		 been forced-suspended by GDB, then have it step over
+		 any breakpoint its stopped at.  Otherwise, resume it
+		 as is, and let it hit any breakpoint it may be
+		 stopped at (or report it's already pending event), so
+		 the event is reported.  */
+	      if (t->reported_event)
+		{
+		  switch_to_thread (t->ptid);
+		  clear_proceed_status_thread (t);
+		  proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+		}
+	      else
+		do_target_resume (t->ptid, 0, TARGET_SIGNAL_0);
+	    }
+	}
+    }
+  else
+    {
+      ERROR_NO_INFERIOR;
+      (*callback) (inferior_thread (), callback_data);
+    }
+}
 
 /* Functions exported for general use, in inferior.h: */
 
@@ -80,7 +182,7 @@ static void nofp_registers_info (char *, int);
 static void print_return_value (struct type *func_type,
 				struct type *value_type);
 
-static void until_next_command (int);
+static void until_next_command (char *, int);
 
 static void until_command (char *, int);
 
@@ -126,9 +228,6 @@ static int strip_bg_char (char **);
 
 void _initialize_infcmd (void);
 
-#define ERROR_NO_INFERIOR \
-   if (!target_has_execution) error (_("The program is not being run."));
-
 /* Scratch area where string containing arguments to give to the
    program will be stored by 'set args'.  As soon as anything is
    stored, notice_args_set will move it into per-inferior storage.
@@ -597,7 +696,7 @@ run_command_1 (char *args, int from_tty, int tbreak_at_main)
      events --- the frontend shouldn't see them as stopped.  In
      all-stop, always finish the state of all threads, as we may be
      resuming more than just the new process.  */
-  if (non_stop)
+  if (target_is_non_stop_p ())
     ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
   else
     ptid = minus_one_ptid;
@@ -657,11 +756,11 @@ proceed_thread_callback (struct thread_info *thread, void *arg)
      much.  If/when GDB gains a way to tell the target `hold this
      thread stopped until I say otherwise', then we can optimize
      this.  */
-  if (!is_stopped (thread->ptid))
+  if (thread->state != THREAD_STOPPED)
     return 0;
 
   switch_to_thread (thread->ptid);
-  clear_proceed_status ();
+  clear_proceed_status_thread (thread);
   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
   return 0;
 }
@@ -714,23 +813,191 @@ continue_1 (int all_threads)
     }
 }
 
+static void
+itset_free_p (void *arg)
+{
+  struct itset **itset_p = arg;
+
+  if (*itset_p)
+    itset_free (*itset_p);
+}
+
+struct itset *
+default_run_free_itset (struct itset *apply_itset, int step)
+{
+  if (non_stop)
+    {
+      /* In non-stop mode, threads are always handled
+	 individually.  */
+      return itset_create_empty ();
+    }
+  else if (scheduler_mode == schedlock_on
+	   || (scheduler_mode == schedlock_step && step))
+    {
+      /* User-settable 'scheduler' mode requires solo thread
+	 resume.  */
+      return itset_create_empty ();
+    }
+  else if (!sched_multi)
+    {
+      struct inferior *inf;
+      char *set_spec;
+      char *p;
+      int first = 1;
+      struct itset *set;
+
+      /* Resume only threads of the current inferior process.  */
+      set_spec = xstrdup ("");
+      ALL_INFERIORS (inf)
+	if (itset_contains_inferior (apply_itset, inf))
+	  {
+	    char buf[128];
+
+	    if (first)
+	      {
+		first = 0;
+		sprintf (buf, "i%d", inf->num);
+	      }
+	    else
+	      sprintf (buf, ",i%d", inf->num);
+
+	    set_spec = reconcat (set_spec, set_spec, buf, (char *) NULL);
+	  }
+
+      p = set_spec;
+      set = itset_create (&p);
+      xfree (set_spec);
+      return set;
+    }
+  else
+    {
+      /* By default, resume all threads in the current set.  */
+      return itset_reference (current_itset);
+    }
+}
+
+char *
+parse_execution_args (char *args, int step,
+		      struct itset **apply_itset,
+		      int *apply_set_explicit,
+		      struct itset **run_free_itset)
+{
+  if (args != NULL)
+    {
+      while (*args)
+	{
+	  char *p;
+
+	  args = skip_spaces (args);
+	  p = skip_to_space (args);
+
+	  if (strncmp (args, "-a", p - args) == 0)
+	    {
+	      if (*apply_itset)
+		itset_free (*apply_itset);
+	      *apply_itset = itset_reference (current_itset);
+	      *apply_set_explicit = 1;
+	      args = p;
+	    }
+	  else if (strncmp (args, "-p", p - args) == 0)
+	    {
+	      if (*apply_itset)
+		itset_free (*apply_itset);
+	      *apply_itset = itset_create (&p);
+	      *apply_set_explicit = 1;
+	      args = p;
+	    }
+	  else if (strncmp (args, "-c", p - args) == 0)
+	    {
+	      if (*run_free_itset)
+		itset_free (*run_free_itset);
+	      *run_free_itset = itset_reference (current_itset);
+	      args = p;
+	    }
+	  else if (strncmp (args, "-l", p - args) == 0)
+	    {
+	      if (*run_free_itset)
+		itset_free (*run_free_itset);
+	      *run_free_itset = itset_create_empty ();
+	      args = p;
+	    }
+	  else if (strcmp (args, "--") == 0)
+	    {
+	      args += 2;
+	      break;
+	    }
+	  else
+	    break;
+	}
+
+      args = skip_spaces (args);
+    }
+
+  if (*apply_itset == NULL)
+    *apply_itset = current_thread_set ();
+
+  if (*run_free_itset == NULL)
+    *run_free_itset = default_run_free_itset (*apply_itset, step);
+
+  if (args && *args == '\0')
+    return NULL;
+  else
+    return args;
+}
+
+static void
+continue_aec_callback (struct thread_info *thread, void *data)
+{
+  clear_proceed_status ();
+  proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+}
+
 /* continue [-a] [proceed-count] [&]  */
 void
 continue_command (char *args, int from_tty)
 {
   int async_exec = 0;
   int all_threads = 0;
-  ERROR_NO_INFERIOR;
+  int ignore_count = 0;
+  int ignore_count_p = 0;
+  struct itset *apply_itset = NULL;
+  int apply_itset_explicit = 0;
+  struct itset *run_free_itset = NULL;
+  struct cleanup *old_chain;
 
   /* Find out whether we must run in the background.  */
   if (args != NULL)
     async_exec = strip_bg_char (&args);
 
+  old_chain = make_cleanup (itset_free_p, &apply_itset);
+  make_cleanup (itset_free_p, &run_free_itset);
+
+  args = parse_execution_args (args, 0,
+			       &apply_itset, &apply_itset_explicit,
+			       &run_free_itset);
+  if (args)
+    {
+      args = skip_spaces (args);
+      if (*args != '\0')
+	{
+	  ignore_count = parse_and_eval_long (args);
+	  ignore_count_p = 1;
+	}
+    }
+
   /* If we must run in the background, but the target can't do it,
      error out.  */
   if (async_exec && !target_can_async_p ())
     error (_("Asynchronous execution not supported on this target."));
 
+  if (itset_is_empty (apply_itset))
+    {
+      if (apply_itset_explicit)
+	error (_("Set of threads to continue is empty."));
+      else
+	error (_("The program is not being run."));
+    }
+
   /* If we are not asked to run in the bg, then prepare to run in the
      foreground, synchronously.  */
   if (!async_exec && target_can_async_p ())
@@ -739,17 +1006,6 @@ continue_command (char *args, int from_tty)
       async_disable_stdin ();
     }
 
-  if (args != NULL)
-    {
-      if (strncmp (args, "-a", sizeof ("-a") - 1) == 0)
-	{
-	  all_threads = 1;
-	  args += sizeof ("-a") - 1;
-	  if (*args == '\0')
-	    args = NULL;
-	}
-    }
-
   if (!non_stop && all_threads)
     error (_("`-a' is meaningless in all-stop mode."));
 
@@ -757,9 +1013,9 @@ continue_command (char *args, int from_tty)
     error (_("Can't resume all threads and specify "
 	     "proceed count simultaneously."));
 
-  /* If we have an argument left, set proceed count of breakpoint we
-     stopped at.  */
-  if (args != NULL)
+  /* Set proceed count of breakpoint we stopped at, if the user
+     requested it.  */
+  if (ignore_count_p)
     {
       bpstat bs = NULL;
       int num, stat;
@@ -782,9 +1038,7 @@ continue_command (char *args, int from_tty)
       while ((stat = bpstat_num (&bs, &num)) != 0)
 	if (stat > 0)
 	  {
-	    set_ignore_count (num,
-			      parse_and_eval_long (args) - 1,
-			      from_tty);
+	    set_ignore_count (num, ignore_count - 1, from_tty);
 	    /* set_ignore_count prints a message ending with a period.
 	       So print two spaces before "Continuing.".  */
 	    if (from_tty)
@@ -802,7 +1056,10 @@ continue_command (char *args, int from_tty)
   if (from_tty)
     printf_filtered (_("Continuing.\n"));
 
-  continue_1 (all_threads);
+  apply_execution_command (apply_itset, run_free_itset,
+			   continue_aec_callback, NULL);
+
+  do_cleanups (old_chain);
 }
 \f
 /* Record the starting point of a "step" or "next" command.  */
@@ -853,36 +1110,114 @@ delete_longjmp_breakpoint_cleanup (void *arg)
   delete_longjmp_breakpoint (thread);
 }
 
+struct step_1_args
+{
+  int count;
+  int skip_subroutines;
+  int single_inst;
+  int thread;
+};
+
+static void step_1_1 (int skip_subroutines, int single_inst, int count);
+
 static void
-step_1 (int skip_subroutines, int single_inst, char *count_string)
+step_1_aec_callback (struct thread_info *thread, void *data)
+{
+  struct step_1_args *args = data;
+
+  switch_to_thread (thread->ptid);
+  step_1_1 (args->skip_subroutines, args->single_inst, args->count);
+}
+
+void
+ensure_runnable (struct thread_info *thr)
+{
+  if (thr->state == THREAD_EXITED)
+    error (_("Thread %d (%s) has exited."),
+	   thr->num, target_pid_to_str (thr->ptid));
+  else if (thr->state == THREAD_EXITED)
+    error (_("Thread %d (%s) is running."),
+	   thr->num, target_pid_to_str (thr->ptid));
+}
+
+static void
+step_1 (int skip_subroutines, int single_inst, char *args)
 {
   int count = 1;
-  struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
   int async_exec = 0;
-  int thread = -1;
+  struct cleanup *old_chain;
+  struct itset *apply_itset = NULL;
+  int apply_itset_explicit = 0;
+  struct itset *run_free_itset = NULL;
+  struct step_1_args step_args;
+  struct thread_info *thr;
+  int thr_count = 0;
 
-  ERROR_NO_INFERIOR;
   ensure_not_tfind_mode ();
-  ensure_valid_thread ();
-  ensure_not_running ();
 
-  if (count_string)
-    async_exec = strip_bg_char (&count_string);
+  if (args)
+    async_exec = strip_bg_char (&args);
+
+  old_chain = make_cleanup (itset_free_p, &apply_itset);
+  make_cleanup (itset_free_p, &run_free_itset);
+
+  args = parse_execution_args (args, 1,
+			       &apply_itset, &apply_itset_explicit,
+			       &run_free_itset);
+
+  if (args)
+    {
+      args = skip_spaces (args);
+      if (*args != '\0')
+	count = parse_and_eval_long (args);
+    }
 
   /* If we get a request for running in the bg but the target
      doesn't support it, error out.  */
   if (async_exec && !target_can_async_p ())
     error (_("Asynchronous execution not supported on this target."));
 
+  ALL_THREADS (thr)
+    if (itset_contains_thread (apply_itset, thr))
+      {
+	++thr_count;
+
+	ensure_runnable (thr);
+      }
+
+  if (thr_count == 0)
+    {
+      if (apply_itset_explicit)
+	error (_("Set of threads to step is empty."));
+      else
+	error (_("The program is not being run."));
+    }
+
   /* If we don't get a request of running in the bg, then we need
      to simulate synchronous (fg) execution.  */
+  /* FIXME: should only do this is actually about to resume.  */
   if (!async_exec && target_can_async_p ())
     {
       /* Simulate synchronous execution.  */
       async_disable_stdin ();
     }
 
-  count = count_string ? parse_and_eval_long (count_string) : 1;
+  step_args.skip_subroutines = skip_subroutines;
+  step_args.single_inst = single_inst;
+  step_args.count = count;
+  step_args.thread = -1;
+
+  apply_execution_command (apply_itset, run_free_itset,
+			   step_1_aec_callback, &step_args);
+
+  do_cleanups (old_chain);
+}
+
+static void
+step_1_1 (int skip_subroutines, int single_inst, int count)
+{
+  int thread = -1;
+  struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
 
   if (!single_inst || skip_subroutines)		/* Leave si command alone.  */
     {
@@ -936,14 +1271,6 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
     }
 }
 
-struct step_1_continuation_args
-{
-  int count;
-  int skip_subroutines;
-  int single_inst;
-  int thread;
-};
-
 /* Called after we are done with one step operation, to check whether
    we need to step again, before we print the prompt and return control
    to the user.  If count is > 1, we will need to do one more call to
@@ -952,7 +1279,7 @@ struct step_1_continuation_args
 static void
 step_1_continuation (void *args, int err)
 {
-  struct step_1_continuation_args *a = args;
+  struct step_1_args *a = args;
 
   if (target_has_execution)
     {
@@ -1073,7 +1400,7 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
 	 further stepping.  */
       if (target_can_async_p ())
 	{
-	  struct step_1_continuation_args *args;
+	  struct step_1_args *args;
 
 	  args = xmalloc (sizeof (*args));
 	  args->skip_subroutines = skip_subroutines;
@@ -1087,23 +1414,52 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
 }
 
 \f
+
+struct jump_cmd_data
+{
+  CORE_ADDR addr;
+};
+
+struct jump_aec_callback_data
+{
+  int from_tty;
+};
+
+static void
+jump_aec_callback (struct thread_info *thread, void *data)
+{
+  struct jump_aec_callback_data *arg = data;
+  struct jump_map_entry *jme;
+  int ix;
+  struct gdbarch *gdbarch = get_current_arch ();
+  int from_tty = arg->from_tty;
+  struct jump_cmd_data *cmd_data = thread->cmd_data;
+  CORE_ADDR addr = cmd_data->addr;
+
+  if (from_tty)
+    printf_filtered (_("Continuing %d (%s) at %s.\n"),
+		     thread->num, target_pid_to_str (thread->ptid),
+		     paddress (gdbarch, addr));
+
+  clear_proceed_status ();
+  proceed (addr, TARGET_SIGNAL_0, 0);
+  return;
+}
+
 /* Continue program at specified address.  */
 
 static void
 jump_command (char *arg, int from_tty)
 {
-  struct gdbarch *gdbarch = get_current_arch ();
-  CORE_ADDR addr;
-  struct symtabs_and_lines sals;
-  struct symtab_and_line sal;
-  struct symbol *fn;
-  struct symbol *sfn;
   int async_exec = 0;
+  struct itset *apply_itset = NULL;
+  int apply_itset_explicit = 0;
+  struct itset *run_free_itset = NULL;
+  struct cleanup *old_chain;
+  struct thread_info *thr;
+  struct jump_aec_callback_data cb_data;
 
-  ERROR_NO_INFERIOR;
   ensure_not_tfind_mode ();
-  ensure_valid_thread ();
-  ensure_not_running ();
 
   /* Find out whether we must run in the background.  */
   if (arg != NULL)
@@ -1114,59 +1470,77 @@ jump_command (char *arg, int from_tty)
   if (async_exec && !target_can_async_p ())
     error (_("Asynchronous execution not supported on this target."));
 
-  if (!arg)
-    error_no_arg (_("starting address"));
+  old_chain = make_cleanup (itset_free_p, &apply_itset);
+  make_cleanup (itset_free_p, &run_free_itset);
 
-  sals = decode_line_spec_1 (arg, DECODE_LINE_FUNFIRSTLINE);
-  if (sals.nelts != 1)
+  arg = parse_execution_args (arg, 0,
+			      &apply_itset, &apply_itset_explicit,
+			      &run_free_itset);
+
+  if (itset_is_empty (apply_itset))
     {
-      error (_("Unreasonable jump request"));
+      if (apply_itset_explicit)
+	error (_("Set of threads to jump is empty."));
+      else
+	error (_("The program is not being run."));
     }
 
-  sal = sals.sals[0];
-  xfree (sals.sals);
+  if (!arg)
+    error_no_arg (_("starting address"));
 
-  if (sal.symtab == 0 && sal.pc == 0)
-    error (_("No source file has been specified."));
+  ALL_THREADS (thr)
+    if (itset_contains_thread (apply_itset, thr))
+      {
+	struct symtabs_and_lines sals;
+	struct symtab_and_line sal;
+	struct symbol *fn;
+	struct symbol *sfn;
+	struct jump_cmd_data *cmd_data;
 
-  resolve_sal_pc (&sal);	/* May error out.  */
+	ensure_runnable (thr);
 
-  /* See if we are trying to jump to another function.  */
-  fn = get_frame_function (get_current_frame ());
-  sfn = find_pc_function (sal.pc);
-  if (fn != NULL && sfn != fn)
-    {
-      if (!query (_("Line %d is not in `%s'.  Jump anyway? "), sal.line,
-		  SYMBOL_PRINT_NAME (fn)))
-	{
-	  error (_("Not confirmed."));
-	  /* NOTREACHED */
-	}
-    }
+	if (!ptid_equal (inferior_ptid, thr->ptid))
+	  switch_to_thread (thr->ptid);
 
-  if (sfn != NULL)
-    {
-      fixup_symbol_section (sfn, 0);
-      if (section_is_overlay (SYMBOL_OBJ_SECTION (sfn)) &&
-	  !section_is_mapped (SYMBOL_OBJ_SECTION (sfn)))
-	{
-	  if (!query (_("WARNING!!!  Destination is in "
-			"unmapped overlay!  Jump anyway? ")))
-	    {
+	sals = decode_line_spec_1 (arg, DECODE_LINE_FUNFIRSTLINE);
+	if (sals.nelts != 1)
+	  error (_("Unreasonable jump request for thread %s"),
+		 target_pid_to_str (thr->ptid));
+
+	sal = sals.sals[0];
+	xfree (sals.sals);
+
+	if (sal.symtab == 0 && sal.pc == 0)
+	  error (_("No source file has been specified."));
+
+	resolve_sal_pc (&sal);	/* May error out.  */
+
+	/* See if we are trying to jump to another function.  */
+	fn = get_frame_function (get_current_frame ());
+	sfn = find_pc_function (sal.pc);
+	if (fn != NULL && sfn != fn)
+	  {
+	    if (!query (_("Line %d is not in `%s'.  Jump anyway? "), sal.line,
+			SYMBOL_PRINT_NAME (fn)))
 	      error (_("Not confirmed."));
-	      /* NOTREACHED */
-	    }
-	}
-    }
+	  }
 
-  addr = sal.pc;
+	if (sfn != NULL)
+	  {
+	    fixup_symbol_section (sfn, 0);
+	    if (section_is_overlay (SYMBOL_OBJ_SECTION (sfn)) &&
+		!section_is_mapped (SYMBOL_OBJ_SECTION (sfn)))
+	      {
+		if (!query (_("WARNING!!!  Destination is in "
+			      "unmapped overlay!  Jump anyway? ")))
+		  error (_("Not confirmed."));
+	      }
+	  }
 
-  if (from_tty)
-    {
-      printf_filtered (_("Continuing at "));
-      fputs_filtered (paddress (gdbarch, addr), gdb_stdout);
-      printf_filtered (".\n");
-    }
+	cmd_data = XNEW (struct jump_cmd_data);
+	cmd_data->addr = sal.pc;
+	thr->cmd_data = cmd_data;
+      }
 
   /* If we are not asked to run in the bg, then prepare to run in the
      foreground, synchronously.  */
@@ -1176,8 +1550,11 @@ jump_command (char *arg, int from_tty)
       async_disable_stdin ();
     }
 
-  clear_proceed_status ();
-  proceed (addr, TARGET_SIGNAL_0, 0);
+  cb_data.from_tty = from_tty;
+  apply_execution_command (apply_itset, run_free_itset,
+			   jump_aec_callback, &cb_data);
+
+  do_cleanups (old_chain);
 }
 \f
 
@@ -1197,27 +1574,55 @@ go_command (char *line_no, int from_tty)
 
 /* Continue program giving it specified signal.  */
 
+struct signal_aec_callback_data
+{
+  enum target_signal oursig;
+};
+
+static void signal_aec_callback (struct thread_info *thread, void *data);
+
 static void
-signal_command (char *signum_exp, int from_tty)
+signal_command (char *arg, int from_tty)
 {
   enum target_signal oursig;
   int async_exec = 0;
+  struct itset *apply_itset = NULL;
+  int apply_itset_explicit = 0;
+  struct itset *run_free_itset = NULL;
+  struct cleanup *old_chain;
+  struct signal_aec_callback_data cb_data;
+  struct thread_info *thr;
 
   dont_repeat ();		/* Too dangerous.  */
-  ERROR_NO_INFERIOR;
   ensure_not_tfind_mode ();
-  ensure_valid_thread ();
-  ensure_not_running ();
 
   /* Find out whether we must run in the background.  */
-  if (signum_exp != NULL)
-    async_exec = strip_bg_char (&signum_exp);
+  if (arg != NULL)
+    async_exec = strip_bg_char (&arg);
 
   /* If we must run in the background, but the target can't do it,
      error out.  */
   if (async_exec && !target_can_async_p ())
     error (_("Asynchronous execution not supported on this target."));
 
+  old_chain = make_cleanup (itset_free_p, &apply_itset);
+  make_cleanup (itset_free_p, &run_free_itset);
+
+  arg = parse_execution_args (arg, 0,
+			      &apply_itset, &apply_itset_explicit,
+			      &run_free_itset);
+
+  if (itset_is_empty (apply_itset))
+    {
+      if (apply_itset_explicit)
+	error (_("Set of threads to signal is empty."));
+      else
+	error (_("The program is not being run."));
+    }
+
+  if (!arg)
+    error_no_arg (_("signal number"));
+
   /* If we are not asked to run in the bg, then prepare to run in the
      foreground, synchronously.  */
   if (!async_exec && target_can_async_p ())
@@ -1226,18 +1631,19 @@ signal_command (char *signum_exp, int from_tty)
       async_disable_stdin ();
     }
 
-  if (!signum_exp)
-    error_no_arg (_("signal number"));
+  ALL_THREADS (thr)
+    if (itset_contains_thread (apply_itset, thr))
+      ensure_runnable (thr);
 
   /* It would be even slicker to make signal names be valid expressions,
      (the type could be "enum $signal" or some such), then the user could
      assign them to convenience variables.  */
-  oursig = target_signal_from_name (signum_exp);
+  oursig = target_signal_from_name (arg);
 
   if (oursig == TARGET_SIGNAL_UNKNOWN)
     {
       /* No, try numeric.  */
-      int num = parse_and_eval_long (signum_exp);
+      int num = parse_and_eval_long (arg);
 
       if (num == 0)
 	oursig = TARGET_SIGNAL_0;
@@ -1254,8 +1660,20 @@ signal_command (char *signum_exp, int from_tty)
 			 target_signal_to_name (oursig));
     }
 
+  cb_data.oursig = oursig;
+  apply_execution_command (apply_itset, run_free_itset,
+			   signal_aec_callback, &cb_data);
+
+  do_cleanups (old_chain);
+}
+
+static void
+signal_aec_callback (struct thread_info *thread, void *data)
+{
+  struct signal_aec_callback_data *d = data;
+
   clear_proceed_status ();
-  proceed ((CORE_ADDR) -1, oursig, 0);
+  proceed ((CORE_ADDR) -1, d->oursig, 0);
 }
 
 /* Continuation args to be passed to the "until" command
@@ -1284,51 +1702,99 @@ until_next_continuation (void *arg, int err)
    we set.  This may involve changes to wait_for_inferior and the
    proceed status code.  */
 
+static void until_next_aec_callback (struct thread_info *thread, void *data);
+
 static void
-until_next_command (int from_tty)
+until_next_command (char *arg, int from_tty)
 {
-  struct frame_info *frame;
-  CORE_ADDR pc;
-  struct symbol *func;
-  struct symtab_and_line sal;
-  struct thread_info *tp = inferior_thread ();
-  int thread = tp->num;
+  struct itset *apply_itset = NULL;
+  int apply_itset_explicit = 0;
+  struct itset *run_free_itset = NULL;
   struct cleanup *old_chain;
+  struct thread_info *thr;
+  int thr_count = 0;
 
-  clear_proceed_status ();
-  set_step_frame ();
+  old_chain = make_cleanup (itset_free_p, &apply_itset);
+  make_cleanup (itset_free_p, &run_free_itset);
 
-  frame = get_current_frame ();
+  arg = parse_execution_args (arg, 0,
+			      &apply_itset, &apply_itset_explicit,
+			      &run_free_itset);
 
-  /* Step until either exited from this function or greater
-     than the current line (if in symbolic section) or pc (if
-     not).  */
+  ALL_THREADS (thr)
+    if (itset_contains_thread (apply_itset, thr))
+      {
+	struct frame_info *frame;
+	CORE_ADDR pc;
+	struct symbol *func;
+	struct symtab_and_line sal;
 
-  pc = get_frame_pc (frame);
-  func = find_pc_function (pc);
+	++thr_count;
 
-  if (!func)
-    {
-      struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (pc);
+	ensure_runnable (thr);
 
-      if (msymbol == NULL)
-	error (_("Execution is not within a known function."));
+	if (!ptid_equal (inferior_ptid, thr->ptid))
+	  switch_to_thread (thr->ptid);
 
-      tp->control.step_range_start = SYMBOL_VALUE_ADDRESS (msymbol);
-      tp->control.step_range_end = pc;
-    }
-  else
-    {
-      sal = find_pc_line (pc, 0);
+	clear_proceed_status ();
+	set_step_frame ();
+
+	frame = get_current_frame ();
+
+	/* Step until either exited from this function or greater than
+	   the current line (if in symbolic section) or pc (if
+	   not).  */
+
+	pc = get_frame_pc (frame);
+	func = find_pc_function (pc);
+
+	if (!func)
+	  {
+	    struct minimal_symbol *msymbol = lookup_minimal_symbol_by_pc (pc);
 
-      tp->control.step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
-      tp->control.step_range_end = sal.end;
+	    if (msymbol == NULL)
+	      error (_("Execution is not within a known function."));
+
+	    thr->control.step_range_start = SYMBOL_VALUE_ADDRESS (msymbol);
+	    thr->control.step_range_end = pc;
+	  }
+	else
+	  {
+	    sal = find_pc_line (pc, 0);
+
+	    thr->control.step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
+	    thr->control.step_range_end = sal.end;
+	  }
+
+	thr->control.step_over_calls = STEP_OVER_ALL;
+
+	thr->step_multi = 0;		/* Only one call to proceed */
+      }
+
+  if (thr_count == 0)
+    {
+      if (apply_itset_explicit)
+	error (_("Set of threads to until is empty."));
+      else
+	error (_("The program is not being run."));
     }
 
-  tp->control.step_over_calls = STEP_OVER_ALL;
+  apply_execution_command (apply_itset, run_free_itset,
+			   until_next_aec_callback, NULL);
 
-  tp->step_multi = 0;		/* Only one call to proceed */
+  do_cleanups (old_chain);
+}
+
+\f
 
+static void
+until_next_aec_callback (struct thread_info *tp, void *data)
+{
+  int thread = tp->num;
+  struct cleanup *old_chain;
+  struct frame_info *frame;
+
+  frame = get_current_frame ();
   set_longjmp_breakpoint (tp, get_frame_id (frame));
   old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
 
@@ -1353,10 +1819,7 @@ until_command (char *arg, int from_tty)
 {
   int async_exec = 0;
 
-  ERROR_NO_INFERIOR;
   ensure_not_tfind_mode ();
-  ensure_valid_thread ();
-  ensure_not_running ();
 
   /* Find out whether we must run in the background.  */
   if (arg != NULL)
@@ -1378,7 +1841,7 @@ until_command (char *arg, int from_tty)
   if (arg)
     until_break_command (arg, from_tty, 0);
   else
-    until_next_command (from_tty);
+    until_next_command (arg, from_tty);
 }
 
 static void
@@ -1386,10 +1849,7 @@ advance_command (char *arg, int from_tty)
 {
   int async_exec = 0;
 
-  ERROR_NO_INFERIOR;
   ensure_not_tfind_mode ();
-  ensure_valid_thread ();
-  ensure_not_running ();
 
   if (arg == NULL)
     error_no_arg (_("a location"));
@@ -1649,21 +2109,34 @@ finish_forward (struct symbol *function, struct frame_info *frame)
     do_all_continuations (0);
 }
 
+struct finish_cmd_data
+{
+  struct frame_id selected_frame_id;
+};
+
+struct finish_aec_callback_data
+{
+  int from_tty;
+};
+
+static void finish_aec_callback (struct thread_info *thread, void *data);
+
 /* "finish": Set a temporary breakpoint at the place the selected
    frame will return to, then continue.  */
 
 static void
 finish_command (char *arg, int from_tty)
 {
-  struct frame_info *frame;
-  struct symbol *function;
-
+  struct itset *apply_itset = NULL;
+  int apply_itset_explicit = 0;
+  struct itset *run_free_itset = NULL;
+  struct cleanup *old_chain;
+  struct thread_info *thr;
+  int thr_count = 0;
+  struct finish_aec_callback_data cb_data;
   int async_exec = 0;
 
-  ERROR_NO_INFERIOR;
   ensure_not_tfind_mode ();
-  ensure_valid_thread ();
-  ensure_not_running ();
 
   /* Find out whether we must run in the background.  */
   if (arg != NULL)
@@ -1674,6 +2147,13 @@ finish_command (char *arg, int from_tty)
   if (async_exec && !target_can_async_p ())
     error (_("Asynchronous execution not supported on this target."));
 
+  old_chain = make_cleanup (itset_free_p, &apply_itset);
+  make_cleanup (itset_free_p, &run_free_itset);
+
+  arg = parse_execution_args (arg, 0,
+			      &apply_itset, &apply_itset_explicit,
+			      &run_free_itset);
+
   /* If we are not asked to run in the bg, then prepare to run in the
      foreground, synchronously.  */
   if (!async_exec && target_can_async_p ())
@@ -1685,29 +2165,78 @@ finish_command (char *arg, int from_tty)
   if (arg)
     error (_("The \"finish\" command does not take any arguments."));
 
-  frame = get_prev_frame (get_selected_frame (_("No selected frame.")));
-  if (frame == 0)
-    error (_("\"finish\" not meaningful in the outermost frame."));
+  ALL_THREADS (thr)
+    if (itset_contains_thread (apply_itset, thr))
+      {
+	struct frame_info *frame;
+	struct frame_info *prev;
+	struct finish_cmd_data *cmd_data;
+
+	++thr_count;
+
+	ensure_runnable (thr);
+
+	if (!ptid_equal (inferior_ptid, thr->ptid))
+	  switch_to_thread (thr->ptid);
+
+	frame = get_selected_frame (_("No selected frame."));
+	prev = get_prev_frame (frame);
+	if (prev == NULL)
+	  error (_("\"finish\" not meaningful in the outermost frame."));
+
+	cmd_data = XNEW (struct finish_cmd_data);
+	cmd_data->selected_frame_id = get_frame_id (frame);
+	thr->cmd_data = cmd_data;
+      }
+
+  if (thr_count == 0)
+    {
+      if (apply_itset_explicit)
+	error (_("Set of threads to finish is empty."));
+      else
+	error (_("The program is not being run."));
+    }
+
+  cb_data.from_tty = from_tty;
+  apply_execution_command (apply_itset, run_free_itset,
+			   finish_aec_callback, &cb_data);
+
+  do_cleanups (old_chain);
+}
+
+static void
+finish_aec_callback (struct thread_info *tp, void *data)
+{
+  struct finish_aec_callback_data *d = data;
+  int from_tty = d->from_tty;
+  struct finish_cmd_data *cmd_data = tp->cmd_data;
+  int ix;
+  struct frame_info *frame, *prev;
+  struct symbol *function;
 
   clear_proceed_status ();
 
+  frame = frame_find_by_id (cmd_data->selected_frame_id);
+  gdb_assert (frame != NULL);
+  select_frame (frame);
+  prev = get_prev_frame (frame);
+  gdb_assert (prev != NULL);
+
   /* Finishing from an inline frame is completely different.  We don't
      try to show the "return value" - no way to locate it.  So we do
      not need a completion.  */
-  if (get_frame_type (get_selected_frame (_("No selected frame.")))
-      == INLINE_FRAME)
+  if (get_frame_type (frame) == INLINE_FRAME)
     {
       /* Claim we are stepping in the calling frame.  An empty step
 	 range means that we will stop once we aren't in a function
 	 called by that frame.  We don't use the magic "1" value for
 	 step_range_end, because then infrun will think this is nexti,
 	 and not step over the rest of this inlined function call.  */
-      struct thread_info *tp = inferior_thread ();
       struct symtab_and_line empty_sal;
 
       init_sal (&empty_sal);
-      set_step_info (frame, empty_sal);
-      tp->control.step_range_start = get_frame_pc (frame);
+      set_step_info (prev, empty_sal);
+      tp->control.step_range_start = get_frame_pc (prev);
       tp->control.step_range_end = tp->control.step_range_start;
       tp->control.step_over_calls = STEP_OVER_ALL;
 
@@ -1716,7 +2245,7 @@ finish_command (char *arg, int from_tty)
       if (from_tty)
 	{
 	  printf_filtered (_("Run till exit from "));
-	  print_stack_frame (get_selected_frame (NULL), 1, LOCATION);
+	  print_stack_frame (frame, 1, LOCATION);
 	}
 
       proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
@@ -1725,7 +2254,7 @@ finish_command (char *arg, int from_tty)
 
   /* Find the function we will return from.  */
 
-  function = find_pc_function (get_frame_pc (get_selected_frame (NULL)));
+  function = find_pc_function (get_frame_pc (frame));
 
   /* Print info on the selected frame, including level number but not
      source.  */
@@ -1736,13 +2265,13 @@ finish_command (char *arg, int from_tty)
       else
 	printf_filtered (_("Run till exit from "));
 
-      print_stack_frame (get_selected_frame (NULL), 1, LOCATION);
+      print_stack_frame (frame, 1, LOCATION);
     }
 
   if (execution_direction == EXEC_REVERSE)
     finish_backward (function);
   else
-    finish_forward (function, frame);
+    finish_forward (function, prev);
 }
 \f
 
@@ -2694,10 +3223,31 @@ interrupt_target_1 (int all_threads)
   ptid_t ptid;
 
   if (all_threads)
-    ptid = minus_one_ptid;
+    {
+      if (target_is_non_stop_p ())
+	{
+	  struct thread_info *t;
+
+	  ALL_LIVE_THREADS (t)
+	    if (itset_contains_thread (current_itset, t))
+	      {
+		target_stop (t->ptid);
+		set_stop_requested (t->ptid, 1);
+	      }
+
+	  return;
+	}
+      else
+	{
+	  ptid = minus_one_ptid;
+	  target_stop (ptid);
+	}
+    }
   else
-    ptid = inferior_ptid;
-  target_stop (ptid);
+    {
+      ptid = inferior_ptid;
+      target_stop (ptid);
+    }
 
   /* Tag the thread as having been explicitly requested to stop, so
      other parts of gdb know not to resume this thread automatically,
@@ -2705,7 +3255,7 @@ interrupt_target_1 (int all_threads)
      non-stop mode, as when debugging a multi-threaded application in
      all-stop mode, we will only get one stop event --- it's undefined
      which thread will report the event.  */
-  if (non_stop)
+  if (target_is_non_stop_p ())
     set_stop_requested (ptid, 1);
 }
 
@@ -2728,9 +3278,6 @@ interrupt_target_command (char *args, int from_tty)
 	  && strncmp (args, "-a", sizeof ("-a") - 1) == 0)
 	all_threads = 1;
 
-      if (!non_stop && all_threads)
-	error (_("-a is meaningless in all-stop mode."));
-
       interrupt_target_1 (all_threads);
     }
 }
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cfaea7f..5697b31 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -103,6 +103,8 @@ extern int sync_execution;
 
 extern void clear_proceed_status (void);
 
+extern void clear_proceed_status_thread (struct thread_info *tp);
+
 extern void proceed (CORE_ADDR, enum target_signal, int);
 
 extern int sched_multi;
@@ -166,6 +168,11 @@ extern void resume (int, enum target_signal);
 
 extern ptid_t user_visible_resume_ptid (int step);
 
+extern const char schedlock_off[];
+extern const char schedlock_on[];
+extern const char schedlock_step[];
+extern const char *scheduler_mode;
+
 extern void insert_step_resume_breakpoint_at_sal (struct gdbarch *,
 						  struct symtab_and_line ,
 						  struct frame_id);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 7f6d643..a4a2bc9 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -81,7 +81,7 @@ static int hook_stop_stub (void *);
 
 static int restore_selected_frame (void *);
 
-static int follow_fork (void);
+int follow_fork (int);
 
 static void set_schedlock_func (char *args, int from_tty,
 				struct cmd_list_element *c);
@@ -416,28 +416,14 @@ show_follow_fork_mode_string (struct ui_file *file, int from_tty,
 }
 \f
 
-/* Tell the target to follow the fork we're stopped at.  Returns true
-   if the inferior should be resumed; false, if the target for some
-   reason decided it's best not to resume.  */
+/* Check if we lost stopped at a fork event, but switched over to
+   another thread since.  If so, switch back to the event thread, and
+   return false.  Otherwise, return true.  */
 
 static int
-follow_fork (void)
+prepare_to_follow_fork (void)
 {
-  int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
-  int should_resume = 1;
-  struct thread_info *tp;
-
-  /* Copy user stepping state to the new inferior thread.  FIXME: the
-     followed fork child thread should have a copy of most of the
-     parent thread structure's run control related fields, not just these.
-     Initialized to avoid "may be used uninitialized" warnings from gcc.  */
-  struct breakpoint *step_resume_breakpoint = NULL;
-  struct breakpoint *exception_resume_breakpoint = NULL;
-  CORE_ADDR step_range_start = 0;
-  CORE_ADDR step_range_end = 0;
-  struct frame_id step_frame_id = { 0 };
-
-  if (!non_stop)
+  if (!target_is_non_stop_p ())
     {
       ptid_t wait_ptid;
       struct target_waitstatus wait_status;
@@ -461,10 +447,32 @@ follow_fork (void)
 	     afterwards refuse to resume, and inform the user what
 	     happened.  */
 	  switch_to_thread (wait_ptid);
-	  should_resume = 0;
+	  return 0;
 	}
     }
 
+  return 1;
+}
+
+/* Tell the target to follow the fork we're stopped at.  Returns true
+   if the inferior should be resumed; false, if the target for some
+   reason decided it's best not to resume.  */
+
+int
+follow_fork (int should_resume)
+{
+  int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
+  struct thread_info *tp;
+  /* Copy user stepping state to the new inferior thread.  FIXME: the
+     followed fork child thread should have a copy of most of the
+     parent thread structure's run control related fields, not just these.
+     Initialized to avoid "may be used uninitialized" warnings from gcc.  */
+  struct breakpoint *step_resume_breakpoint = NULL;
+  struct breakpoint *exception_resume_breakpoint = NULL;
+  CORE_ADDR step_range_start = 0;
+  CORE_ADDR step_range_end = 0;
+  struct frame_id step_frame_id = { 0 };
+
   tp = inferior_thread ();
 
   /* If there were any forks/vforks that were caught and are now to be
@@ -1420,7 +1428,7 @@ displaced_step_restore (struct displaced_step_inferior_state *displaced,
 				  displaced->step_copy));
 }
 
-static void do_target_resume (ptid_t ptid, int step, enum target_signal signo);
+void do_target_resume (ptid_t ptid, int step, enum target_signal signo);
 
 static int
 displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
@@ -1608,16 +1616,16 @@ resume_cleanups (void *ignore)
   normal_stop ();
 }
 
-static const char schedlock_off[] = "off";
-static const char schedlock_on[] = "on";
-static const char schedlock_step[] = "step";
+const char schedlock_off[] = "off";
+const char schedlock_on[] = "on";
+const char schedlock_step[] = "step";
 static const char *scheduler_enums[] = {
   schedlock_off,
   schedlock_on,
   schedlock_step,
   NULL
 };
-static const char *scheduler_mode = schedlock_off;
+const char *scheduler_mode = schedlock_off;
 static void
 show_scheduler_mode (struct ui_file *file, int from_tty,
 		     struct cmd_list_element *c, const char *value)
@@ -1690,9 +1698,9 @@ user_visible_resume_ptid (int step)
     }
 
   /* Maybe resume a single thread after all.  */
-  if (non_stop)
+  if (target_is_non_stop_p ())
     {
-      /* With non-stop mode on, threads are always handled
+      /* In non-stop mode, threads are always handled
 	 individually.  */
       resume_ptid = inferior_ptid;
     }
@@ -1722,7 +1730,7 @@ queued_for_displaced_step (ptid_t ptid)
   return 0;
 }
 
-static void
+void
 do_target_resume (ptid_t ptid, int step, enum target_signal signo)
 {
   int resume_many;
@@ -1794,6 +1802,7 @@ do_target_resume (ptid_t ptid, int step, enum target_signal signo)
 	     happens to apply to another thread.  */
 	  tp->suspend.stop_signal = TARGET_SIGNAL_0;
 	  tp->control.resumed = 1;
+	  tp->reported_event = 0;
 
 	  if (tp->suspend.waitstatus_pending_p)
 	    {
@@ -2034,16 +2043,6 @@ a command like `return' or `jump' to continue execution."));
 	  set_running (inferior_ptid, 1);
 	  clear_inline_frame_state (inferior_ptid);
 	  discard_cleanups (old_cleanups);
-
-	  resume_ptid = user_visible_resume_ptid (step);
-
-	  gdb_assert (!tp->control.resumed);
-
-	  /* Resume others.  */
-	  tp->executing = 1;
-	  do_target_resume (resume_ptid, 0, TARGET_SIGNAL_0);
-	  tp->executing = 0;
-
 	  gdb_assert (!tp->control.resumed);
 	  return;
 	}
@@ -2187,7 +2186,7 @@ a command like `return' or `jump' to continue execution."));
 /* Clear out all variables saying what to do when inferior is continued.
    First do this, then set the ones you want, then call `proceed'.  */
 
-static void
+void
 clear_proceed_status_thread (struct thread_info *tp)
 {
   if (tp->state == THREAD_RUNNING)
@@ -2233,7 +2232,7 @@ clear_proceed_status_callback (struct thread_info *tp, void *data)
 void
 clear_proceed_status (void)
 {
-  if (!non_stop)
+  if (!target_is_non_stop_p ())
     {
       /* In all-stop mode, delete the per-thread status of all
 	 threads, even if inferior_ptid is null_ptid, there may be
@@ -2246,7 +2245,7 @@ clear_proceed_status (void)
     {
       struct inferior *inferior;
 
-      if (non_stop)
+      if (target_is_non_stop_p ())
 	{
 	  /* If in non-stop mode, only delete the per-thread status of
 	     the current thread.  */
@@ -2368,7 +2367,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
   /* If we're stopped at a fork/vfork, follow the branch set by the
      "set follow-fork-mode" command; otherwise, we'll just proceed
      resuming the current thread.  */
-  if (!follow_fork ())
+  if (prepare_to_follow_fork () && !follow_fork (1))
     {
       /* The target for some reason decided not to resume.  */
       normal_stop ();
@@ -2421,7 +2420,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
 			"infrun: proceed (addr=%s, signal=%d, step=%d)\n",
 			paddress (gdbarch, addr), siggnal, step);
 
-  if (non_stop)
+  if (target_is_non_stop_p ())
     /* In non-stop, each thread is handled individually.  The context
        must already be set to the right thread here.  */
     ;
@@ -2805,7 +2804,7 @@ delete_step_thread_step_resume_breakpoint (void)
        resume breakpoints out of GDB's lists.  */
     return;
 
-  if (non_stop)
+  if (target_is_non_stop_p ())
     {
       /* If in non-stop mode, only delete the step-resume or
 	 longjmp-resume breakpoint of the thread that just stopped
@@ -3011,7 +3010,7 @@ prepare_for_detach (void)
       /* In non-stop mode, each thread is handled individually.
 	 Switch early, so the global state is set correctly for this
 	 thread.  */
-      if (non_stop
+      if (target_is_non_stop_p ()
 	  && ecs->ws.kind != TARGET_WAITKIND_EXITED
 	  && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
 	context_switch (ecs->ptid);
@@ -3356,20 +3355,18 @@ fetch_inferior_event (void *client_data)
   if (ecs->ws.kind != TARGET_WAITKIND_IGNORE)
     select_event_thread (ecs);
 
-  if (non_stop
-      && ecs->ws.kind != TARGET_WAITKIND_IGNORE
+  if (ecs->ws.kind != TARGET_WAITKIND_IGNORE
       && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
       && ecs->ws.kind != TARGET_WAITKIND_EXITED
       && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
-    /* In non-stop mode, each thread is handled individually.  Switch
-       early, so the global state is set correctly for this
+    /* Switch early, so the global state is set correctly for this
        thread.  */
     context_switch (ecs->ptid);
 
   /* If an error happens while handling the event, propagate GDB's
      knowledge of the executing state to the frontend/user running
      state.  */
-  if (!non_stop)
+  if (!target_is_non_stop_p ())
     ts_old_chain = make_cleanup (finish_thread_state_cleanup, &minus_one_ptid);
   else
     ts_old_chain = make_cleanup (finish_thread_state_cleanup, &ecs->ptid);
@@ -4174,6 +4171,7 @@ handle_inferior_event (struct execution_control_state *ecs)
       current_inferior ()->exit_code = (LONGEST) ecs->ws.value.integer;
 
       gdb_flush (gdb_stdout);
+      ecs->event_thread = NULL;
       target_mourn_inferior ();
       singlestep_breakpoints_inserted_p = 0;
       cancel_single_step_breakpoints ();
@@ -4196,6 +4194,7 @@ handle_inferior_event (struct execution_control_state *ecs)
          target_kill() was called here, which hints that fatal signals aren't
          really fatal on some systems.  If that's true, then some changes
          may be needed.  */
+      ecs->event_thread = NULL;
       target_mourn_inferior ();
 
       print_signal_exited_reason (ecs->ws.value.sig);
@@ -4329,7 +4328,7 @@ handle_inferior_event (struct execution_control_state *ecs)
 
 	  ecs->event_thread->suspend.stop_signal = TARGET_SIGNAL_0;
 
-	  should_resume = follow_fork ();
+	  should_resume = prepare_to_follow_fork() && follow_fork (1);
 
 	  parent = ecs->ptid;
 	  child = ecs->ws.value.related_pid;
@@ -4739,7 +4738,7 @@ handle_inferior_event (struct execution_control_state *ecs)
 	    error (_("Cannot step over breakpoint hit in wrong thread"));
 	  else
 	    {			/* Single step */
-	      if (!non_stop)
+	      if (!target_is_non_stop_p ())
 		{
 		  /* Only need to require the next event from this
 		     thread in all-stop mode.  */
@@ -6363,6 +6362,15 @@ stop_stepping (struct execution_control_state *ecs)
   /* Let callers know we don't want to wait for the inferior anymore.  */
   ecs->wait_some_more = 0;
 
+  if (ecs->event_thread)
+    {
+      /* Mark this thread as having reported its event to the
+	 frontend/user.  Later, when proceeding, if the thread is
+	 stopped at a breakpoint, we'll step over it in order to make
+	 progress.  */
+      ecs->event_thread->reported_event = 1;
+    }
+
   if (target_is_non_stop_p () && stop_only_if_needed)
     {
       struct thread_info *t;
diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c
index b075e33..ee638c2 100644
--- a/gdb/mi/mi-interp.c
+++ b/gdb/mi/mi-interp.c
@@ -640,6 +640,12 @@ mi_on_resume (ptid_t ptid)
       else
 	iterate_over_threads (mi_output_running_pid, &ptid);
     }
+  else if (thread_count () == 1)
+    {
+      /* Backwards compatibility.  If there's only one thread, output
+	 "all".  */
+      fprintf_unfiltered (raw_stdout, "*running,thread-id=\"all\"\n");
+    }
   else
     {
       struct thread_info *ti = find_thread_ptid (ptid);
diff --git a/gdb/testsuite/gdb.base/jump.exp b/gdb/testsuite/gdb.base/jump.exp
index adf50c6..a9b8711 100644
--- a/gdb/testsuite/gdb.base/jump.exp
+++ b/gdb/testsuite/gdb.base/jump.exp
@@ -116,7 +116,7 @@ gdb_test "jump 12" \
     "n"
 
 gdb_test "jump 12" \
-    "Continuing at.*" \
+    "Continuing .* \(.*\) at.*" \
     "jump out of current function" \
     "Line 12 is not in `main'.  Jump anyway.*y or n. $" \
     "y"
diff --git a/gdb/thread.c b/gdb/thread.c
index b42c8c3..846d2d3 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -134,6 +134,8 @@ clear_thread_inferior_resources (struct thread_info *tp)
 static void
 free_thread (struct thread_info *tp)
 {
+  xfree (tp->cmd_data);
+
   if (tp->private)
     {
       if (tp->private_dtor)


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

* [RFC/WIP PATCH v2 10/14] Parallel steps, wait for all threads to reach their goal
  2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
                   ` (12 preceding siblings ...)
  2011-12-17 12:38 ` [RFC/WIP PATCH v2 09/14] Add I/T set support to most execution commands Pedro Alves
@ 2011-12-17 14:05 ` Pedro Alves
  13 siblings, 0 replies; 16+ messages in thread
From: Pedro Alves @ 2011-12-17 14:05 UTC (permalink / raw)
  To: gdb-patches

Something I mentioned in previous patches.  This makes it so that with e.g.,

	(gdb) step -p t1,t2

	you only get the prompt back when both t1 and t2 finished
	stepping.

2011-12-16  Pedro Alves  <pedro@codesourcery.com>

	* breakpoint.c (until_break_command): Adjust.
	* gdbthread.h (struct itset): Forward declare.
	(struct thread_info) <apply_set, waiting_for_apply_set_siblings>:
	New fields.
	* infcmd.c (apply_execution_command): New parameter
	`want_parallel'.  Handle it.
	(continue_command, step_1, jump_command, signal_command)
	(until_next_command, finish_command): Adjust.
	* infrun.c (clear_proceed_status_thread): Clear the thread's apply
	set and waiting_for_apply_set_siblings flag.
	(all_in_apply_set_done_p): New.
	(handle_inferior_event): Check if there are other thread's in the
	even thread's apply set.  If so, wait for them to finish before
	considering the execution command done with.
---
 gdb/breakpoint.c |    3 ++-
 gdb/gdbthread.h  |    4 +++
 gdb/infcmd.c     |   16 +++++++++-----
 gdb/infrun.c     |   62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 78 insertions(+), 7 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 9df16b6..92929db 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -9709,6 +9709,7 @@ char *parse_execution_args (char *args, int step,
 typedef void (*aec_callback_func) (struct thread_info *thr, void *data);
 void apply_execution_command (struct itset *apply_itset,
 			      struct itset *run_free_itset,
+			      int want_parallel,
 			      aec_callback_func callback, void *callback_data);
 
 void ensure_runnable (struct thread_info *thr);
@@ -9797,7 +9798,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
     }
 
   cb_data.from_tty = from_tty;
-  apply_execution_command (apply_itset, run_free_itset,
+  apply_execution_command (apply_itset, run_free_itset, 1,
 			   until_break_aec_callback, NULL);
 
   do_cleanups (old_chain);
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 4b628c9..378d425 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -23,6 +23,7 @@
 #define GDBTHREAD_H
 
 struct symtab;
+struct itset;
 
 #include "breakpoint.h"
 #include "frame.h"
@@ -246,6 +247,9 @@ struct thread_info
   /* Data used by the execution command in effect.  */
   void *cmd_data;
 
+  struct itset *apply_set;
+  int waiting_for_apply_set_siblings;
+
   /* Private data used by the target vector implementation.  */
   struct private_thread_info *private;
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index bec876e..96ab8d9 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -92,6 +92,7 @@ int follow_fork (int should_resume);
 void
 apply_execution_command (struct itset *apply_itset,
 			 struct itset *run_free_itset,
+			 int want_parallel,
 			 aec_callback_func callback, void *callback_data)
 {
   if (target_is_non_stop_p ())
@@ -133,6 +134,9 @@ apply_execution_command (struct itset *apply_itset,
 	    {
 	      switch_to_thread (t->ptid);
 	      (*callback) (t, callback_data);
+	      gdb_assert (t->apply_set == NULL);
+	      if (want_parallel)
+		t->apply_set = itset_reference (apply_itset);
 	    }
 	  else if (t->state == THREAD_STOPPED
 		   && itset_contains_thread (run_free_itset, t))
@@ -1056,7 +1060,7 @@ continue_command (char *args, int from_tty)
   if (from_tty)
     printf_filtered (_("Continuing.\n"));
 
-  apply_execution_command (apply_itset, run_free_itset,
+  apply_execution_command (apply_itset, run_free_itset, 0,
 			   continue_aec_callback, NULL);
 
   do_cleanups (old_chain);
@@ -1207,7 +1211,7 @@ step_1 (int skip_subroutines, int single_inst, char *args)
   step_args.count = count;
   step_args.thread = -1;
 
-  apply_execution_command (apply_itset, run_free_itset,
+  apply_execution_command (apply_itset, run_free_itset, 1,
 			   step_1_aec_callback, &step_args);
 
   do_cleanups (old_chain);
@@ -1551,7 +1555,7 @@ jump_command (char *arg, int from_tty)
     }
 
   cb_data.from_tty = from_tty;
-  apply_execution_command (apply_itset, run_free_itset,
+  apply_execution_command (apply_itset, run_free_itset, 1,
 			   jump_aec_callback, &cb_data);
 
   do_cleanups (old_chain);
@@ -1661,7 +1665,7 @@ signal_command (char *arg, int from_tty)
     }
 
   cb_data.oursig = oursig;
-  apply_execution_command (apply_itset, run_free_itset,
+  apply_execution_command (apply_itset, run_free_itset, 0,
 			   signal_aec_callback, &cb_data);
 
   do_cleanups (old_chain);
@@ -1779,7 +1783,7 @@ until_next_command (char *arg, int from_tty)
 	error (_("The program is not being run."));
     }
 
-  apply_execution_command (apply_itset, run_free_itset,
+  apply_execution_command (apply_itset, run_free_itset, 1,
 			   until_next_aec_callback, NULL);
 
   do_cleanups (old_chain);
@@ -2198,7 +2202,7 @@ finish_command (char *arg, int from_tty)
     }
 
   cb_data.from_tty = from_tty;
-  apply_execution_command (apply_itset, run_free_itset,
+  apply_execution_command (apply_itset, run_free_itset, 1,
 			   finish_aec_callback, &cb_data);
 
   do_cleanups (old_chain);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index a4a2bc9..f77ecd7 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2198,6 +2198,10 @@ clear_proceed_status_thread (struct thread_info *tp)
       return;
     }
 
+  itset_free (tp->apply_set);
+  tp->apply_set = NULL;
+  tp->waiting_for_apply_set_siblings = 0;
+
   if (debug_infrun)
     fprintf_unfiltered (gdb_stdlog,
 			"infrun: clear_proceed_status_thread (%s)\n",
@@ -3902,6 +3906,43 @@ stop_all_threads (struct itset *stop_set)
     fprintf_unfiltered (gdb_stdlog, "infrun.c: stop_all_threads done\n");
 }
 
+static int
+all_in_apply_set_done_p (struct execution_control_state *ecs)
+{
+  if (ecs->event_thread->apply_set != NULL)
+    {
+      struct itset *set;
+      struct thread_info *thr;
+
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog, "infrun: thread has an apply-set\n");
+
+      update_thread_list ();
+
+      ecs->event_thread->waiting_for_apply_set_siblings = 1;
+
+      ALL_THREADS (thr)
+	if (itset_contains_thread (ecs->event_thread->apply_set, thr))
+	  {
+	    if (!thr->waiting_for_apply_set_siblings)
+	      {
+		if (debug_infrun)
+		  fprintf_unfiltered (gdb_stdlog,
+				      "infrun: at least thread %s has not reached its goal yet\n",
+				      target_pid_to_str (thr->ptid));
+
+		prepare_to_wait (ecs);
+		return 0;
+	      }
+	  }
+
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog, "infrun: all in apply-set are done\n");
+    }
+
+  return 1;
+}
+
 /* Given an execution control state that has been freshly filled in
    by an event from the inferior, figure out what it means and take
    appropriate action.  */
@@ -5870,6 +5911,10 @@ process_event_stop_test:
          one instruction.  */
       if (debug_infrun)
 	 fprintf_unfiltered (gdb_stdlog, "infrun: stepi/nexti\n");
+
+      if (!all_in_apply_set_done_p (ecs))
+	return;
+
       ecs->event_thread->control.stop_step = 1;
       print_end_stepping_range_reason ();
       stop_stepping (ecs);
@@ -5884,6 +5929,10 @@ process_event_stop_test:
          or can this happen as a result of a return or longjmp?).  */
       if (debug_infrun)
 	 fprintf_unfiltered (gdb_stdlog, "infrun: no line number info\n");
+
+      if (!all_in_apply_set_done_p (ecs))
+	return;
+
       ecs->event_thread->control.stop_step = 1;
       print_end_stepping_range_reason ();
       stop_stepping (ecs);
@@ -5917,6 +5966,9 @@ process_event_stop_test:
 	      && call_sal.symtab == ecs->event_thread->current_symtab)
 	    step_into_inline_frame (ecs->ptid);
 
+	  if (!all_in_apply_set_done_p (ecs))
+	    return;
+
 	  ecs->event_thread->control.stop_step = 1;
 	  print_end_stepping_range_reason ();
 	  stop_stepping (ecs);
@@ -5932,6 +5984,9 @@ process_event_stop_test:
 	    keep_going (ecs);
 	  else
 	    {
+	      if (!all_in_apply_set_done_p (ecs))
+		return;
+
 	      ecs->event_thread->control.stop_step = 1;
 	      print_end_stepping_range_reason ();
 	      stop_stepping (ecs);
@@ -5959,6 +6014,9 @@ process_event_stop_test:
 	keep_going (ecs);
       else
 	{
+	  if (!all_in_apply_set_done_p (ecs))
+	    return;
+
 	  ecs->event_thread->control.stop_step = 1;
 	  print_end_stepping_range_reason ();
 	  stop_stepping (ecs);
@@ -5977,6 +6035,10 @@ process_event_stop_test:
       if (debug_infrun)
 	 fprintf_unfiltered (gdb_stdlog,
 			     "infrun: stepped to a different line\n");
+
+      if (!all_in_apply_set_done_p (ecs))
+	return;
+
       ecs->event_thread->control.stop_step = 1;
       print_end_stepping_range_reason ();
       stop_stepping (ecs);


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

* RE: [RFC/WIP PATCH v2 01/12] Flip to set target-async on by default
  2011-12-17  2:59 ` [RFC/WIP PATCH v2 01/12] Flip to set target-async on by default Pedro Alves
@ 2011-12-23 18:04   ` Marc Khouzam
  0 siblings, 0 replies; 16+ messages in thread
From: Marc Khouzam @ 2011-12-23 18:04 UTC (permalink / raw)
  To: 'Pedro Alves', 'gdb-patches@sourceware.org'

> -----Original Message-----
> From: gdb-patches-owner@sourceware.org 
> [mailto:gdb-patches-owner@sourceware.org] On Behalf Of Pedro Alves
> Sent: Friday, December 16, 2011 9:59 PM
> To: gdb-patches@sourceware.org
> Subject: [RFC/WIP PATCH v2 01/12] Flip to set target-async on 
> by default
> 
> This flips "set target-async" to default to on.  Marc Khouzam's
> earlier reply (on the v1 series) indicating Eclipse does not use
> target-async for all-stop makes me think it may be prudent to actually
> try this with Eclipse and see what happens.  Haven't done that though.

I tried all-stop in Eclipse with target-async on.  As expected, things
didn't work.  Most notably, the interrupt no longer worked since Eclipse
uses ^C in all-stop.

So, I've just pushed a fix to Eclipse to explicitly set target-async to off
when in all-stop mode. http://bugs.eclipse.org/365471

Note that in Eclipse/CDT, we have two GDB integrations.  
The default and the one we work on regularly (DSF-GDB) is the one I fixed.
The older one, almost deprecated, (CDI-GDB), I didn't fix just yet because 
it wasn't as simple.  Worst case, the user will need to add
  set target-async off
in their gdbinit file, if they must use the old integration.

Happy Holidays!

Marc


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

end of thread, other threads:[~2011-12-23 16:01 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-12-17  2:59 [RFC/WIP I/T sets V2 PATCH 00/14] I/T sets Pedro Alves
2011-12-17  2:59 ` [RFC/WIP PATCH v2 01/12] Flip to set target-async on by default Pedro Alves
2011-12-23 18:04   ` Marc Khouzam
2011-12-17  2:59 ` [RFC/WIP PATCH v2 03/12] manythreads.exp: Adjust to handle threads appearing/disappearing after "Program received signal SIGFOO" Pedro Alves
2011-12-17  3:00 ` [RFC/WIP PATCH v2 04/12] ia64-sigill.exp: Don't assume there's no infrun output after the prompt Pedro Alves
2011-12-17  3:00 ` [RFC/WIP PATCH v2 11/12] Default the current itset to the current inferior set (curset) Pedro Alves
2011-12-17  3:00 ` [RFC/WIP PATCH v2 07/12] Expand %ITSET% and %THREAD% in the prompt Pedro Alves
2011-12-17  3:05 ` [RFC/WIP PATCH v2 08/12] I/T set support for breakpoints - trigger set, and stop set Pedro Alves
2011-12-17  3:07 ` [RFC/WIP PATCH v2 13/12] Comment out new info breakpoints output, in order to not break the test suite Pedro Alves
2011-12-17  3:08 ` [RFC/WIP PATCH v2 12/12] Default "set schedule-multiple" to on Pedro Alves
2011-12-17  8:49 ` [RFC/WIP PATCH v2 14/12] experiment with "scope" Pedro Alves
2011-12-17 12:35 ` [RFC/WIP PATCH v2 02/14] Implement all-stop on top of a target running non-stop mode Pedro Alves
2011-12-17 12:37 ` [RFC/WIP PATCH v2 05/14] watchthreads-reorder.exp: Don't assume there's no infrun output after the prompt Pedro Alves
2011-12-17 12:37 ` [RFC/WIP PATCH v2 06/14] Add base itsets support Pedro Alves
2011-12-17 12:38 ` [RFC/WIP PATCH v2 09/14] Add I/T set support to most execution commands Pedro Alves
2011-12-17 14:05 ` [RFC/WIP PATCH v2 10/14] Parallel steps, wait for all threads to reach their goal Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox