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]

Make linux-nat.c's non-stop mode target_stop implementation really asynchronous.


This makes non-stop mode's "(gdb) interrupt", or rather the linux-nat.c's
target_stop implementation truly asynchronous, like remote.c's counterpart,
closer to gdbserver's linux-low.c implementation.

Currently, we 

 - queue a SIGSTOP
 - wait for it to be reported
 - leave a event pending
 - tell the event loop there's something to handle

all from within linux_nat_stop_lwp (from interrupt -> target_stop).

Back to the event loop, we'd noticing a target event,
and go do target_wait, collecting the pending event.

Now we'll just

 - queue a SIGSTOP and leave.  

Eventually, the lwp really stops, and we'll collect
the stop as usual.  We do need to record in the lwp that
we're stopping it, and that's what the new last_resume_kind
field is for.

(I found I needed this after making the core stopping all
lwps itself instead of linux_nat_wait_1 doing it.  That is,
all-stop on top of the target running on non-stop mode.
I was noticing bad things happening with the event loop
losing the pending event.  I thought I might as well make
this work as always intended and get rid of the need to
leave the events pending and do async_file_mark() ...)

Tested on x86_64-linux and applied.

-- 
Pedro Alves

2011-09-09  Pedro Alves  <pedro@codesourcery.com>

	* linux-nat.h (enum resume_kind): New.
	(struct lwp_info) <last_resume_kind>: New field.
	* linux-nat.c (linux_child_follow_fork): Set last_resume_kind to
	resume_stop on the new lwp.
	(add_lwp): Set last_resume_kind as resume_continue by default.
	(lin_lwp_attach_lwp): Set last_resume_kind as resume_stop.
	(resume_lwp): New, factored out from resume_callback.  Also check
	for pending status in lp->waitstatus.
	(resume_callback): Reimplement.
	(resume_clear_callback): Set last_resume_kind as resume_stop.
	(resume_set_callback): Set last_resume_kind as resume_continue.
	(linux_nat_resume, linux_handle_extended_wait): Set
	last_resume_kind.
	(running_callback): Also check lp->waitstatus for pending events.
	(select_singlestep_lwp_callback): Check that lp->last_resume_kind
	is resume_step.
	(stop_and_resume_callback): Don't re-resume if the core wanted the
	lwp stopped.  Use resume_lwp instead of resume_callback.  Avoid
	using an invalidated pointer.
	(linux_nat_filter_event): Don't discard SIGSTOPs as delayed
	SIGSTOPs if the core wanted the LWP to stop.
	(linux_nat_wait_1) Don't consume a pending SIGSTOP if the core
	wanted the lwp to stop.  If the core wanted the lwp to stop, and
	the lwp stopped with a SIGSTOP, report a TARGET_SIGNAL_0 instead
	of TARGET_SIGNAL_STOP.
	(linux_nat_stop_lwp): Don't synchronously wait for the lwp to stop
	here.  Instead, signal the lwp, and set the last_resume_kind to
	resume_stop.

---
 gdb/linux-nat.c |  189 ++++++++++++++++++++++++++++++++++----------------------
 gdb/linux-nat.h |   17 +++++
 2 files changed, 133 insertions(+), 73 deletions(-)

Index: src/gdb/linux-nat.h
===================================================================
--- src.orig/gdb/linux-nat.h	2011-09-09 14:45:52.329762094 +0100
+++ src/gdb/linux-nat.h	2011-09-09 15:41:34.399762678 +0100
@@ -22,6 +22,20 @@
 
 #include <signal.h>
 
+/* Ways to "resume" a thread.  */
+
+enum resume_kind
+{
+  /* Thread should continue.  */
+  resume_continue,
+
+  /* Thread should single-step.  */
+  resume_step,
+
+  /* Thread should be stopped.  */
+  resume_stop
+};
+
 /* Structure describing an LWP.  This is public only for the purposes
    of ALL_LWPS; target-specific code should generally not access it
    directly.  */
