Fix for even more missed events; eliminate thread-hop code.
[deliverable/binutils-gdb.git] / gdb / infrun.c
index ed8ec302df41c2ea8bb469c0d95585002147cdf2..60abd8ca663b6905d73985132d84b6961870e1c8 100644 (file)
@@ -972,11 +972,6 @@ static ptid_t singlestep_ptid;
 /* PC when we started this single-step.  */
 static CORE_ADDR singlestep_pc;
 
-/* If another thread hit the singlestep breakpoint, we save the original
-   thread here so that we can resume single-stepping it later.  */
-static ptid_t saved_singlestep_ptid;
-static int stepping_past_singlestep_breakpoint;
-
 /* Info about an instruction that is being stepped over.  Invalid if
    ASPACE is NULL.  */
 
@@ -1943,24 +1938,8 @@ a command like `return' or `jump' to continue execution."));
       resume_ptid = user_visible_resume_ptid (step);
 
       /* Maybe resume a single thread after all.  */
-      if (singlestep_breakpoints_inserted_p
-         && stepping_past_singlestep_breakpoint)
-       {
-         /* The situation here is as follows.  In thread T1 we wanted to
-            single-step.  Lacking hardware single-stepping we've
-            set breakpoint at the PC of the next instruction -- call it
-            P.  After resuming, we've hit that breakpoint in thread T2.
-            Now we've removed original breakpoint, inserted breakpoint
-            at P+1, and try to step to advance T2 past breakpoint.
-            We need to step only T2, as if T1 is allowed to freely run,
-            it can run past P, and if other threads are allowed to run,
-            they can hit breakpoint at P+1, and nested hits of single-step
-            breakpoints is not something we'd want -- that's complicated
-            to support, and has no value.  */
-         resume_ptid = inferior_ptid;
-       }
-      else if ((step || singlestep_breakpoints_inserted_p)
-              && tp->control.trap_expected)
+      if ((step || singlestep_breakpoints_inserted_p)
+         && tp->control.trap_expected)
        {
          /* We're allowing a thread to run past a breakpoint it has
             hit, by single-stepping the thread with the breakpoint
@@ -2222,6 +2201,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
   gdbarch = get_regcache_arch (regcache);
   aspace = get_regcache_aspace (regcache);
   pc = regcache_read_pc (regcache);
+  tp = inferior_thread ();
 
   if (step > 0)
     step_start_function = find_pc_function (pc);
@@ -2278,13 +2258,19 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
         thread that reported the most recent event.  If a step-over
         is required it returns TRUE and sets the current thread to
         the old thread.  */
+
+      /* Store the prev_pc for the stepping thread too, needed by
+        switch_back_to_stepping thread.  */
+      tp->prev_pc = regcache_read_pc (get_current_regcache ());
+
       if (prepare_to_proceed (step))
-       force_step = 1;
+       {
+         force_step = 1;
+         /* The current thread changed.  */
+         tp = inferior_thread ();
+       }
     }
 
-  /* prepare_to_proceed may change the current thread.  */
-  tp = inferior_thread ();
-
   if (force_step)
     tp->control.trap_expected = 1;
 
@@ -2434,8 +2420,6 @@ init_wait_for_inferior (void)
 
   clear_proceed_status ();
 
-  stepping_past_singlestep_breakpoint = 0;
-
   target_last_wait_ptid = minus_one_ptid;
 
   previous_inferior_ptid = inferior_ptid;
@@ -2443,6 +2427,9 @@ init_wait_for_inferior (void)
 
   /* Discard any skipped inlined frames.  */
   clear_inline_frame_state (minus_one_ptid);
+
+  singlestep_ptid = null_ptid;
+  singlestep_pc = 0;
 }
 
 \f
@@ -2453,7 +2440,6 @@ init_wait_for_inferior (void)
 enum infwait_states
 {
   infwait_normal_state,
-  infwait_thread_hop_state,
   infwait_step_watch_state,
   infwait_nonstep_watch_state
 };
@@ -2484,6 +2470,12 @@ struct execution_control_state
      infwait_nonstep_watch_state state, and the thread reported an
      event.  */
   int stepped_after_stopped_by_watchpoint;
