This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[RFC/WIP PATCH 11/14] Add I/T set support to most execution commands


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 (as set by itfocus,
or by the [SET] prefix), 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 [1.*]
warning: 3 threads for inferior 1 in the current i/t set, switching to first
New focus: Current inferior is 1.
[1.*]> 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
[1.*]> step

This last "step" commands 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:

[1.*]> step [.1, .2]

That steps threads 1 and 2, and lets all other threads in the current
focus run free.  Currently each of thread in the apply set step
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).  Instead, I think it'd make sense to wait for all threads in
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.

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

[1.*]> step [1.*]

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

[1.*]> 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 [] 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:

[1.*]> [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.,

[1.*]> [1.2] step [1.2]

That is, the [1.2] prefix sets the current set temporarily to thread
2, and the apply set is also set to thread 2.

Or, you may more simply do:

[1.*]> [] step [1.2]

That is, the [] prefix sets the current set temporarily to the empty
set, and the apply set is thread 2.

Or, you may also do:

[1.*]> [] 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:

[1.2]> step
[1.*]> [1.2] step
[1.*]> [] step
[1.*]> 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 let's all threads in [all]
run free.  That is, it's 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 missing:

 - This whole series is not regression free yet.

 - "step [set]" should wait for all thread in the set to finish the
   step.

 - 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.

 - Probably many other things I'm not remembering right now.
---
 gdb/breakpoint.c                |  166 ++++++--
 gdb/gdbthread.h                 |    5 
 gdb/infcall.c                   |   23 +
 gdb/infcmd.c                    |  831 +++++++++++++++++++++++++++++++--------
 gdb/inferior.h                  |    7 
 gdb/infrun.c                    |  100 +++--
 gdb/itset.c                     |    8 
 gdb/itset.h                     |    2 
 gdb/testsuite/gdb.base/jump.exp |    2 
 gdb/thread.c                    |    2 
 10 files changed, 899 insertions(+), 247 deletions(-)

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 75badf9..d642651 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -9799,64 +9799,149 @@ 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_itset,
+			    struct itset **run_free_itset);
+
+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;
+  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, &run_free_itset);
 
-  if (last_displayed_sal_is_valid ())
-    sals = decode_line_1 (&arg, 1,
-			  get_last_displayed_symtab (),
-			  get_last_displayed_line (),
-			  NULL);
-  else
-    sals = decode_line_1 (&arg, 1, (struct symtab *) NULL, 0, NULL);
+  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, 1,
+				get_last_displayed_symtab (),
+				get_last_displayed_line (),
+				NULL);
+	else
+	  sals = decode_line_1 (&arg, 1, (struct symtab *) NULL, 0, NULL);
+
+	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;
+      }
+
+  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),
@@ -9865,10 +9950,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
@@ -9883,7 +9969,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 0135219..7598f6a 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 50f1289..7149db4 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -38,6 +38,14 @@
 #include "ada-lang.h"
 #include "gdbthread.h"
 #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.  */
@@ -413,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 7b935fe..e22c87f 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -57,6 +57,102 @@
 #include "tracepoint.h"
 #include "inf-loop.h"
 #include "continuations.h"
+#include "itset.h"
+#include "cli/cli-utils.h"
+
+
+struct itset *
+current_thread_set (void)
+{
+  struct itset *set;
+  struct inferior *inf;
+  struct thread_info *tp;
+  char *b;
+
+  inf = current_inferior ();
+  tp = inferior_thread ();
+
+  b = alloca (256);
+  sprintf (b, "[%d.%d]", inf->num, 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
+    {
+      (*callback) (inferior_thread (), callback_data);
+    }
+}
 
 /* Functions exported for general use, in inferior.h: */
 
@@ -79,7 +175,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);
 
@@ -596,7 +692,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;
@@ -656,11 +752,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;
 }