@@ -52,6 +66,9 @@ struct lwp_info
      didn't try to let the LWP run.  */
   int resumed;
 
+  /* The last resume GDB requested on this thread.  */
+  enum resume_kind last_resume_kind;
+
   /* If non-zero, a pending wait status.  */
   int status;
 
Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c	2011-09-09 14:46:25.649762100 +0100
+++ src/gdb/linux-nat.c	2011-09-09 20:07:34.839765470 +0100
@@ -701,6 +701,7 @@ holding the child stopped.  Try \"set de
 	  add_thread (inferior_ptid);
 	  child_lp = add_lwp (inferior_ptid);
 	  child_lp->stopped = 1;
+	  child_lp->last_resume_kind = resume_stop;
 
 	  /* If this is a vfork child, then the address-space is
 	     shared with the parent.  */
@@ -891,6 +892,7 @@ holding the child stopped.  Try \"set de
       add_thread (inferior_ptid);
       child_lp = add_lwp (inferior_ptid);
       child_lp->stopped = 1;
+      child_lp->last_resume_kind = resume_stop;
 
       /* If this is a vfork child, then the address-space is shared
 	 with the parent.  If we detached from the parent, then we can
@@ -1158,6 +1160,7 @@ add_lwp (ptid_t ptid)
 
   memset (lp, 0, sizeof (struct lwp_info));
 
+  lp->last_resume_kind = resume_continue;
   lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
 
   lp->ptid = ptid;
@@ -1515,6 +1518,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
       lp->stopped = 1;
     }
 
+  lp->last_resume_kind = resume_stop;
   restore_child_signals_mask (&prev_mask);
   return 0;
 }
@@ -1829,46 +1833,61 @@ linux_nat_detach (struct target_ops *ops
 
 /* Resume LP.  */
 
-static int
-resume_callback (struct lwp_info *lp, void *data)
+static void
+resume_lwp (struct lwp_info *lp, int step)
 {
-  struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
-
-  if (lp->stopped && inf->vfork_child != NULL)
+  if (lp->stopped)
     {
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "RC: Not resuming %s (vfork parent)\n",
-			    target_pid_to_str (lp->ptid));
+      struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
+
+      if (inf->vfork_child != NULL)
+	{
+	  if (debug_linux_nat)
+	    fprintf_unfiltered (gdb_stdlog,
+				"RC: Not resuming %s (vfork parent)\n",
+				target_pid_to_str (lp->ptid));
+	}
+      else if (lp->status == 0
+	       && lp->waitstatus.kind == TARGET_WAITKIND_IGNORE)
+	{
+	  if (debug_linux_nat)
+	    fprintf_unfiltered (gdb_stdlog,
+				"RC:  PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
+				target_pid_to_str (lp->ptid));
+
+	  linux_ops->to_resume (linux_ops,
+				pid_to_ptid (GET_LWP (lp->ptid)),
+				step, TARGET_SIGNAL_0);
+	  if (debug_linux_nat)
+	    fprintf_unfiltered (gdb_stdlog,
+				"RC:  PTRACE_CONT %s, 0, 0 (resume sibling)\n",
+				target_pid_to_str (lp->ptid));
+	  lp->stopped = 0;
+	  lp->step = step;
+	  memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+	  lp->stopped_by_watchpoint = 0;
+	}
+      else
+	{
+	  if (debug_linux_nat)
+	    fprintf_unfiltered (gdb_stdlog,
+				"RC: Not resuming sibling %s (has pending)\n",
+				target_pid_to_str (lp->ptid));
+	}
     }
-  else if (lp->stopped && lp->status == 0)
+  else
     {
       if (debug_linux_nat)
 	fprintf_unfiltered (gdb_stdlog,
-			    "RC:  PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
-			    target_pid_to_str (lp->ptid));
-
-      linux_ops->to_resume (linux_ops,
-			    pid_to_ptid (GET_LWP (lp->ptid)),
-			    0, TARGET_SIGNAL_0);
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "RC:  PTRACE_CONT %s, 0, 0 (resume sibling)\n",
+			    "RC: Not resuming sibling %s (not stopped)\n",
 			    target_pid_to_str (lp->ptid));
