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]

Re: Teach gdbserver to step over internal breakpoints


On Wednesday 24 March 2010 21:22:39, Pedro Alves wrote:
> On Wednesday 24 March 2010 12:13:31, Pedro Alves wrote:
> > On Wednesday 24 March 2010 00:05:38, Pedro Alves wrote:
> > > I've now tested this on arm-none-linux-gnueabi,
> > > and there were no regressions.  
> > 
> > I busted this.  I forgot to force a thread event
> > breakpoint to exercise the code paths on ARM.  I redid the testing
> > overnight, and it revealed a bunch of regressions; it turns out
> > I trimmed the original patch too much --- there are some needed
> > missing bits I left behind.  I'm working on fixing this.
> 
> Here's one of the bits.

And here's the other bit.  The issue is that if we get
rid of leaving breakpoints pending, we need to handle
starvation some other way, otherwise, schedlock.exp complains.
I took a deep breath, thought about this for a long while, and
finaly decided that doing what native GDB does gives less
headaches for both future changes, and is better in terms
of consolidating native gdb and gdbserver codebases.

This also moves the internal breakpoint handling one layer
up, from linux_wait_for_event_1 to linux_nat_1, which is
better since it's one step closer to being moved to common
gdbserver event loop code.  Still not there, because that
would require also moving the "stop-all-threads" bits
for all-stop to common code as well, and then that requires
thinking what to do with targets where the debug api
implicitly stops all threads for us, like the Win32 debug API.

Tested on arm-linux-gnueabi and x86_64-linux, both with forcing
a thread event breakpoint (simulating LinuxThreads), and without
hacking anything.  I've applied it.

I don't see any regressions cause by yesterday's patch
left.

-- 
Pedro Alves

2010-03-24  Pedro Alves  <pedro@codesourcery.com>

	gdb/gdbserver/
	* linux-low.c (status_pending_p_callback): Fix comment.
	(linux_wait_for_event_1): Move most of the internal breakpoint
	handling from here...
	(linux_wait_1): ... to here.
	(count_events_callback): New.
	(select_singlestep_lwp_callback): New.
	(select_event_lwp_callback): New.
	(cancel_breakpoints_callback): New.
	(select_event_lwp): New.
	(linux_wait_1): Simplify internal breakpoint handling.  Give equal
	priority to all LWPs that have had events that should be reported
	to the client.  Cancel breakpoints when about to reporting the
	event to the client, not while stopping lwps.  No longer cancel
	finished single-steps here.
	(cancel_finished_single_step): Delete.
	(cancel_finished_single_steps): Delete.

---
 gdb/gdbserver/linux-low.c |  606 +++++++++++++++++++++++-----------------------
 1 file changed, 314 insertions(+), 292 deletions(-)

Index: src/gdb/gdbserver/linux-low.c
===================================================================
--- src.orig/gdb/gdbserver/linux-low.c	2010-03-24 20:58:30.000000000 +0000
+++ src/gdb/gdbserver/linux-low.c	2010-03-24 20:59:35.000000000 +0000
@@ -146,7 +146,6 @@ static void mark_lwp_dead (struct lwp_in
 static int linux_core_of_thread (ptid_t ptid);
 static void proceed_all_lwps (void);
 static void unstop_all_lwps (struct lwp_info *except);
-static void cancel_finished_single_steps (struct lwp_info *except);
 static int finish_step_over (struct lwp_info *lwp);
 static CORE_ADDR get_stop_pc (struct lwp_info *lwp);
 static int kill_lwp (unsigned long lwpid, int signo);
@@ -934,8 +933,7 @@ linux_thread_alive (ptid_t ptid)
     return 0;
 }
 