@@ -713,23 +809,182 @@ 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, "%d.*", inf->num);
+	      }
+	    else
+	      sprintf (buf, ",%d.*", inf->num);
+
+	    set_spec = reconcat (set_spec, set_spec, buf, (char *) NULL);
+	  }
+      set_spec = reconcat (set_spec, set_spec, "]", (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,
+		      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);
+	      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 (*args == '[')
+	{
+	  if (*apply_itset)
+	    itset_free (*apply_itset);
+	  *apply_itset = itset_create (&args);
+	  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)
+{
+  proceed_thread_callback (thread, NULL);
+}
+
 /* continue [-a] [proceed-count] [&]  */
 void
 continue_command (char *args, int from_tty)
 {
   int async_exec = 0;
   int all_threads = 0;
+  int ignore_count = 0;
+  int ignore_count_p = 0;
+  struct itset *apply_itset = NULL;
+  struct itset *run_free_itset = NULL;
+  struct cleanup *old_chain;
   ERROR_NO_INFERIOR;
 
   /* 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, &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))
+    error (_("Set of threads to continue is empty."));
+
   /* 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 ())
@@ -738,17 +993,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."));
 
@@ -756,9 +1000,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;
@@ -781,9 +1025,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)
@@ -801,7 +1043,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);
 }
 
 /* Record the starting point of a "step" or "next" command.  */
@@ -852,36 +1097,106 @@ 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;
+  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, &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)
+    error (_("Set of threads to step is empty."));
+
   /* 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.  */
     {
@@ -935,14 +1250,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
@@ -951,7 +1258,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)
     {
@@ -1072,7 +1379,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;
@@ -1086,23 +1393,53 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
 }
 
 
+
+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;
+  struct itset *run_free_itset = NULL;
+  struct cleanup *old_chain;
+  struct thread_info *thr;
+  int thr_count = 0;
+  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)
@@ -1113,59 +1450,71 @@ jump_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, &run_free_itset);
   if (!arg)
     error_no_arg (_("starting address"));
 
-  sals = decode_line_spec_1 (arg, 1);
-  if (sals.nelts != 1)
-    {
-      error (_("Unreasonable jump request"));
-    }
+  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;
 
-  sal = sals.sals[0];
-  xfree (sals.sals);
+	++thr_count;
 
-  if (sal.symtab == 0 && sal.pc == 0)
-    error (_("No source file has been specified."));
+	ensure_runnable (thr);
 
-  resolve_sal_pc (&sal);	/* May error out.  */
+	if (!ptid_equal (inferior_ptid, thr->ptid))
+	  switch_to_thread (thr->ptid);
 
-  /* 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 */
-	}
-    }
+	sals = decode_line_spec_1 (arg, 1);
+	if (sals.nelts != 1)
+	  error (_("Unreasonable jump request for thread %s"),
+		 target_pid_to_str (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? ")))
-	    {
+	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 (thr_count == 0)
+    error (_("Set of threads to jump is empty."));
 
   /* If we are not asked to run in the bg, then prepare to run in the
      foreground, synchronously.  */
@@ -1175,8 +1524,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);
 }
 
 
@@ -1196,27 +1548,43 @@ 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;
+  struct itset *run_free_itset = NULL;
+  struct cleanup *old_chain;
+  struct signal_aec_callback_data cb_data;
+  struct thread_info *thr;
+  int thr_count = 0;
 
   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, &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 ())
@@ -1225,18 +1593,29 @@ signal_command (char *signum_exp, int from_tty)
       async_disable_stdin ();
     }
 
-  if (!signum_exp)
+  if (!arg)
     error_no_arg (_("signal number"));
 
+  ALL_THREADS (thr)
+    if (itset_contains_thread (apply_itset, thr))
+      {
+	++thr_count;
+
+	ensure_runnable (thr);
+      }
+
+  if (thr_count == 0)
+    error (_("Set of threads to signal is empty."));
+
   /* 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;
@@ -1253,8 +1632,20 @@ signal_command (char *signum_exp, int from_tty)
 			 target_signal_to_name (oursig));
     }
 
-  clear_proceed_status ();
-  proceed ((CORE_ADDR) -1, oursig, 0);
+  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_thread (thread);
+  proceed ((CORE_ADDR) -1, d->oursig, 0);
 }
 
 /* Continuation args to be passed to the "until" command
@@ -1283,51 +1674,91 @@ 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;
+  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, &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_thread (thr);
+	set_step_frame ();
 
-      tp->control.step_range_start = BLOCK_START (SYMBOL_BLOCK_VALUE (func));
-      tp->control.step_range_end = sal.end;
-    }
+	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);
+
+	    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 */
+      }
 
