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] 07/10 non-stop inferior control


This patch adds the inferior control support for non-stop mode.

In non-stop mode, each thread is handled individually.  It should
be like you have a separate debugger attached to each thread.
To accomplish that, as soon as we have an event, we context switch
to it, and go on handling it.  The cases of hiting a breakpoint
in another thread while we're stepping don't need to be handled
specially, as the stepping thread will have its state, and the
other thread will have its own state.

Every exec command should apply only to the selected thread.

A new target_stop_ptid method was added to request the target
to interrupt a single thread.

Several checks have been added so GDB doesn't try to do
things with running threads, which don't make sense,
like asking for the current PC of a running thread.

Info threads now shows the running state of a thread.  MI
support can be added on top.

(gdb) info threads
 3 Thread 0xf7603b90 (LWP 23454) Â(running)
* 2 Thread 0xf7e04b90 (LWP 23453) Â0xffffe410 in __kernel_vsyscall ()
 1 Thread 0xf7e056b0 (LWP 23450) Â(running)

-- 
Pedro Alves
2008-05-06  Pedro Alves  <pedro@codesourcery.com>

	* infrun.c (resume): In non-stop mode, always resume just one
	thread.
	(prepare_to_proceed): Do nothing in non-stop mode.
	(fetch_inferior_event): In non-stop mode, switch context before
	handling the event.
	(error_is_running, ensure_not_running): New.
	(handle_inferior_event): In non-stop mode: Mark only the event
	thread as stopped. Require that the target module handles new
	threads correctly.  Don't switch to infwait_thread_hop_state.
	(normal_stop): Only mark not-running if inferior hasn't exited.
	In non-stop mode, only mark the event thread.

	* thread.c (print_thread_info): Don't read from a running thread.
	Output "(running)" if thread is running.
	(switch_to_thread): Don't read stop_pc if thread is running.
	(do_restore_current_thread_cleanup): Don't write to a running
	thread.
	(thread_apply_all_command): Don't read from a running thread.  In
	non-stop mode, do a full context-switch instead of just switching
	threads.
	(thread_apply_command): In non-stop mode, do a full context-switch
	instead of just switching threads.
	(do_captured_thread_select): Likewise.  Inform user if selected
	thread is running.

	* inf-loop.c (inferior_event_handler): In non-stop mode, don't
	unregister the target from the event loop.
	
	* infcmd.c (continue_command, step_1, jump_command,
	signal_command): Ensure the selected thread isn't running.
	(interrupt_target_command): In non-stop mode, either interrupt
	only the selected thread, or read the thread id to interrupt from
	a parameter.

	* inferior.h (error_is_running, ensure_not_running): Declare.

	* target.h (struct target_ops): Add to_stop_ptid member.
	(target_stop_ptid): Define.
	* target.c (update_current_target): Inherit to_stop_ptid.  Default
	to_stop_ptid to target_ignore.
	(debug_to_stop_ptid): New.
	(debug_to_rcmd): Set to_stop_ptid.

---
 gdb/inf-loop.c |   14 +++++---
 gdb/infcmd.c   |   25 ++++++++++++++-
 gdb/inferior.h |    6 +++
 gdb/infrun.c   |   78 ++++++++++++++++++++++++++++++++++++++++++-----
 gdb/target.c   |   13 +++++++
 gdb/target.h   |    4 ++
 gdb/thread.c   |   93 ++++++++++++++++++++++++++++++++++++++++-----------------
 7 files changed, 191 insertions(+), 42 deletions(-)

Index: src/gdb/infrun.c
===================================================================
--- src.orig/gdb/infrun.c	2008-05-06 15:09:23.000000000 +0100
+++ src/gdb/infrun.c	2008-05-06 15:52:30.000000000 +0100
@@ -1052,9 +1052,15 @@ a command like `return' or `jump' to con
 	  resume_ptid = inferior_ptid;
 	}
 
