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]

[non-stop] 04/10 per-thread commands in non-stop mode


In non-stop mode, we can have one exec command active per-thread,
and they're independant, while in all-stop, there can only be one,
and it's global.  In non-stop mode, whenever we switch threads,
and because each is handled independently of the others, whenever
we proceed the inferior, the context has to be set to that
thread, unlike in all-stop, where we keep the state set to the last
thread that had an event, and revert to it while proceeding.

So we have:

 all-stop mode:
  - There can only be one exec command active at any time.

 non-stop mode:
  - There can be more than one exec command active at any time.
  - Any given thread can only have one command active at any time.

A continuation is a mechanism to finish a command.  In all-stop
mode, when the inferior stops and GDB finishes handling that
event, we give the user the control again.  The previous command
is finished at this point.  It is up to the user to decide
what to do next.  Since we only allow one exec command at any
given time, the next exec command the user issues fully
replaces the previous one.

In non-stop, we can have more than one exec command active,
although only one per thread.  Since continuations are a
mechanism of finishing a command, that is, they
implement 3) below:

 1) prepare inferior (insert internal breakpoints, etc.)
 2) resume inferior and wait for stop
 3) finish or cleanup after the command (remove internal
    breakpoints, etc.)

... and we can have a command per thread, it makes sense to
make each thread have a continuation.

Now, some commands carry "command state" with them.  The biggest
example is the "step"-like commands:  step/stepi/next.  When
we have per-thread commands, we also need to store
individual step-like command state per thread.

This means that in non-stop, we have to make more things per-thread,
as per the patch.  We also add the necessary context-switching glue
so in non-stop the context is always set to the current thread.

-- 
Pedro Alves
2008-06-15  Pedro Alves  <pedro@codesourcery.com>
	    Vladimir Prus  <vladimir@codesourcery.com>

	* gdbthread.h: Remove unneeded forward declarations.
	Include "inferior.h".
	(struct thread_info): Add continuations,
	intermediate_continuations, proceed_to_finish, step_over_calls,
	stop_step, step_multi and stop_signal members.
	(save_infrun_state): Add continuations,
	intermediate_continuations, proceed_to_finish, step_over_calls,
	stop_step, step_multi, stop_signal and stop_bpstat parameters.
	(load_infrun_state): Add continuations,
	intermediate_continuations, proceed_to_finish, step_over_calls,
	stop_step, step_multi, stop_signal and stop_bpstat parameters.

	* thread.c (load_infrun_state): In non-stop mode, load
	continuations, intermediate_continuations, proceed_to_finish,
	step_over_calls, stop_step, step_multi and stop_signal.
	(save_infrun_state): Store continuations,
	intermediate_continuations, proceed_to_finish, step_over_calls,
	stop_step, step_multi, stop_signal and stop_bpstat.
	(save_infrun_state): Store continuations,
	intermediate_continuations, proceed_to_finish, step_over_calls,
	stop_step, step_multi, stop_signal and stop_bpstat.
	(free_thread): Clear The thread's stop_bpstat.

	* inferior.h (context_switch_to): Declare.
	
	* infrun.c (ecss): New global.
	(context_switch): Context switch continuations,
	intermediate_continuations, proceed_to_finish, step_over_calls,
	stop_step, step_multi, stop_signal and stop_bpstat.
	(wait_for_inferior): Use global ecss.
	(async_ecss, async_ecs): Delete.
	(fetch_inferior_event): Use global ecss.
	(context_switch_to): New.

	* top.c (execute_command): In non-stop, only check if the current
	thread is running, in all-stop, check if there's any thread
	running.

	* breakpoint.c (bpstat_remove_breakpoint): New.
	(bpstat_remove_breakpoint_callback): New.
	(delete_breakpoint): Clear the stop_bpstats of all threads.

	* mi/mi-main.c (mi_cmd_execute): In non-stop, only check if the
	current thread is running, in all-stop, check if there's any
	thread running.

	* Makefile.in (gdbthread_h): Depend on $(inferior_h).

---
 gdb/Makefile.in  |    2 -
 gdb/breakpoint.c |   36 ++++++++++++++++++++++-------
 gdb/gdbthread.h  |   43 +++++++++++++++++++++++++++--------
 gdb/inferior.h   |    2 +
 gdb/infrun.c     |   67 ++++++++++++++++++++++++++++++++++++-------------------
 gdb/mi/mi-main.c |    3 +-
 gdb/thread.c     |   56 ++++++++++++++++++++++++++++++++++++++++++++-
 gdb/top.c        |    3 +-
 8 files changed, 168 insertions(+), 44 deletions(-)