-  tp->control.step_over_calls = STEP_OVER_ALL;
+  if (thr_count == 0)
+    error (_("Set of threads to until is empty."));
 
-  tp->step_multi = 0;		/* Only one call to proceed */
+  apply_execution_command (apply_itset, run_free_itset,
+			   until_next_aec_callback, NULL);
 
+  do_cleanups (old_chain);
+}
+
+
+
+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);
 
@@ -1354,8 +1785,6 @@ until_command (char *arg, int from_tty)
 
   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)
@@ -1377,7 +1806,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
@@ -1387,8 +1816,6 @@ advance_command (char *arg, int from_tty)
 
   ERROR_NO_INFERIOR;
   ensure_not_tfind_mode ();
-  ensure_valid_thread ();
-  ensure_not_running ();
 
   if (arg == NULL)
     error_no_arg (_("a location"));
@@ -1648,21 +2075,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;
+  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)
@@ -1673,6 +2113,11 @@ 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, &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 ())
@@ -1684,29 +2129,73 @@ 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)
+    error (_("Set of threads to finish is empty."));
+
+  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;
 
@@ -1715,7 +2204,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);
@@ -1724,7 +2213,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.  */
@@ -1735,13 +2224,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);
 }
 
 
@@ -2693,10 +3182,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,
@@ -2704,7 +3214,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);
 }
 
@@ -2727,9 +3237,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 559b4f1..add97e7 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -105,6 +105,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;
@@ -168,6 +170,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 c279508..6b16184 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,
 }
 
 
-/* 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 void
 displaced_step_fixup (ptid_t event_ptid, enum target_signal signal)
@@ -1587,16 +1595,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)
@@ -1669,9 +1677,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;
     }
@@ -1686,7 +1694,7 @@ user_visible_resume_ptid (int step)
   return resume_ptid;
 }
 
-static void
+void
 do_target_resume (ptid_t ptid, int step, enum target_signal signo)
 {
   int resume_many;
@@ -1734,6 +1742,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)
 	    {
@@ -2074,7 +2083,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)
@@ -2120,7 +2129,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
@@ -2133,7 +2142,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.  */
@@ -2255,7 +2264,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 ();
@@ -2308,7 +2317,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.  */
     ;
@@ -2676,7 +2685,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
@@ -2879,7 +2888,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);
@@ -3181,20 +3190,18 @@ fetch_inferior_event (void *client_data)
       /* cancel breakpoints */
     }
 
-  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);
@@ -4158,7 +4165,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;
@@ -4567,7 +4574,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.  */
@@ -5207,7 +5214,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;
 
@@ -6191,6 +6198,11 @@ 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)
+    {
+      ecs->event_thread->reported_event = 1;
+    }
+
   if (target_is_non_stop_p ()
       && stop_only_if_needed)
     {
diff --git a/gdb/itset.c b/gdb/itset.c
index bd2c74a..717d261 100644
--- a/gdb/itset.c
+++ b/gdb/itset.c
@@ -1492,6 +1492,14 @@ itset_create_stopped (void)
   return itset_create (&spec);
 }
 
+struct itset *
+itset_create_empty (void)
+{
+  char *spec = "[]";
+
+  return itset_create (&spec);
+}
+
 /* Return 1 if SET contains INF, 0 otherwise.  */
 
 int
diff --git a/gdb/itset.h b/gdb/itset.h
index 9574275..da08477 100644
--- a/gdb/itset.h
+++ b/gdb/itset.h
@@ -67,6 +67,8 @@ const char *itset_spec (const struct itset *itset);
 
 int itset_is_empty (const struct itset *itset);
 
+struct itset *itset_create_empty (void);
+
 /* Acquire a new reference to an I/T set.  Returns the I/T set, for
    convenience.  */
 
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)


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]