+
+  /* True if the event thread hit the single-step breakpoint of
+     another thread.  Thus the event doesn't cause a stop, the thread
+     needs to be single-stepped past the single-step breakpoint before
+     we can switch back to the original stepping thread.  */
+  int hit_singlestep_breakpoint;
 };
 
 static void handle_inferior_event (struct execution_control_state *ecs);
@@ -3364,11 +3356,6 @@ handle_inferior_event (struct execution_control_state *ecs)
 
   switch (infwait_state)
     {
-    case infwait_thread_hop_state:
-      if (debug_infrun)
-        fprintf_unfiltered (gdb_stdlog, "infrun: infwait_thread_hop_state\n");
-      break;
-
     case infwait_normal_state:
       if (debug_infrun)
         fprintf_unfiltered (gdb_stdlog, "infrun: infwait_normal_state\n");
@@ -3937,163 +3924,6 @@ handle_signal_stop (struct execution_control_state *ecs)
       return;
     }
 
-  if (stepping_past_singlestep_breakpoint)
-    {
-      gdb_assert (singlestep_breakpoints_inserted_p);
-      gdb_assert (ptid_equal (singlestep_ptid, ecs->ptid));
-      gdb_assert (!ptid_equal (singlestep_ptid, saved_singlestep_ptid));
-
-      stepping_past_singlestep_breakpoint = 0;
-
-      /* We've either finished single-stepping past the single-step
-         breakpoint, or stopped for some other reason.  It would be nice if
-         we could tell, but we can't reliably.  */
-      if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
-       {
-         if (debug_infrun)
-           fprintf_unfiltered (gdb_stdlog,
-                               "infrun: stepping_past_"
-                               "singlestep_breakpoint\n");
-         /* Pull the single step breakpoints out of the target.  */
-         if (!ptid_equal (ecs->ptid, inferior_ptid))
-           context_switch (ecs->ptid);
-         remove_single_step_breakpoints ();
-         singlestep_breakpoints_inserted_p = 0;
-
-         ecs->event_thread->control.trap_expected = 0;
-
-         context_switch (saved_singlestep_ptid);
-         if (deprecated_context_hook)
-           deprecated_context_hook (pid_to_thread_id (saved_singlestep_ptid));
-
-         resume (1, GDB_SIGNAL_0);
-         prepare_to_wait (ecs);
-         return;
-       }
-    }
-
-  /* See if a thread hit a thread-specific breakpoint that was meant for
-     another thread.  If so, then step that thread past the breakpoint,
-     and continue it.  */
-
-  if (ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
-    {
-      int thread_hop_needed = 0;
-      struct address_space *aspace = 
-       get_regcache_aspace (get_thread_regcache (ecs->ptid));
-
-      /* Check if a regular breakpoint has been hit before checking
-         for a potential single step breakpoint.  Otherwise, GDB will
-         not see this breakpoint hit when stepping onto breakpoints.  */
-      if (regular_breakpoint_inserted_here_p (aspace, stop_pc))
-       {
-         if (!breakpoint_thread_match (aspace, stop_pc, ecs->ptid))
-           thread_hop_needed = 1;
-       }
-      else if (singlestep_breakpoints_inserted_p)
-       {
-         /* We have not context switched yet, so this should be true
-            no matter which thread hit the singlestep breakpoint.  */
-         gdb_assert (ptid_equal (inferior_ptid, singlestep_ptid));
-         if (debug_infrun)
-           fprintf_unfiltered (gdb_stdlog, "infrun: software single step "
-                               "trap for %s\n",
-                               target_pid_to_str (ecs->ptid));
-
-         /* The call to in_thread_list is necessary because PTIDs sometimes
-            change when we go from single-threaded to multi-threaded.  If
-            the singlestep_ptid is still in the list, assume that it is
-            really different from ecs->ptid.  */
-         if (!ptid_equal (singlestep_ptid, ecs->ptid)
-             && in_thread_list (singlestep_ptid))
-           {
-             /* If the PC of the thread we were trying to single-step
-                has changed, discard this event (which we were going
-                to ignore anyway), and pretend we saw that thread
-                trap.  This prevents us continuously moving the
-                single-step breakpoint forward, one instruction at a
-                time.  If the PC has changed, then the thread we were
-                trying to single-step has trapped or been signalled,
-                but the event has not been reported to GDB yet.
-
-                There might be some cases where this loses signal
-                information, if a signal has arrived at exactly the
-                same time that the PC changed, but this is the best
-                we can do with the information available.  Perhaps we
-                should arrange to report all events for all threads
-                when they stop, or to re-poll the remote looking for
-                this particular thread (i.e. temporarily enable
-                schedlock).  */
-
-            CORE_ADDR new_singlestep_pc
-              = regcache_read_pc (get_thread_regcache (singlestep_ptid));
-
-            if (new_singlestep_pc != singlestep_pc)
-              {
-                enum gdb_signal stop_signal;
-
-                if (debug_infrun)
-                  fprintf_unfiltered (gdb_stdlog, "infrun: unexpected thread,"
-                                      " but expected thread advanced also\n");
-
-                /* The current context still belongs to
-                   singlestep_ptid.  Don't swap here, since that's
-                   the context we want to use.  Just fudge our
-                   state and continue.  */
-                 stop_signal = ecs->event_thread->suspend.stop_signal;
-                 ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
-                 ecs->ptid = singlestep_ptid;
-                 ecs->event_thread = find_thread_ptid (ecs->ptid);
-                 ecs->event_thread->suspend.stop_signal = stop_signal;
-                 stop_pc = new_singlestep_pc;
-               }
-             else
-              {
-                if (debug_infrun)
-                  fprintf_unfiltered (gdb_stdlog,
-                                      "infrun: unexpected thread\n");
-
-                thread_hop_needed = 1;
-                stepping_past_singlestep_breakpoint = 1;
-                saved_singlestep_ptid = singlestep_ptid;
-              }
-           }
-       }
-
-      if (thread_hop_needed)
-       {
-         if (debug_infrun)
-           fprintf_unfiltered (gdb_stdlog, "infrun: thread_hop_needed\n");
-
-         /* Switch context before touching inferior memory, the
-            previous thread may have exited.  */
-         if (!ptid_equal (inferior_ptid, ecs->ptid))
-           context_switch (ecs->ptid);
-
-         /* Saw a breakpoint, but it was hit by the wrong thread.
-            Just continue.  */
-
-         if (singlestep_breakpoints_inserted_p)
-           {
-             /* Pull the single step breakpoints out of the target.  */
-             remove_single_step_breakpoints ();
-             singlestep_breakpoints_inserted_p = 0;
-           }
-
-         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;
-           }
-
-         ecs->event_thread->stepping_over_breakpoint = 1;
-         keep_going (ecs);
-         return;
-       }
-    }
-
   /* See if something interesting happened to the non-current thread.  If
      so, then switch to that thread.  */
   if (!ptid_equal (ecs->ptid, inferior_ptid))