Index: src/gdb/gdbthread.h
===================================================================
--- src.orig/gdb/gdbthread.h	2008-06-15 17:57:52.000000000 +0100
+++ src/gdb/gdbthread.h	2008-06-15 19:40:21.000000000 +0100
@@ -22,17 +22,12 @@
 #ifndef GDBTHREAD_H
 #define GDBTHREAD_H
 
-struct breakpoint;
-struct frame_id;
 struct symtab;
 
-/* For bpstat */
 #include "breakpoint.h"
-
-/* For struct frame_id.  */
 #include "frame.h"
-
 #include "ui-out.h"
+#include "inferior.h"
 
 struct thread_info
 {
@@ -82,6 +77,20 @@ struct thread_info
      when we finally do stop stepping.  */
   bpstat stepping_through_solib_catchpoints;
 
+  /* The below are only per-thread in non-stop mode.  */
+  /* Per-thread command support.  */
+  struct continuation *continuations;
+  struct continuation *intermediate_continuations;
+  int proceed_to_finish;
+  enum step_over_calls_kind step_over_calls;
+  int stop_step;
+  int step_multi;
+
+  enum target_signal stop_signal;
+  /* Used in continue_command to set the proceed count of the
+     breakpoint the thread stopped at.  */
+  bpstat stop_bpstat;
+
   /* Private data used by the target vector implementation.  */
   struct private_thread_info *private;
 };
@@ -145,7 +154,15 @@ extern void save_infrun_state (ptid_t pt
 			       int       stepping_through_solib_after_catch,
 			       bpstat    stepping_through_solib_catchpoints,
 			       int       current_line,
-			       struct symtab *current_symtab);
+			       struct symtab *current_symtab,
+			       struct continuation *continuations,
+			       struct continuation *intermediate_continuations,
+			       int proceed_to_finish,
+			       enum step_over_calls_kind step_over_calls,
+			       int stop_step,
+			       int step_multi,
+			       enum target_signal stop_signal,
+			       bpstat stop_bpstat);
 
 /* infrun context switch: load the debugger state previously saved
    for the given thread.  */
@@ -157,10 +174,18 @@ extern void load_infrun_state (ptid_t pt
 			       CORE_ADDR *step_range_end,
 			       struct frame_id *step_frame_id,
 			       int       *another_trap,
-			       int       *stepping_through_solib_affter_catch,
+			       int       *stepping_through_solib_after_catch,
 			       bpstat    *stepping_through_solib_catchpoints,
 			       int       *current_line,
-			       struct symtab **current_symtab);
+			       struct symtab **current_symtab,
+			       struct continuation **continuations,
+			       struct continuation **intermediate_continuations,
+			       int *proceed_to_finish,
+			       enum step_over_calls_kind *step_over_calls,
+			       int *stop_step,
+			       int *step_multi,
+			       enum target_signal *stop_signal,
+			       bpstat *stop_bpstat);
 
 /* Switch from one thread to another.  */
 extern void switch_to_thread (ptid_t ptid);
Index: src/gdb/thread.c
===================================================================
--- src.orig/gdb/thread.c	2008-06-15 17:57:52.000000000 +0100
+++ src/gdb/thread.c	2008-06-15 19:40:21.000000000 +0100
@@ -93,6 +93,8 @@ free_thread (struct thread_info *tp)
   if (tp->step_resume_breakpoint)
     tp->step_resume_breakpoint->disposition = disp_del_at_next_stop;
 
+  bpstat_clear (&tp->stop_bpstat);
+
   /* FIXME: do I ever need to call the back-end to give it a
      chance at this private data before deleting the thread?  */
   if (tp->private)