-/* Return 1 if this lwp has an interesting status pending.  This
-   function may silently resume an inferior lwp.  */
+/* Return 1 if this lwp has an interesting status pending.  */
 static int
 status_pending_p_callback (struct inferior_list_entry *entry, void *arg)
 {
@@ -1189,10 +1187,8 @@ ptid_t step_over_bkpt;
 static int
 linux_wait_for_event_1 (ptid_t ptid, int *wstat, int options)
 {
-  CORE_ADDR stop_pc;
   struct lwp_info *event_child, *requested_child;
 
- again:
   event_child = NULL;
   requested_child = NULL;
 
@@ -1232,20 +1228,7 @@ linux_wait_for_event_1 (ptid_t ptid, int
      events.  */
   while (1)
     {
-      int step_over_finished = 0;
-      int bp_explains_trap = 0;
-      int cancel_sigtrap;
-
-      if (ptid_equal (step_over_bkpt, null_ptid))
-	event_child = linux_wait_for_lwp (ptid, wstat, options);
-      else
-	{
-	  if (debug_threads)
-	    fprintf (stderr, "step_over_bkpt set [%s], doing a blocking wait\n",
-		     target_pid_to_str (step_over_bkpt));
-	  event_child = linux_wait_for_lwp (step_over_bkpt,
-					    wstat, options & ~WNOHANG);
-	}
+      event_child = linux_wait_for_lwp (ptid, wstat, options);
 
       if ((options & WNOHANG) && event_child == NULL)
 	{
@@ -1380,218 +1363,6 @@ linux_wait_for_event_1 (ptid_t ptid, int
 	    }
 	}
 
-      cancel_sigtrap = (stopping_threads
-			|| event_child->last_resume_kind == resume_stop);
-
-      /* Do not allow nested internal breakpoint handling, or leave
-	 the unadjusted PCs visible to GDB.  Simply cancel the
-	 breakpoint now, and eventually when the thread is resumed, it
-	 will trap again, if the breakpoint is still there by
-	 then.  */
-      if (WIFSTOPPED (*wstat)
-	  && WSTOPSIG (*wstat) == SIGTRAP
-	  && cancel_sigtrap)
-	{
-	  if (debug_threads)
-	    fprintf (stderr, "Got a nested SIGTRAP while stopping threads\n");
-
-	  if (cancel_breakpoint (event_child))
-	    {
-	      if (debug_threads)
-		fprintf (stderr, " bkpt canceled\n");
-
-	      /* We don't resume immediately to collect the SIGSTOP,
-		 due to other reasons we may care for this SIGTRAP
-		 below.  Care must be taken to not process anything
-		 breakpoint related though from this point on.  */
-	    }
-	}
-
-      /* If this event was not handled above, and is not a SIGTRAP, we
-	 report it.  SIGILL and SIGSEGV are also treated as traps in
-	 case a breakpoint is inserted at the current PC.  If this
-	 target does not support breakpoints, we also report the
-	 SIGTRAP without further processing; it's of no concern to
-	 us.  */
-      if (!WIFSTOPPED (*wstat)
-	  || !supports_breakpoints ()
-	  || (WSTOPSIG (*wstat) != SIGTRAP
-	      && WSTOPSIG (*wstat) != SIGILL
-	      && WSTOPSIG (*wstat) != SIGSEGV)
-	  /* Only handle SIGILL or SIGSEGV if we've hit a recognized
-	     breakpoint.  */
-	  || (WSTOPSIG (*wstat) != SIGTRAP
-	      && !(*the_low_target.breakpoint_at) (event_child->stop_pc)))
-	{
-	  if (debug_threads && WIFSTOPPED (*wstat))
-	    fprintf (stderr, "Reporting signal %d for LWP %ld.\n",
-		     WSTOPSIG (*wstat), lwpid_of (event_child));
-
-	  /* If we were stepping over a breakpoint, this signal
-	     arriving means we didn't manage to move past the
-	     breakpoint location.  Cancel the operation for now --- it
-	     will be handled again on the next resume, if required
-	     (the breakpoint may be removed meanwhile, for
-	     example).  */
-	  if (finish_step_over (event_child))
-	    {
-	      event_child->need_step_over = 1;
-	      unstop_all_lwps (event_child);
-	    }
-	  return lwpid_of (event_child);
-	}
-
-      stop_pc = event_child->stop_pc;
-
-      /* Handle anything that requires bookkeeping before deciding to
-	 report the event or continue waiting.  */
-
-      /* First check if we can explain the SIGTRAP with an internal
-	 breakpoint, or if we should possibly report the event to GDB.
-	 Do this before anything that may remove or insert a
-	 breakpoint.  */
-      bp_explains_trap = breakpoint_inserted_here (stop_pc);
-
-      /* We have a SIGTRAP, possibly a step-over dance has just
-	 finished.  If so, tweak the state machine accordingly,
-	 reinsert breakpoints and delete any reinsert (software
-	 single-step) breakpoints.  */
-      step_over_finished = finish_step_over (event_child);
-
-      /* Now invoke the callbacks of any breakpoints there.  */
-      if (!cancel_sigtrap)
-	check_breakpoints (stop_pc);
-
-      /* If we're stopping threads, resume once more to collect the
-	 SIGSTOP, and do nothing else.  Note that we don't set
-	 need_step_over.  If this predicate matches, then we've
-	 cancelled the SIGTRAP before reaching here, and we do want
-	 any breakpoint at STOP_PC to be re-hit on resume.  */
-      if (stopping_threads
-	  || event_child->last_resume_kind == resume_stop)
-	{
-	  gdb_assert (cancel_sigtrap);
-
-	  if (step_over_finished)
-	    unstop_all_lwps (event_child);
-
-	  if (debug_threads)
-	    {
-	      if (event_child->last_resume_kind == resume_stop)
-		fprintf (stderr, "Bailing out; GDB wanted the LWP to stop.\n");
-	      else if (stopping_threads)
-		fprintf (stderr, "Bailing out; stopping threads.\n");
-
-	      if (bp_explains_trap)
-		fprintf (stderr, "   Hit a breakpoint.\n");
-	      if (step_over_finished)
-		fprintf (stderr, "   Step-over finished.\n");
-	      if (event_child->stopped_by_watchpoint)
-		fprintf (stderr, "   Stopped by watchpoint.\n");
-	    }
-
-	  /* Leave these pending.  */
-	  if (event_child->stopped_by_watchpoint)
-	    return lwpid_of (event_child);
-
-	  /* Otherwise, there may or not be a pending SIGSTOP.  If
-	     there isn't one, queue one up.  In any case, go back to
-	     the event loop to collect it.  Don't return yet, as we
-	     don't want this SIGTRAP to be left pending.  Note that
-	     since we cancelled the breakpoint above, the PC is
-	     already adjusted, and hence get_stop_pc returns the
-	     correct PC when we collect the SIGSTOP.  */
-
-	  if (!event_child->stop_expected)
-	    {
-	      event_child->stop_expected = 1;
-	      kill_lwp (lwpid_of (event_child), SIGSTOP);
-	    }
-
-	  /* Clear the single-stepping flag and SIGTRAP as we resume.  */
-	  linux_resume_one_lwp (event_child, 0, 0, NULL);
-	  continue;
-	}
-
-      /* We have all the data we need.  Either report the event to
-	 GDB, or resume threads and keep waiting for more.  */
-
-      /* Check If GDB would be interested in this event.  If GDB
-	 wanted this thread to single step, we always want to report
-	 the SIGTRAP, and let GDB handle it.  */
-      if ((event_child->last_resume_kind == resume_step)
-	  || event_child->stopped_by_watchpoint)
-	{
-	  if (step_over_finished)
-	    unstop_all_lwps (event_child);
-
-	  if (debug_threads)
-	    {
-	      if (event_child->last_resume_kind == resume_step)
-		fprintf (stderr, "GDB wanted to single-step, reporting event.\n");
-	      if (event_child->stopped_by_watchpoint)
-		fprintf (stderr, "Stopped by watchpoint.\n");
-	    }
-	}
-      /* We found no reason GDB would want us to stop.  We either hit
-	 one of our own breakpoints, or finished an internal step GDB
-	 shouldn't know about.  */
-      else if (step_over_finished || bp_explains_trap)
-	{
-	  if (debug_threads)
-	    {
-	      if (bp_explains_trap)
-		fprintf (stderr, "Hit a gdbserver breakpoint.\n");
-	      if (step_over_finished)
-		fprintf (stderr, "Step-over finished.\n");
-	    }
-
-	  /* If we stepped or ran into an internal breakpoint, we've
-	     already handled it.  So next time we resume (from this
-	     PC), we should step over it.  */
-	  if (breakpoint_here (stop_pc))
-	    event_child->need_step_over = 1;
-
-	  /* We're not reporting this breakpoint to GDB, so apply the
-	     decr_pc_after_break adjustment to the inferior's regcache
-	     ourselves.  */
-
-	  if (the_low_target.set_pc != NULL)
-	    {
-	      struct regcache *regcache
-		= get_thread_regcache (get_lwp_thread (event_child), 1);
-	      (*the_low_target.set_pc) (regcache, stop_pc);
-	    }
-
-	  /* We've finished stepping over a breakpoint.  We've stopped
-	     all LWPs momentarily except the stepping one.  This is
-	     where we resume them all again.  We're going to keep
-	     waiting, so use proceed, which handles stepping over the
-	     next breakpoint.  */
-	  if (debug_threads)
-	    fprintf (stderr, "proceeding all threads.\n");
-	  proceed_all_lwps ();
-
-	  /* If we stopped threads momentarily, we may have threads
-	     with pending statuses now (except when we're going to
-	     force the next event out of a specific LWP, in which case
-	     don't want to handle the pending events of other LWPs
-	     yet.  */
-	  if (ptid_equal (step_over_bkpt, null_ptid))
-	    goto again;
-	  else
-	    continue;
-	}
-      else
-	{
-	  /* GDB breakpoint or program trap, perhaps.  */
-	  if (step_over_finished)
-	    unstop_all_lwps (event_child);
-	}
-
-      if (debug_threads)
-	fprintf (stderr, "Hit a non-gdbserver trap event.\n");
-
       return lwpid_of (event_child);
     }
 
@@ -1639,6 +1410,151 @@ linux_wait_for_event (ptid_t ptid, int *
     }
 }
 
+
+/* Count the LWP's that have had events.  */
+
+static int
+count_events_callback (struct inferior_list_entry *entry, void *data)
+{
+  struct lwp_info *lp = (struct lwp_info *) entry;
+  int *count = data;
+
+  gdb_assert (count != NULL);
+
+  /* Count only resumed LWPs that have a SIGTRAP event pending that
+     should be reported to GDB.  */
+  if (get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
+      && lp->last_resume_kind != resume_stop
+      && lp->status_pending_p
+      && WIFSTOPPED (lp->status_pending)
+      && WSTOPSIG (lp->status_pending) == SIGTRAP
+      && !breakpoint_inserted_here (lp->stop_pc))
+    (*count)++;
+
+  return 0;
+}
+
+/* Select the LWP (if any) that is currently being single-stepped.  */
+
+static int
+select_singlestep_lwp_callback (struct inferior_list_entry *entry, void *data)
+{
+  struct lwp_info *lp = (struct lwp_info *) entry;
+
+  if (get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
+      && lp->last_resume_kind == resume_step
+      && lp->status_pending_p)
+    return 1;
+  else
+    return 0;
+}
+
+/* Select the Nth LWP that has had a SIGTRAP event that should be
+   reported to GDB.  */
+
+static int
+select_event_lwp_callback (struct inferior_list_entry *entry, void *data)
+{
+  struct lwp_info *lp = (struct lwp_info *) entry;
+  int *selector = data;
+
+  gdb_assert (selector != NULL);
+
+  /* Select only resumed LWPs that have a SIGTRAP event pending. */
+  if (lp->last_resume_kind != resume_stop
+      && get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
+      && lp->status_pending_p
+      && WIFSTOPPED (lp->status_pending)
+      && WSTOPSIG (lp->status_pending) == SIGTRAP
+      && !breakpoint_inserted_here (lp->stop_pc))
+    if ((*selector)-- == 0)
+      return 1;
+
+  return 0;
+}
+
+static int
+cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data)
+{
+  struct lwp_info *lp = (struct lwp_info *) entry;
+  struct lwp_info *event_lp = data;
+
+  /* Leave the LWP that has been elected to receive a SIGTRAP alone.  */
+  if (lp == event_lp)
+    return 0;
+
+  /* If a LWP other than the LWP that we're reporting an event for has
+     hit a GDB breakpoint (as opposed to some random trap signal),
+     then just arrange for it to hit it again later.  We don't keep
+     the SIGTRAP status and don't forward the SIGTRAP signal to the
+     LWP.  We will handle the current event, eventually we will resume
+     all LWPs, and this one will get its breakpoint trap again.
+
+     If we do not do this, then we run the risk that the user will
+     delete or disable the breakpoint, but the LWP will have already
+     tripped on it.  */
+
+  if (lp->last_resume_kind != resume_stop
+      && get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
+      && lp->status_pending_p
+      && WIFSTOPPED (lp->status_pending)
+      && WSTOPSIG (lp->status_pending) == SIGTRAP
+      && cancel_breakpoint (lp))
+    /* Throw away the SIGTRAP.  */
+    lp->status_pending_p = 0;
+
+  return 0;
+}
+
+/* Select one LWP out of those that have events pending.  */
+
+static void
+select_event_lwp (struct lwp_info **orig_lp)
+{
+  int num_events = 0;
+  int random_selector;
+  struct lwp_info *event_lp;
+
+  /* Give preference to any LWP that is being single-stepped.  */
+  event_lp
+    = (struct lwp_info *) find_inferior (&all_lwps,
+					 select_singlestep_lwp_callback, NULL);
+  if (event_lp != NULL)
+    {
+      if (debug_threads)
+	fprintf (stderr,
+		 "SEL: Select single-step %s\n",
+		 target_pid_to_str (ptid_of (event_lp)));
+    }
+  else
+    {
+      /* No single-stepping LWP.  Select one at random, out of those
+         which have had SIGTRAP events.  */
+
+      /* First see how many SIGTRAP events we have.  */
+      find_inferior (&all_lwps, count_events_callback, &num_events);
+
+      /* Now randomly pick a LWP out of those that have had a SIGTRAP.  */
+      random_selector = (int)
+	((num_events * (double) rand ()) / (RAND_MAX + 1.0));
+
+      if (debug_threads && num_events > 1)
+	fprintf (stderr,
+		 "SEL: Found %d SIGTRAP events, selecting #%d\n",
+		 num_events, random_selector);
+
+      event_lp = (struct lwp_info *) find_inferior (&all_lwps,
+						    select_event_lwp_callback,
+						    &random_selector);
+    }
+
+  if (event_lp != NULL)
+    {
+      /* Switch the event LWP.  */
+      *orig_lp = event_lp;
+    }
+}
+
 /* Set this inferior LWP's state as "want-stopped".  We won't resume
    this LWP until the client gives us another action for it.  */
 
@@ -1673,9 +1589,13 @@ linux_wait_1 (ptid_t ptid,
 {
   int w;
   struct thread_info *thread = NULL;
-  struct lwp_info *lwp = NULL;
+  struct lwp_info *event_child = NULL;
   int options;
   int pid;
+  int step_over_finished;
+  int bp_explains_trap;
+  int maybe_internal_trap;
+  int report_to_gdb;
 
   /* Translate generic target options into linux options.  */
   options = __WALL;
@@ -1711,11 +1631,20 @@ retry:
 	ptid = cont_thread;
     }
 
-  pid = linux_wait_for_event (ptid, &w, options);
+  if (ptid_equal (step_over_bkpt, null_ptid))
+    pid = linux_wait_for_event (ptid, &w, options);
+  else
+    {
+      if (debug_threads)
+	fprintf (stderr, "step_over_bkpt set [%s], doing a blocking wait\n",
+		 target_pid_to_str (step_over_bkpt));
+      pid = linux_wait_for_event (step_over_bkpt, &w, options & ~WNOHANG);
+    }
+
   if (pid == 0) /* only if TARGET_WNOHANG */
     return null_ptid;
 
-  lwp = get_thread_lwp (current_inferior);
+  event_child = get_thread_lwp (current_inferior);
 
   /* If we are waiting for a particular child, and it exited,
      linux_wait_for_event will return its exit status.  Similarly if
@@ -1733,13 +1662,13 @@ retry:
     {
       if (WIFEXITED (w) || WIFSIGNALED (w))
 	{
-	  int pid = pid_of (lwp);
+	  int pid = pid_of (event_child);
 	  struct process_info *process = find_process_pid (pid);
 
 #ifdef USE_THREAD_DB
 	  thread_db_free (process, 0);
 #endif
-	  delete_lwp (lwp);
+	  delete_lwp (event_child);
 	  linux_remove_process (process);
 
 	  current_inferior = NULL;
@@ -1771,18 +1700,161 @@ retry:
 	goto retry;
     }
 
+  /* If this event was not handled before, and is not a SIGTRAP, we
+     report it.  SIGILL and SIGSEGV are also treated as traps in case
+     a breakpoint is inserted at the current PC.  If this target does
+     not support internal breakpoints at all, we also report the
+     SIGTRAP without further processing; it's of no concern to us.  */
+  maybe_internal_trap
+    = (supports_breakpoints ()
+       && (WSTOPSIG (w) == SIGTRAP
+	   || ((WSTOPSIG (w) == SIGILL
+		|| WSTOPSIG (w) == SIGSEGV)
+	       && (*the_low_target.breakpoint_at) (event_child->stop_pc))));
+
+  if (maybe_internal_trap)
+    {
+      /* Handle anything that requires bookkeeping before deciding to
+	 report the event or continue waiting.  */
+
+      /* First check if we can explain the SIGTRAP with an internal
+	 breakpoint, or if we should possibly report the event to GDB.
+	 Do this before anything that may remove or insert a
+	 breakpoint.  */
+      bp_explains_trap = breakpoint_inserted_here (event_child->stop_pc);
+
+      /* We have a SIGTRAP, possibly a step-over dance has just
+	 finished.  If so, tweak the state machine accordingly,
+	 reinsert breakpoints and delete any reinsert (software
+	 single-step) breakpoints.  */
+      step_over_finished = finish_step_over (event_child);
+
+      /* Now invoke the callbacks of any internal breakpoints there.  */
+      check_breakpoints (event_child->stop_pc);
+
+      if (bp_explains_trap)
+	{
+	  /* If we stepped or ran into an internal breakpoint, we've
+	     already handled it.  So next time we resume (from this
+	     PC), we should step over it.  */
+	  if (debug_threads)
+	    fprintf (stderr, "Hit a gdbserver breakpoint.\n");
+
+	  event_child->need_step_over = 1;
+	}
+    }
+  else
+    {
+      /* We have some other signal, possibly a step-over dance was in
+	 progress, and it should be cancelled too.  */
+      step_over_finished = finish_step_over (event_child);
+    }
+
+  /* We have all the data we need.  Either report the event to GDB, or
+     resume threads and keep waiting for more.  */
+
+  /* Check If GDB would be interested in this event.  If GDB wanted
+     this thread to single step, we always want to report the SIGTRAP,
+     and let GDB handle it.  */
+  report_to_gdb = (!maybe_internal_trap
+		   || event_child->last_resume_kind == resume_step
+		   || event_child->stopped_by_watchpoint
+		   || (!step_over_finished && !bp_explains_trap));
+
+  /* We found no reason GDB would want us to stop.  We either hit one
+     of our own breakpoints, or finished an internal step GDB
+     shouldn't know about.  */
+  if (!report_to_gdb)
+    {
+      if (debug_threads)
+	{
+	  if (bp_explains_trap)
+	    fprintf (stderr, "Hit a gdbserver breakpoint.\n");
+	  if (step_over_finished)
+	    fprintf (stderr, "Step-over finished.\n");
+	}
+
+      /* We're not reporting this breakpoint to GDB, so apply the
+	 decr_pc_after_break adjustment to the inferior's regcache
+	 ourselves.  */
+
+      if (the_low_target.set_pc != NULL)
+	{
+	  struct regcache *regcache
+	    = get_thread_regcache (get_lwp_thread (event_child), 1);
+	  (*the_low_target.set_pc) (regcache, event_child->stop_pc);
+	}
+
+      /* We've finished stepping over a breakpoint.  We've stopped all
+	 LWPs momentarily except the stepping one.  This is where we
+	 resume them all again.  We're going to keep waiting, so use
+	 proceed, which handles stepping over the next breakpoint.  */
+      if (debug_threads)
+	fprintf (stderr, "proceeding all threads.\n");
+      proceed_all_lwps ();
+      goto retry;
+    }
+
+  if (debug_threads)
+    {
+      if (event_child->last_resume_kind == resume_step)
+	fprintf (stderr, "GDB wanted to single-step, reporting event.\n");
+      if (event_child->stopped_by_watchpoint)
+	fprintf (stderr, "Stopped by watchpoint.\n");
+      if (debug_threads)
+	fprintf (stderr, "Hit a non-gdbserver trap event.\n");
+    }
+
+  /* Alright, we're going to report a stop.  */
+
+  if (!non_stop)
+    {
+      /* In all-stop, stop all threads.  */
+      stop_all_lwps ();
+
+      /* If we're not waiting for a specific LWP, choose an event LWP
+	 from among those that have had events.  Giving equal priority
+	 to all LWPs that have had events helps prevent
+	 starvation.  */
+      if (ptid_equal (ptid, minus_one_ptid))
+	{
+	  event_child->status_pending_p = 1;
+	  event_child->status_pending = w;
+
+	  select_event_lwp (&event_child);
+
+	  event_child->status_pending_p = 0;
+	  w = event_child->status_pending;
+	}
+
+      /* Now that we've selected our final event LWP, cancel any
+	 breakpoints in other LWPs that have hit a GDB breakpoint.
+	 See the comment in cancel_breakpoints_callback to find out
+	 why.  */
+      find_inferior (&all_lwps, cancel_breakpoints_callback, event_child);
+    }
+  else
+    {
+      /* If we just finished a step-over, then all threads had been
+	 momentarily paused.  In all-stop, that's fine, we want
+	 threads stopped by now anyway.  In non-stop, we need to
+	 re-resume threads that GDB wanted to be running.  */
+      if (step_over_finished)
+	unstop_all_lwps (event_child);
+    }
+
   ourstatus->kind = TARGET_WAITKIND_STOPPED;
 
   /* Do this before the gdb_wants_all_stopped calls below, since they
      always set last_resume_kind to resume_stop.  */
-  if (lwp->last_resume_kind == resume_stop && WSTOPSIG (w) == SIGSTOP)
+  if (event_child->last_resume_kind == resume_stop && WSTOPSIG (w) == SIGSTOP)
     {
       /* A thread that has been requested to stop by GDB with vCont;t,
 	 and it stopped cleanly, so report as SIG0.  The use of
 	 SIGSTOP is an implementation detail.  */
       ourstatus->value.sig = TARGET_SIGNAL_0;
     }
-  else if (lwp->last_resume_kind == resume_stop && WSTOPSIG (w) != SIGSTOP)
+  else if (event_child->last_resume_kind == resume_stop && WSTOPSIG (w) != SIGSTOP)
     {
       /* A thread that has been requested to stop by GDB with vCont;t,
 	 but, it stopped for other reasons.  */
@@ -1797,17 +1869,6 @@ retry:
 
   if (!non_stop)
     {
-      /* In all-stop, stop all threads, we're about to report an event
-	 to GDB.  */
-      stop_all_lwps ();
-
-      /* Do not leave a pending single-step finish to be reported to
-	 the client.  The client will give us a new action for this
-	 thread, possibly a continue request --- otherwise, the client
-	 would consider this pending SIGTRAP reported later a spurious
-	 signal.  */
-      cancel_finished_single_steps (lwp);
-
       /* From GDB's perspective, all-stop mode always stops all
 	 threads implicitly.  Tag all threads as "want-stopped".  */
       gdb_wants_all_stopped ();
@@ -1817,17 +1878,17 @@ retry:
       /* We're reporting this LWP as stopped.  Update it's
       	 "want-stopped" state to what the client wants, until it gets
       	 a new resume action.  */
-      gdb_wants_lwp_stopped (&lwp->head);
+      gdb_wants_lwp_stopped (&event_child->head);
     }
 
   if (debug_threads)
     fprintf (stderr, "linux_wait ret = %s, %d, %d\n",
-	     target_pid_to_str (lwp->head.id),
+	     target_pid_to_str (ptid_of (event_child)),
 	     ourstatus->kind,
 	     ourstatus->value.sig);
 
-  get_lwp_thread (lwp)->last_status = *ourstatus;
-  return lwp->head.id;
+  get_lwp_thread (event_child)->last_status = *ourstatus;
+  return ptid_of (event_child);
 }
 
 /* Get rid of any pending event in the pipe.  */
@@ -1956,45 +2017,6 @@ mark_lwp_dead (struct lwp_info *lwp, int
   lwp->stop_expected = 0;
 }
 
-static int
-cancel_finished_single_step (struct inferior_list_entry *entry, void *except)
-{
-  struct lwp_info *lwp = (struct lwp_info *) entry;
-  struct thread_info *saved_inferior;
-
-  if (lwp == except)
-    return 0;
-
-  saved_inferior = current_inferior;
-  current_inferior = get_lwp_thread (lwp);
-
-  /* Do not leave a pending single-step finish to be reported to the
-     client.  The client will give us a new action for this thread,
-     possibly a continue request --- otherwise, the client would
-     consider this pending SIGTRAP reported later a spurious
-     signal.  */
-  if (lwp->status_pending_p
-      && WSTOPSIG (lwp->status_pending) == SIGTRAP
-      && lwp->stepping
-      && !lwp->stopped_by_watchpoint)
-    {
-      if (debug_threads)
-	fprintf (stderr, "  single-step SIGTRAP cancelled\n");
-
-      lwp->status_pending_p = 0;
-      lwp->status_pending = 0;
-    }
-
-  current_inferior = saved_inferior;
-  return 0;
-}
-
-static void
-cancel_finished_single_steps (struct lwp_info *except)
-{
-  find_inferior (&all_lwps, cancel_finished_single_step, except);
-}
-
 static void
 wait_for_sigstop (struct inferior_list_entry *entry)
 {


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