* amd64-tdep.c (amd64_frame_cache): Fix comment.
[deliverable/binutils-gdb.git] / gdb / infrun.c
index ea83f13f1dee444e6b23a2216bbabd17b00f4f1a..6bbee5e81761faebf0eecc9f70d153f84972f8a1 100644 (file)
@@ -44,6 +44,7 @@
 #include "value.h"
 #include "observer.h"
 #include "language.h"
+#include "gdb_assert.h"
 
 /* Prototypes for local functions */
 
@@ -474,6 +475,14 @@ follow_exec (int pid, char *execd_pathname)
    because we cannot remove the breakpoints in the inferior process
    until after the `wait' in `wait_for_inferior'.  */
 static int singlestep_breakpoints_inserted_p = 0;
+
+/* The thread we inserted single-step breakpoints for.  */
+static ptid_t singlestep_ptid;
+
+/* 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;
 \f
 
 /* Things to clean up if we QUIT out of resume ().  */
@@ -560,6 +569,7 @@ resume (int step, enum target_signal sig)
       /* and do not pull these breakpoints until after a `wait' in
          `wait_for_inferior' */
       singlestep_breakpoints_inserted_p = 1;
+      singlestep_ptid = inferior_ptid;
     }
 
   /* Handle any optimized stores to the inferior NOW...  */
@@ -597,7 +607,8 @@ resume (int step, enum target_signal sig)
       resume_ptid = RESUME_ALL;        /* Default */
 
       if ((step || singlestep_breakpoints_inserted_p) &&
-         !breakpoints_inserted && breakpoint_here_p (read_pc ()))
+         (stepping_past_singlestep_breakpoint
+          || (!breakpoints_inserted && breakpoint_here_p (read_pc ()))))
        {
          /* Stepping past a breakpoint without inserting breakpoints.
             Make sure only the current thread gets to step, so that
@@ -896,6 +907,8 @@ init_wait_for_inferior (void)
   number_of_threads_in_syscalls = 0;
 
   clear_proceed_status ();
+
+  stepping_past_singlestep_breakpoint = 0;
 }
 
 static void
@@ -1739,12 +1752,46 @@ handle_inferior_event (struct execution_control_state *ecs)
 
   stop_pc = read_pc_pid (ecs->ptid);
 
+  if (stepping_past_singlestep_breakpoint)
+    {
+      gdb_assert (SOFTWARE_SINGLE_STEP_P () && 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 (stop_signal == TARGET_SIGNAL_TRAP)
+        {
+         /* Pull the single step breakpoints out of the target.  */
+         SOFTWARE_SINGLE_STEP (0, 0);
+         singlestep_breakpoints_inserted_p = 0;
+
+         ecs->random_signal = 0;
+
+         ecs->ptid = saved_singlestep_ptid;
+         context_switch (ecs);
+         if (context_hook)
+           context_hook (pid_to_thread_id (ecs->ptid));
+
+         resume (1, TARGET_SIGNAL_0);
+         prepare_to_wait (ecs);
+         return;
+       }
+    }
+
+  stepping_past_singlestep_breakpoint = 0;
+
   /* 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 (stop_signal == TARGET_SIGNAL_TRAP)
     {
+      int thread_hop_needed = 0;
+
       /* 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.  */
@@ -1752,12 +1799,38 @@ handle_inferior_event (struct execution_control_state *ecs)
        {
          ecs->random_signal = 0;
          if (!breakpoint_thread_match (stop_pc, ecs->ptid))
+           thread_hop_needed = 1;
+       }
+      else if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
+       {
+         ecs->random_signal = 0;
+         /* 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))
+           {
+             thread_hop_needed = 1;
+             stepping_past_singlestep_breakpoint = 1;
+             saved_singlestep_ptid = singlestep_ptid;
+           }
+       }
+
+      if (thread_hop_needed)
            {
              int remove_status;
 
              /* Saw a breakpoint, but it was hit by the wrong thread.
                 Just continue. */
 
+             if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
+               {
+                 /* Pull the single step breakpoints out of the target. */
+                 SOFTWARE_SINGLE_STEP (0, 0);
+                 singlestep_breakpoints_inserted_p = 0;
+               }
+
              remove_status = remove_breakpoints ();
              /* Did we fail to remove breakpoints?  If so, try
                 to set the PC past the bp.  (There's at least
@@ -1799,7 +1872,6 @@ handle_inferior_event (struct execution_control_state *ecs)
                  registers_changed ();
                  return;
                }
-           }
        }
       else if (SOFTWARE_SINGLE_STEP_P () && singlestep_breakpoints_inserted_p)
         {
@@ -3997,8 +4069,8 @@ _initialize_infrun (void)
   int numsigs;
   struct cmd_list_element *c;
 
-  register_gdbarch_swap (&stop_registers, sizeof (stop_registers), NULL);
-  register_gdbarch_swap (NULL, 0, build_infrun);
+  DEPRECATED_REGISTER_GDBARCH_SWAP (stop_registers);
+  deprecated_register_gdbarch_swap (NULL, 0, build_infrun);
 
   add_info ("signals", signals_info,
            "What debugger does when program gets various signals.\n\
This page took 0.024577 seconds and 4 git commands to generate.