@@ -331,7 +333,15 @@ load_infrun_state (ptid_t ptid,
 		   int *stepping_through_solib_after_catch,
 		   bpstat *stepping_through_solib_catchpoints,
 		   int *current_line,
-		   struct symtab **current_symtab)
+		   struct symtab **current_symtab,
+		   struct continuation **continuations,
+		   struct continuation **intermediate_continuations,
+		   int *proceed_to_finish,
+		   enum step_over_calls_kind *step_over_calls,
+		   int *stop_step,
+		   int *step_multi,
+		   enum target_signal *stop_signal,
+		   bpstat *stop_bpstat)
 {
   struct thread_info *tp;
 
@@ -354,6 +364,26 @@ load_infrun_state (ptid_t ptid,
     tp->stepping_through_solib_catchpoints;
   *current_line = tp->current_line;
   *current_symtab = tp->current_symtab;
+
+  /* In all-stop mode, these are global state, while in non-stop mode,
+     they are per thread.  */
+  if (non_stop)
+    {
+      *continuations = tp->continuations;
+      tp->continuations = NULL;
+      *intermediate_continuations = tp->intermediate_continuations;
+      tp->intermediate_continuations = NULL;
+      *proceed_to_finish = tp->proceed_to_finish;
+      *step_over_calls = tp->step_over_calls;
+      *stop_step = tp->stop_step;
+      *step_multi = tp->step_multi;
+      *stop_signal = tp->stop_signal;
+
+      /* Swap instead of copy, so we only have to update one of
+	 them.  */
+      *stop_bpstat = tp->stop_bpstat;
+      tp->stop_bpstat = 0;
+    }
 }
 
 /* Save infrun state for the thread PID.  */
@@ -370,7 +400,15 @@ save_infrun_state (ptid_t ptid,
 		   int stepping_through_solib_after_catch,
 		   bpstat stepping_through_solib_catchpoints,
 		   int current_line,
-		   struct symtab *current_symtab)
+		   struct symtab *current_symtab,
+		   struct continuation *continuations,
+		   struct continuation *intermediate_continuations,
+		   int proceed_to_finish,
+		   enum step_over_calls_kind step_over_calls,
+		   int stop_step,
+		   int step_multi,
+		   enum target_signal stop_signal,
+		   bpstat stop_bpstat)
 {
   struct thread_info *tp;
 
@@ -391,6 +429,20 @@ save_infrun_state (ptid_t ptid,
   tp->stepping_through_solib_catchpoints = stepping_through_solib_catchpoints;
   tp->current_line = current_line;
   tp->current_symtab = current_symtab;
+
+  /* In all-stop mode, these are global state, while in non-stop mode,
+     they are per thread.  */
+  if (non_stop)
+    {
+      tp->continuations = continuations;
+      tp->intermediate_continuations = intermediate_continuations;
+      tp->proceed_to_finish = proceed_to_finish;
+      tp->step_over_calls = step_over_calls;
+      tp->stop_step = stop_step;
+      tp->step_multi = step_multi;
+      tp->stop_signal = stop_signal;
+      tp->stop_bpstat = stop_bpstat;
+    }
 }
 
 /* Return true if TP is an active thread. */
Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c	2008-06-15 19:40:18.000000000 +0100
+++ src/gdb/infrun.c	2008-06-15 20:01:30.000000000 +0100
@@ -288,6 +288,8 @@ static struct breakpoint *step_resume_br
 static ptid_t target_last_wait_ptid;
 static struct target_waitstatus target_last_waitstatus;
 
+struct execution_control_state ecss;
+
 /* This is used to remember when a fork, vfork or exec event
    was caught by a catchpoint, and thus the event is to be
    followed at the next resume of the inferior, and not
@@ -1426,7 +1428,6 @@ void
 wait_for_inferior (int treat_exec_as_sigtrap)
 {
   struct cleanup *old_cleanups;
-  struct execution_control_state ecss;
   struct execution_control_state *ecs;
 
   if (debug_infrun)
@@ -1437,8 +1438,6 @@ wait_for_inferior (int treat_exec_as_sig
   old_cleanups = make_cleanup (delete_step_resume_breakpoint,
 			       &step_resume_breakpoint);
 
-  /* wfi still stays in a loop, so it's OK just to take the address of
-     a local to get the ecs pointer.  */
   ecs = &ecss;
 
   /* Fill in with reasonable starting values.  */
@@ -1484,25 +1483,20 @@ wait_for_inferior (int treat_exec_as_sig
    event loop whenever a change of state is detected on the file
    descriptor corresponding to the target. It can be called more than
    once to complete a single execution command. In such cases we need
-   to keep the state in a global variable ASYNC_ECSS. If it is the
-   last time that this function is called for a single execution
-   command, then report to the user that the inferior has stopped, and
-   do the necessary cleanups. */
-
-struct execution_control_state async_ecss;
-struct execution_control_state *async_ecs;
+   to keep the state in a global variable ECSS. If it is the last time
+   that this function is called for a single execution command, then
+   report to the user that the inferior has stopped, and do the
+   necessary cleanups. */
 
 void
 fetch_inferior_event (void *client_data)
 {
-  static struct cleanup *old_cleanups;
-
-  async_ecs = &async_ecss;
+  struct execution_control_state *ecs = &ecss;
 
-  if (!async_ecs->wait_some_more)
+  if (!ecs->wait_some_more)
     {
       /* Fill in with reasonable starting values.  */
-      init_execution_control_state (async_ecs);
+      init_execution_control_state (ecs);
 
       /* We'll update this if & when we switch to a new thread. */
       previous_inferior_ptid = inferior_ptid;
@@ -1519,15 +1513,15 @@ fetch_inferior_event (void *client_data)
     }
 
   if (deprecated_target_wait_hook)
-    async_ecs->ptid =
-      deprecated_target_wait_hook (async_ecs->waiton_ptid, async_ecs->wp);
+    ecs->ptid =
+      deprecated_target_wait_hook (ecs->waiton_ptid, ecs->wp);
   else
-    async_ecs->ptid = target_wait (async_ecs->waiton_ptid, async_ecs->wp);
+    ecs->ptid = target_wait (ecs->waiton_ptid, ecs->wp);
 
   /* Now figure out what to do with the result of the result.  */
-  handle_inferior_event (async_ecs);
+  handle_inferior_event (ecs);
 
-  if (!async_ecs->wait_some_more)
+  if (!ecs->wait_some_more)
     {
       delete_step_resume_breakpoint (&step_resume_breakpoint);
 
@@ -1605,7 +1599,14 @@ context_switch (struct execution_control
 			 ecs->stepping_over_breakpoint,
 			 ecs->stepping_through_solib_after_catch,
 			 ecs->stepping_through_solib_catchpoints,
-			 ecs->current_line, ecs->current_symtab);
+			 ecs->current_line, ecs->current_symtab,
+			 cmd_continuation, intermediate_continuation,
+			 proceed_to_finish,
+			 step_over_calls,
+			 stop_step,
+			 step_multi,
+			 stop_signal,
+			 stop_bpstat);
 
       /* Load infrun state for the new thread.  */
       load_infrun_state (ecs->ptid, &prev_pc,
@@ -1615,12 +1616,34 @@ context_switch (struct execution_control
 			 &ecs->stepping_over_breakpoint,
 			 &ecs->stepping_through_solib_after_catch,
 			 &ecs->stepping_through_solib_catchpoints,
-			 &ecs->current_line, &ecs->current_symtab);
+			 &ecs->current_line, &ecs->current_symtab,
+			 &cmd_continuation, &intermediate_continuation,
+			 &proceed_to_finish,
+			 &step_over_calls,
+			 &stop_step,
+			 &step_multi,
+			 &stop_signal,
+			 &stop_bpstat);
     }
 
   switch_to_thread (ecs->ptid);
 }
 
+/* Context switch to thread PTID.  */
+ptid_t
+context_switch_to (ptid_t ptid)
+{
+  ptid_t current_ptid = inferior_ptid;
+
+  /* Context switch to the new thread.	*/
+  if (!ptid_equal (ptid, inferior_ptid))
+    {
+      ecss.ptid = ptid;
+      context_switch (&ecss);
+    }
+  return current_ptid;
+}
+
 static void
 adjust_pc_after_break (struct execution_control_state *ecs)
 {
Index: src/gdb/Makefile.in
===================================================================
--- src.orig/gdb/Makefile.in	2008-06-15 17:57:52.000000000 +0100
+++ src/gdb/Makefile.in	2008-06-15 19:40:21.000000000 +0100
@@ -801,7 +801,7 @@ gdb_stabs_h = gdb-stabs.h
 gdb_stat_h = gdb_stat.h
 gdb_string_h = gdb_string.h
 gdb_thread_db_h = gdb_thread_db.h
-gdbthread_h = gdbthread.h $(breakpoint_h) $(frame_h) $(ui_out_h)
+gdbthread_h = gdbthread.h $(breakpoint_h) $(frame_h) $(ui_out_h) $(inferior_h)
 gdbtypes_h = gdbtypes.h $(hashtab_h)
 gdb_vfork_h = gdb_vfork.h
 gdb_wait_h = gdb_wait.h
Index: src/gdb/top.c
===================================================================
--- src.orig/gdb/top.c	2008-06-15 17:57:52.000000000 +0100
+++ src/gdb/top.c	2008-06-15 19:40:21.000000000 +0100
@@ -419,7 +419,8 @@ execute_command (char *p, int from_tty)
          commands. */
       if (target_can_async_p ()
 	  && target_has_execution
-	  && any_running ()
+ 	  && ((!non_stop && any_running ())
+ 	      || (non_stop && is_running (inferior_ptid)))
 	  && !get_cmd_async_ok (c))
 	error (_("Cannot execute this command while the target is running."));
 
Index: src/gdb/breakpoint.c
===================================================================
--- src.orig/gdb/breakpoint.c	2008-06-15 19:40:17.000000000 +0100
+++ src/gdb/breakpoint.c	2008-06-15 19:40:21.000000000 +0100
@@ -7090,6 +7090,29 @@ update_global_location_list_nothrow (voi
     update_global_location_list ();
 }
 
+/* Clear BPT from a BPS.  */
+static void
+bpstat_remove_breakpoint (bpstat bps, struct breakpoint *bpt)
+{
+  bpstat bs;
+  for (bs = bps; bs; bs = bs->next)
+    if (bs->breakpoint_at && bs->breakpoint_at->owner == bpt)
+      {
+	bs->breakpoint_at = NULL;
+	bs->old_val = NULL;
+	/* bs->commands will be freed later.  */
+      }
+}
+
+/* Callback for iterate_over_threads.  */
+static int
+bpstat_remove_breakpoint_callback (struct thread_info *th, void *data)
+{
+  struct breakpoint *bpt = data;
+  bpstat_remove_breakpoint (th->stop_bpstat, bpt);
+  return 0;
+}
+
 /* Delete a breakpoint and clean up all traces of it in the data
    structures. */
 
@@ -7097,7 +7120,6 @@ void
 delete_breakpoint (struct breakpoint *bpt)
 {
   struct breakpoint *b;
-  bpstat bs;
   struct bp_location *loc, *next;
 
   gdb_assert (bpt != NULL);
@@ -7161,13 +7183,11 @@ delete_breakpoint (struct breakpoint *bp
          bpstat_do_actions (&stop_bpstat);
      in event-top.c won't do anything, and temporary breakpoints
      with commands won't work.  */
-  for (bs = stop_bpstat; bs; bs = bs->next)
-    if (bs->breakpoint_at && bs->breakpoint_at->owner == bpt)
-      {
-	bs->breakpoint_at = NULL;
-	bs->old_val = NULL;
-	/* bs->commands will be freed later.  */
-      }
+
+  /* Clear the current context.  */
+  bpstat_remove_breakpoint (stop_bpstat, bpt);
+  /* And from all threads.  */
+  iterate_over_threads (bpstat_remove_breakpoint_callback, bpt);
 
   /* Now that breakpoint is removed from breakpoint
      list, update the global location list.  This
Index: src/gdb/mi/mi-main.c
===================================================================
--- src.orig/gdb/mi/mi-main.c	2008-06-15 17:57:52.000000000 +0100
+++ src/gdb/mi/mi-main.c	2008-06-15 19:40:21.000000000 +0100
@@ -1195,7 +1195,8 @@ mi_cmd_execute (struct mi_parse *parse)
 
   if (parse->cmd->argv_func != NULL)
     {
-      if (is_running (inferior_ptid))
+      if ((!non_stop && any_running ())
+	  || (non_stop && is_running (inferior_ptid)))
 	{
 	  if (strcmp (parse->command, "exec-interrupt"))
 	    {
Index: src/gdb/inferior.h
===================================================================
--- src.orig/gdb/inferior.h	2008-06-15 20:01:08.000000000 +0100
+++ src/gdb/inferior.h	2008-06-15 20:01:30.000000000 +0100
@@ -132,6 +132,8 @@ extern void clear_proceed_status (void);
 
 extern void proceed (CORE_ADDR, enum target_signal, int);
 
+extern ptid_t context_switch_to (ptid_t ptid);
+
 /* When set, stop the 'step' command if we enter a function which has
    no line number information.  The normal behavior is that we step
    over such function.  */

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