@@ -4111,9 +3941,36 @@ handle_signal_stop (struct execution_control_state *ecs)
   frame = get_current_frame ();
   gdbarch = get_frame_arch (frame);
 
+  /* Pull the single step breakpoints out of the target.  */
   if (singlestep_breakpoints_inserted_p)
     {
-      /* Pull the single step breakpoints out of the target.  */
+      /* However, before doing so, if this single-step breakpoint was
+        actually for another thread, set this thread up for moving
+        past it.  */
+      if (!ptid_equal (ecs->ptid, singlestep_ptid)
+         && ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP)
+       {
+         struct regcache *regcache;
+         struct address_space *aspace;
+         CORE_ADDR pc;
+
+         regcache = get_thread_regcache (ecs->ptid);
+         aspace = get_regcache_aspace (regcache);
+         pc = regcache_read_pc (regcache);
+         if (single_step_breakpoint_inserted_here_p (aspace, pc))
+           {
+             if (debug_infrun)
+               {
+                 fprintf_unfiltered (gdb_stdlog,
+                                     "infrun: [%s] hit step over single-step"
+                                     " breakpoint of [%s]\n",
+                                     target_pid_to_str (ecs->ptid),
+                                     target_pid_to_str (singlestep_ptid));
+               }
+             ecs->hit_singlestep_breakpoint = 1;
+           }
+       }
+
       remove_single_step_breakpoints ();
       singlestep_breakpoints_inserted_p = 0;
     }