-      if ((scheduler_mode == schedlock_on)
-	  || (scheduler_mode == schedlock_step
-	      && (step || singlestep_breakpoints_inserted_p)))
+      if (non_stop)
+	{
+	  /* With non-stop mode on, threads are always handled
+	     individually.  */
+	  resume_ptid = inferior_ptid;
+	}
+      else if ((scheduler_mode == schedlock_on)
+	       || (scheduler_mode == schedlock_step
+		   && (step || singlestep_breakpoints_inserted_p)))
 	{
 	  /* User-settable 'scheduler' mode requires solo thread resume. */
 	  resume_ptid = inferior_ptid;
@@ -1125,6 +1131,11 @@ prepare_to_proceed (int step)
   ptid_t wait_ptid;
   struct target_waitstatus wait_status;
 
+  if (non_stop)
+    /* In non-stop, each thread is handled individually.  The context
+       must already be set to the right thread here.  */
+    return 0;
+
   /* Get the last target status returned by target_wait().  */
   get_last_target_status (&wait_ptid, &wait_status);
 
@@ -1529,6 +1540,15 @@ fetch_inferior_event (void *client_data)
   else
     ecs->ptid = target_wait (waiton_ptid, &ecs->ws);
 
+  if (non_stop
+      && ecs->ws.kind != TARGET_WAITKIND_IGNORE
+      && 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
+       thread.  */
+    context_switch (ecs->ptid);
+
   /* Now figure out what to do with the result of the result.  */
   handle_inferior_event (ecs);
 
@@ -1737,6 +1757,20 @@ init_infwait_state (void)
   infwait_state = infwait_normal_state;
 }
 
+void
+error_is_running (void)
+{
+  error (_("\
+Cannot execute this command while the selected thread is running."));
+}
+
+void
+ensure_not_running (void)
+{
+  if (is_running (inferior_ptid))
+    error_is_running ();
+}
+
 /* 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.  */
@@ -1808,11 +1842,15 @@ handle_inferior_event (struct execution_
       && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED && ecs->new_thread_event)
     add_thread (ecs->ptid);
 
-  /* Mark all threads as not-executing.  In non-stop, this should be
-     adjusted to only mark ecs->ptid.  */
   if (ecs->ws.kind != TARGET_WAITKIND_IGNORE
       && stop_soon != STOP_QUIETLY)
-    set_executing (pid_to_ptid (-1), 0);
+    {
+      /* Mark the stopped threads accordingly.  */
+      if (!non_stop)
+	set_executing (pid_to_ptid (-1), 0);
+      else
+	set_executing (ecs->ptid, 0);
+    }
 
   switch (ecs->ws.kind)
     {
@@ -2058,6 +2096,11 @@ handle_inferior_event (struct execution_
      all threads in order to make progress.  */
   if (ecs->new_thread_event)
     {
+      if (non_stop)
+	/* Non-stop requires a behaving target.  */
+	internal_error (__FILE__, __LINE__,
+			"target misreported a new thread in non-stop mode.");
+
       target_resume (RESUME_ALL, 0, TARGET_SIGNAL_0);
       prepare_to_wait (ecs);
       return;
@@ -2124,6 +2167,9 @@ handle_inferior_event (struct execution_
 
   if (!ptid_equal (deferred_step_ptid, null_ptid))
     {
+      /* In non-stop mode, there's never a deferred_step_ptid set.  */
+      gdb_assert (!non_stop);
+
       /* If we stopped for some other reason than single-stepping, ignore
 	 the fact that we were supposed to switch back.  */
       if (stop_signal == TARGET_SIGNAL_TRAP)
@@ -2272,8 +2318,13 @@ handle_inferior_event (struct execution_
 	      if (!ptid_equal (inferior_ptid, ecs->ptid))
 		context_switch (ecs->ptid);
 
-	      waiton_ptid = ecs->ptid;
-	      infwait_state = infwait_thread_hop_state;
+	      if (!non_stop)
+		{
+		  /* Only need to require the next event from this
+		     thread in all-stop mode.  */
+		  waiton_ptid = ecs->ptid;
+		  infwait_state = infwait_thread_hop_state;
+		}
 
 	      tss->stepping_over_breakpoint = 1;
 	      keep_going (ecs);
@@ -3827,7 +3878,16 @@ done:
   /* Delete the breakpoint we stopped at, if it wants to be deleted.
      Delete any breakpoint that is to be deleted at the next stop.  */
   breakpoint_auto_delete (stop_bpstat);
-  set_running (pid_to_ptid (-1), 0);
+
+  if (target_has_execution
+      && last.kind != TARGET_WAITKIND_SIGNALLED
+      && last.kind != TARGET_WAITKIND_EXITED)
+    {
+      if (!non_stop)
+	set_running (pid_to_ptid (-1), 0);
+      else
+	set_running (inferior_ptid, 0);
+    }
 }
 
 static int
Index: src/gdb/thread.c
===================================================================
--- src.orig/gdb/thread.c	2008-05-06 15:08:43.000000000 +0100
+++ src/gdb/thread.c	2008-05-06 15:52:32.000000000 +0100
@@ -561,8 +561,12 @@ print_thread_info (struct ui_out *uiout,
   char *extra_info;
   int current_thread = -1;
 
+  tp = find_thread_pid (inferior_ptid);
+
   /* Backup current thread and selected frame.  */
-  saved_frame_id = get_frame_id (get_selected_frame (NULL));
+  if (tp && !tp->running_)
+    saved_frame_id = get_frame_id (get_selected_frame (NULL));
+
   old_chain = make_cleanup_restore_current_thread (inferior_ptid, saved_frame_id);
 
   make_cleanup_ui_out_list_begin_end (uiout, "threads");
@@ -591,20 +595,26 @@ print_thread_info (struct ui_out *uiout,
       ui_out_text (uiout, " ");
       ui_out_field_string (uiout, "target-id", target_tid_to_str (tp->ptid));
 
-      extra_info = target_extra_thread_info (tp);
-      if (extra_info)
+      if (tp->running_)
+	ui_out_text (uiout, "  (running)\n");
+      else
 	{
-	  ui_out_text (uiout, " (");
-	  ui_out_field_string (uiout, "details", extra_info);
-	  ui_out_text (uiout, ")");
+	  extra_info = target_extra_thread_info (tp);
+	  if (extra_info)
+	    {
+	      ui_out_text (uiout, " (");
+	      ui_out_field_string (uiout, "details", extra_info);
+	      ui_out_text (uiout, ")");
+	    }
+	  ui_out_text (uiout, "  ");
+	  /* The switch below puts us at the top of the stack (leaf
+	     frame).  */
+	  switch_to_thread (tp->ptid);
+	  print_stack_frame (get_selected_frame (NULL),
+			     /* For MI output, print frame level.  */
+			     ui_out_is_mi_like_p (uiout),
+			     LOCATION);
 	}
-      ui_out_text (uiout, "  ");
-      /* That switch put us at the top of the stack (leaf frame).  */
-      switch_to_thread (tp->ptid);
-      print_stack_frame (get_selected_frame (NULL), 
-			 /* For MI output, print frame level.  */
-			 ui_out_is_mi_like_p (uiout),
-			 LOCATION);
 
       do_cleanups (chain2);
     }
@@ -620,6 +630,10 @@ print_thread_info (struct ui_out *uiout,
 	ui_out_field_int (uiout, "current-thread-id", current_thread);
     }
 
+  tp = find_thread_pid (inferior_ptid);
+  if (!tp || tp->running_)
+    return;
+
   /*  If case we were not able to find the original frame, print the
       new selected frame.  */
   if (frame_find_by_id (saved_frame_id) == NULL)
@@ -658,7 +672,11 @@ switch_to_thread (ptid_t ptid)
   inferior_ptid = ptid;
   reinit_frame_cache ();
   registers_changed ();
-  stop_pc = read_pc ();
+
+  if (!is_executing (ptid))
+    stop_pc = read_pc ();
+  else
+    stop_pc = ~(CORE_ADDR) 0;
 }
 
 static void
@@ -695,7 +713,13 @@ do_restore_current_thread_cleanup (void 
 {
   struct current_thread_cleanup *old = arg;
   restore_current_thread (old->inferior_ptid);
-  restore_selected_frame (old->selected_frame_id);
+
+  /* A command like 'thread apply all continue&' will change the
+     running state of the originally selected thread, so we have to
+     recheck it here.  */
+  if (in_thread_list (old->inferior_ptid)
+      && !is_running (old->inferior_ptid))
+    restore_selected_frame (old->selected_frame_id);
   xfree (old);
 }
 
@@ -723,8 +747,7 @@ static void
 thread_apply_all_command (char *cmd, int from_tty)
 {
   struct thread_info *tp;
-  struct cleanup *old_chain;
-  struct cleanup *saved_cmd_cleanup_chain;
+  struct cleanup *old_chain = make_cleanup (null_cleanup, 0);
   char *saved_cmd;
   struct frame_id saved_frame_id;
   ptid_t current_ptid;
@@ -734,8 +757,12 @@ thread_apply_all_command (char *cmd, int
     error (_("Please specify a command following the thread ID list"));
   
   current_ptid = inferior_ptid;
-  saved_frame_id = get_frame_id (get_selected_frame (NULL));
-  old_chain = make_cleanup_restore_current_thread (inferior_ptid, saved_frame_id);
+
+  if (!is_running (inferior_ptid))
+    {
+      saved_frame_id = get_frame_id (get_selected_frame (NULL));
+      make_cleanup_restore_current_thread (inferior_ptid, saved_frame_id);
+    }
 
   /* It is safe to update the thread list now, before
      traversing it for "thread apply all".  MVS */
@@ -744,11 +771,15 @@ thread_apply_all_command (char *cmd, int
   /* Save a copy of the command in case it is clobbered by
      execute_command */
   saved_cmd = xstrdup (cmd);
-  saved_cmd_cleanup_chain = make_cleanup (xfree, (void *) saved_cmd);
+  make_cleanup (xfree, saved_cmd);
   for (tp = thread_list; tp; tp = tp->next)
     if (thread_alive (tp))
       {
-	switch_to_thread (tp->ptid);
+	if (non_stop)
+	  context_switch_to (tp->ptid);
+	else
+	  switch_to_thread (tp->ptid);
+
 	printf_filtered (_("\nThread %d (%s):\n"),
 			 tp->num, target_tid_to_str (inferior_ptid));
 	execute_command (cmd, from_tty);
@@ -758,12 +789,10 @@ thread_apply_all_command (char *cmd, int
   if (!ptid_equal (current_ptid, inferior_ptid))
     thread_has_changed = 1;
 
-  do_cleanups (saved_cmd_cleanup_chain);
   do_cleanups (old_chain);
   /* Print stack frame only if we changed thread.  */
-  if (thread_has_changed)
+  if (thread_has_changed && !is_running (inferior_ptid))
     print_stack_frame (get_current_frame (), 1, SRC_LINE);
-
 }
 
 static void
@@ -831,7 +860,10 @@ thread_apply_command (char *tidlist, int
 	    warning (_("Thread %d has terminated."), start);
 	  else
 	    {
-	      switch_to_thread (tp->ptid);
+	      if (non_stop)
+		context_switch_to (tp->ptid);
+	      else
+		switch_to_thread (tp->ptid);
 	      printf_filtered (_("\nThread %d (%s):\n"), tp->num,
 			       target_tid_to_str (inferior_ptid));
 	      execute_command (cmd, from_tty);
@@ -898,7 +930,10 @@ do_captured_thread_select (struct ui_out
   if (!thread_alive (tp))
     error (_("Thread ID %d has terminated."), num);
 
-  switch_to_thread (tp->ptid);
+  if (non_stop)
+    context_switch_to (tp->ptid);
+  else
+    switch_to_thread (tp->ptid);
 
   ui_out_text (uiout, "[Switching to thread ");
   ui_out_field_int (uiout, "new-thread-id", pid_to_thread_id (inferior_ptid));
@@ -906,7 +941,11 @@ do_captured_thread_select (struct ui_out
   ui_out_text (uiout, target_tid_to_str (inferior_ptid));
   ui_out_text (uiout, ")]");
 
-  print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+  if (!tp->running_)
+    print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
+  else
+    ui_out_text (uiout, " (running)\n");
+
   return GDB_RC_OK;
 }
 
Index: src/gdb/inf-loop.c
===================================================================
--- src.orig/gdb/inf-loop.c	2008-05-06 15:07:58.000000000 +0100
+++ src/gdb/inf-loop.c	2008-05-06 15:10:02.000000000 +0100
@@ -73,11 +73,15 @@ inferior_event_handler (enum inferior_ev
       break;
 
     case INF_EXEC_COMPLETE:
-      /* Unregister the inferior from the event loop. This is done so that
-	 when the inferior is not running we don't get distracted by
-	 spurious inferior output.  */
-      if (target_has_execution)
-	target_async (NULL, 0);
+
+      if (!non_stop)
+	{
+	  /* Unregister the inferior from the event loop. This is done
+	     so that when the inferior is not running we don't get
+	     distracted by spurious inferior output.  */
+	  if (target_has_execution)
+	    target_async (NULL, 0);
+	}
 
       /* The call to async_enable_stdin below resets 'sync_execution'.
 	 However, if sync_execution is 1 now, we also need to show the
Index: src/gdb/infcmd.c
===================================================================
--- src.orig/gdb/infcmd.c	2008-05-06 15:07:24.000000000 +0100
+++ src/gdb/infcmd.c	2008-05-06 15:52:30.000000000 +0100
@@ -612,6 +612,7 @@ continue_command (char *proc_count_exp, 
 {
   int async_exec = 0;
   ERROR_NO_INFERIOR;
+  ensure_not_running ();
 
   /* Find out whether we must run in the background. */
   if (proc_count_exp != NULL)
@@ -713,6 +714,7 @@ step_1 (int skip_subroutines, int single
   int thread = -1;
 
   ERROR_NO_INFERIOR;
+  ensure_not_running ();
 
   if (count_string)
     async_exec = strip_bg_char (&count_string);
@@ -935,6 +937,7 @@ jump_command (char *arg, int from_tty)
   int async_exec = 0;
 
   ERROR_NO_INFERIOR;
+  ensure_not_running ();
 
   /* Find out whether we must run in the background. */
   if (arg != NULL)
@@ -1035,6 +1038,7 @@ signal_command (char *signum_exp, int fr
 
   dont_repeat ();		/* Too dangerous.  */
   ERROR_NO_INFERIOR;
+  ensure_not_running ();
 
   /* Find out whether we must run in the background.  */
   if (signum_exp != NULL)
@@ -2103,7 +2107,26 @@ interrupt_target_command (char *args, in
   if (target_can_async_p ())
     {
       dont_repeat ();		/* Not for the faint of heart */
-      target_stop ();
+
+      if (non_stop)
+	{
+	  ptid_t ptid;
+	  if (args && *args)
+	    {
+	      int num = value_as_long (parse_and_eval (args));
+
+	      if (!valid_thread_id (num))
+		error (_("Thread ID %d not known."), num);
+
+	      ptid = thread_id_to_pid (num);
+	    }
+	  else
+	    ptid = inferior_ptid;
+
+	  target_stop_ptid (ptid);
+	}
+      else
+	target_stop ();
     }
 }
 
Index: src/gdb/inferior.h
===================================================================
--- src.orig/gdb/inferior.h	2008-05-06 15:08:56.000000000 +0100
+++ src/gdb/inferior.h	2008-05-06 15:52:30.000000000 +0100
@@ -245,6 +245,12 @@ extern void get_last_target_status(ptid_
 
 extern void follow_inferior_reset_breakpoints (void);
 
+/* Throw an error indicating the current thread is running.  */
+extern void error_is_running (void);
+
+/* Calls error_is_running if the current thread is running.  */
+extern void ensure_not_running (void);
+
 /* From infcmd.c */
 
 extern void tty_command (char *, int);
Index: src/gdb/target.h
===================================================================
--- src.orig/gdb/target.h	2008-05-06 15:08:23.000000000 +0100
+++ src/gdb/target.h	2008-05-06 15:10:02.000000000 +0100
@@ -397,6 +397,7 @@ struct target_ops
     char *(*to_pid_to_str) (ptid_t);
     char *(*to_extra_thread_info) (struct thread_info *);
     void (*to_stop) (void);
+    void (*to_stop_ptid) (ptid_t ptid);
     void (*to_rcmd) (char *command, struct ui_file *output);
     char *(*to_pid_to_exec_file) (int pid);
     void (*to_log_command) (const char *);
@@ -884,6 +885,9 @@ int target_follow_fork (int follow_child
 
 #define target_stop current_target.to_stop
 
+#define target_stop_ptid current_target.to_stop_ptid
+
+
 /* Send the specified COMMAND to the target's monitor
    (shell,interpreter) for execution.  The result of the query is
    placed in OUTBUF.  */
Index: src/gdb/target.c
===================================================================
--- src.orig/gdb/target.c	2008-05-06 15:07:58.000000000 +0100
+++ src/gdb/target.c	2008-05-06 15:10:02.000000000 +0100
@@ -456,6 +456,7 @@ update_current_target (void)
       INHERIT (to_pid_to_str, t);
       INHERIT (to_extra_thread_info, t);
       INHERIT (to_stop, t);
+      INHERIT (to_stop_ptid, t);
       /* Do not inherit to_xfer_partial.  */
       INHERIT (to_rcmd, t);
       INHERIT (to_pid_to_exec_file, t);
@@ -631,6 +632,9 @@ update_current_target (void)
   de_fault (to_stop,
 	    (void (*) (void))
 	    target_ignore);
+  de_fault (to_stop_ptid,
+	    (void (*) (ptid_t))
+	    target_ignore);
   current_target.to_xfer_partial = current_xfer_partial;
   de_fault (to_rcmd,
 	    (void (*) (char *, struct ui_file *))
@@ -2787,6 +2791,14 @@ debug_to_stop (void)
 }
 
 static void
+debug_to_stop_ptid (ptid_t ptid)
+{
+  debug_target.to_stop_ptid (ptid);
+
+  fprintf_unfiltered (gdb_stdlog, "target_stop_pid\n");
+}
+
+static void
 debug_to_rcmd (char *command,
 	       struct ui_file *outbuf)
 {
@@ -2860,6 +2872,7 @@ setup_target_debug (void)
   current_target.to_thread_alive = debug_to_thread_alive;
   current_target.to_find_new_threads = debug_to_find_new_threads;
   current_target.to_stop = debug_to_stop;
+  current_target.to_stop_ptid = debug_to_stop_ptid;
   current_target.to_rcmd = debug_to_rcmd;
   current_target.to_pid_to_exec_file = debug_to_pid_to_exec_file;
 }

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