-      lp->stopped = 0;
-      lp->step = 0;
-      memset (&lp->siginfo, 0, sizeof (lp->siginfo));
-      lp->stopped_by_watchpoint = 0;
     }
-  else if (lp->stopped && debug_linux_nat)
-    fprintf_unfiltered (gdb_stdlog,
-			"RC: Not resuming sibling %s (has pending)\n",
-			target_pid_to_str (lp->ptid));
-  else if (debug_linux_nat)
-    fprintf_unfiltered (gdb_stdlog,
-			"RC: Not resuming sibling %s (not stopped)\n",
-			target_pid_to_str (lp->ptid));
+}
 
+static int
+resume_callback (struct lwp_info *lp, void *data)
+{
+  resume_lwp (lp, 0);
   return 0;
 }
 
@@ -1876,6 +1895,7 @@ static int
 resume_clear_callback (struct lwp_info *lp, void *data)
 {
   lp->resumed = 0;
+  lp->last_resume_kind = resume_stop;
   return 0;
 }
 
@@ -1883,6 +1903,7 @@ static int
 resume_set_callback (struct lwp_info *lp, void *data)
 {
   lp->resumed = 1;
+  lp->last_resume_kind = resume_continue;
   return 0;
 }
 
@@ -1922,6 +1943,7 @@ linux_nat_resume (struct target_ops *ops
 
   /* Remember if we're stepping.  */
   lp->step = step;
+  lp->last_resume_kind = step ? resume_step : resume_continue;
 
   /* If we have a pending wait status for this thread, there is no
      point in resuming the process.  But first make sure that
@@ -2286,6 +2308,7 @@ linux_handle_extended_wait (struct lwp_i
 
 	      new_lp->stopped = 0;
 	      new_lp->resumed = 1;
+	      new_lp->last_resume_kind = resume_continue;
 
 	      signo = (status
 		       ? target_signal_from_host (WSTOPSIG (status))
@@ -2918,7 +2941,10 @@ status_callback (struct lwp_info *lp, vo
 static int
 running_callback (struct lwp_info *lp, void *data)
 {
-  return (lp->stopped == 0 || (lp->status != 0 && lp->resumed));
+  return (!lp->stopped
+	  || ((lp->status != 0
+	       || lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+	      && lp->resumed));
 }
 
 /* Count the LWP's that have had events.  */
@@ -2942,7 +2968,8 @@ count_events_callback (struct lwp_info *
 static int
 select_singlestep_lwp_callback (struct lwp_info *lp, void *data)
 {
-  if (lp->step && lp->status != 0)
+  if (lp->last_resume_kind == resume_step
+      && lp->status != 0)
     return 1;
   else
     return 0;
@@ -3094,19 +3121,22 @@ resumed_callback (struct lwp_info *lp, v
 static int
 stop_and_resume_callback (struct lwp_info *lp, void *data)
 {
-  struct lwp_info *ptr;
-
-  if (!lp->stopped && !lp->signalled)
+  if (!lp->stopped)
     {
+      enum resume_kind last_resume_kind = lp->last_resume_kind;
+      ptid_t ptid = lp->ptid;
+
       stop_callback (lp, NULL);
       stop_wait_callback (lp, NULL);
-      /* Resume if the lwp still exists.  */
-      for (ptr = lwp_list; ptr; ptr = ptr->next)
-	if (lp == ptr)
-	  {
-	    resume_callback (lp, NULL);
-	    resume_set_callback (lp, NULL);
-	  }
+
+      /* Resume if the lwp still exists, and the core wanted it
+	 running.  */
+      if (last_resume_kind != resume_stop)
+	{
+	  lp = find_lwp_pid (ptid);
+	  if (lp)
+	    resume_lwp (lp, lp->step);
+	}
     }
   return 0;
 }
@@ -3267,25 +3297,29 @@ linux_nat_filter_event (int lwpid, int s
 			    "LLW: Delayed SIGSTOP caught for %s.\n",
 			    target_pid_to_str (lp->ptid));
 
-      /* This is a delayed SIGSTOP.  */
       lp->signalled = 0;
 
-      registers_changed ();
+      if (lp->last_resume_kind != resume_stop)
+	{
+	  /* This is a delayed SIGSTOP.  */
 
-      linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
+	  registers_changed ();
+
+	  linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
 			    lp->step, TARGET_SIGNAL_0);
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
-			    lp->step ?
-			    "PTRACE_SINGLESTEP" : "PTRACE_CONT",
-			    target_pid_to_str (lp->ptid));
+	  if (debug_linux_nat)
+	    fprintf_unfiltered (gdb_stdlog,
+				"LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
+				lp->step ?
+				"PTRACE_SINGLESTEP" : "PTRACE_CONT",
+				target_pid_to_str (lp->ptid));
 
-      lp->stopped = 0;
-      gdb_assert (lp->resumed);
+	  lp->stopped = 0;
+	  gdb_assert (lp->resumed);
 
-      /* Discard the event.  */
-      return NULL;
+	  /* Discard the event.  */
+	  return NULL;
+	}
     }
 
   /* Make sure we don't report a SIGINT that we have already displayed
@@ -3435,7 +3469,7 @@ retry:
 	lp = NULL;
     }
 
-  if (lp && lp->signalled)
+  if (lp && lp->signalled && lp->last_resume_kind != resume_stop)
     {
       /* A pending SIGSTOP may interfere with the normal stream of
          events.  In a typical case where interference is a problem,
@@ -3747,7 +3781,10 @@ retry:
       iterate_over_lwps (minus_one_ptid, resume_clear_callback, NULL);
     }
   else
-    lp->resumed = 0;
+    {
+      lp->resumed = 0;
+      lp->last_resume_kind = resume_stop;
+    }
 
   if (linux_nat_status_is_event (status))
     {
@@ -3770,6 +3807,16 @@ retry:
 
   restore_child_signals_mask (&prev_mask);
 
+  if (lp->last_resume_kind == resume_stop
+      && ourstatus->kind == TARGET_WAITKIND_STOPPED
+      && WSTOPSIG (status) == SIGSTOP)
+    {
+      /* A thread that has been requested to stop by GDB with
+	 target_stop, and it stopped cleanly, so report as SIG0.  The
+	 use of SIGSTOP is an implementation detail.  */
+      ourstatus->value.sig = TARGET_SIGNAL_0;
+    }
+
   if (ourstatus->kind == TARGET_WAITKIND_EXITED
       || ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
     lp->core = -1;
@@ -5426,22 +5473,18 @@ linux_nat_stop_lwp (struct lwp_info *lwp
 			    target_pid_to_str (lwp->ptid));
 
 
-      stop_callback (lwp, NULL);
-      stop_wait_callback (lwp, NULL);
-
-      /* If the lwp exits while we try to stop it, there's nothing
-	 else to do.  */
-      lwp = find_lwp_pid (ptid);
-      if (lwp == NULL)
-	return 0;
+      if (lwp->last_resume_kind == resume_stop)
+	{
+	  if (debug_linux_nat)
+	    fprintf_unfiltered (gdb_stdlog,
+				"linux-nat: already stopping LWP %ld at "
+				"GDB's request\n",
+				ptid_get_lwp (lwp->ptid));
+	  return 0;
+	}
 
-      /* If we didn't collect any signal other than SIGSTOP while
-	 stopping the LWP, push a SIGNAL_0 event.  In either case, the
-	 event-loop will end up calling target_wait which will collect
-	 these.  */
-      if (lwp->status == 0)
-	lwp->status = W_STOPCODE (0);
-      async_file_mark ();
+      stop_callback (lwp, NULL);
+      lwp->last_resume_kind = resume_stop;
     }
   else
     {


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