@@ -4308,6 +4165,12 @@ handle_signal_stop (struct execution_control_state *ecs)
     random_signal = !(ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
                      && currently_stepping (ecs->event_thread));
 
+  /* Perhaps the thread hit a single-step breakpoint of _another_
+     thread.  Single-step breakpoints are transparent to the
+     breakpoints module.  */
+  if (random_signal)
+    random_signal = !ecs->hit_singlestep_breakpoint;
+
   /* No?  Perhaps we got a moribund watchpoint.  */
   if (random_signal)
     random_signal = !stopped_by_watchpoint;
@@ -5271,12 +5134,16 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
                                 ecs->event_thread);
       if (tp)
        {
+         struct frame_info *frame;
+         struct gdbarch *gdbarch;
+
          /* However, if the current thread is blocked on some internal
             breakpoint, and we simply need to step over that breakpoint
             to get it going again, do that first.  */
          if ((ecs->event_thread->control.trap_expected
               && ecs->event_thread->suspend.stop_signal != GDB_SIGNAL_TRAP)
-             || ecs->event_thread->stepping_over_breakpoint)
+             || ecs->event_thread->stepping_over_breakpoint
+             || ecs->hit_singlestep_breakpoint)
            {
              keep_going (ecs);
              return 1;
@@ -5326,7 +5193,52 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
          ecs->event_thread = tp;
          ecs->ptid = tp->ptid;
          context_switch (ecs->ptid);
-         keep_going (ecs);
+
+         stop_pc = regcache_read_pc (get_thread_regcache (ecs->ptid));
+         frame = get_current_frame ();
+         gdbarch = get_frame_arch (frame);
+
+         /* If the PC of the thread we were trying to single-step has
+            changed, then the thread we were trying to single-step
+            has trapped or been signalled, but the event has not been
+            reported to GDB yet.  Re-poll the remote looking for this
+            particular thread (i.e. temporarily enable schedlock) by:
+
+              - setting a break at the current PC
+              - resuming that particular thread, only (by setting
+                trap expected)
+
+            This prevents us continuously moving the single-step
+            breakpoint forward, one instruction at a time,
+            overstepping.  */
+
+         if (gdbarch_software_single_step_p (gdbarch)
+             && stop_pc != tp->prev_pc)
+           {
+             if (debug_infrun)
+               fprintf_unfiltered (gdb_stdlog,
+                                   "infrun: expected thread advanced also\n");
+
+             insert_single_step_breakpoint (get_frame_arch (frame),
+                                            get_frame_address_space (frame),
+                                            stop_pc);
+             singlestep_breakpoints_inserted_p = 1;
+             ecs->event_thread->control.trap_expected = 1;
+             singlestep_ptid = inferior_ptid;
+             singlestep_pc = stop_pc;
+
+             resume (0, GDB_SIGNAL_0);
+             prepare_to_wait (ecs);
+           }
+         else
+           {
+             if (debug_infrun)
+               fprintf_unfiltered (gdb_stdlog,
+                                   "infrun: expected thread still "
+                                   "hasn't advanced\n");
+             keep_going (ecs);
+           }
+
          return 1;
        }
     }
@@ -5799,7 +5711,8 @@ keep_going (struct execution_control_state *ecs)
         (watchpoints, etc.) but the one we're stepping over, step one
         instruction, and then re-insert the breakpoint when that step
         is finished.  */
-      if (ecs->event_thread->stepping_over_breakpoint
+      if ((ecs->hit_singlestep_breakpoint
+          || ecs->event_thread->stepping_over_breakpoint)
          && !use_displaced_stepping (get_regcache_arch (regcache)))
        {
          set_step_over_info (get_regcache_aspace (regcache),
@@ -5821,7 +5734,8 @@ keep_going (struct execution_control_state *ecs)
        }
 
       ecs->event_thread->control.trap_expected
-       = ecs->event_thread->stepping_over_breakpoint;
+       = (ecs->event_thread->stepping_over_breakpoint
+          || ecs->hit_singlestep_breakpoint);
 
       /* Do not deliver GDB_SIGNAL_TRAP (except when the user
         explicitly specifies that such a signal should be delivered
This page took 0.030284 seconds and 4 git commands to generate.