gdb/
[deliverable/binutils-gdb.git] / gdb / infrun.c
index 7026d08cf437f61c3e53e741fd01e30b5291ec75..85d889a5a29f043a2ca653faa8614745a46abeea 100644 (file)
@@ -78,7 +78,7 @@ static int currently_stepping (struct execution_control_state *ecs);
 
 static void xdb_handle_command (char *args, int from_tty);
 
-static int prepare_to_proceed (void);
+static int prepare_to_proceed (int);
 
 void _initialize_infrun (void);
 
@@ -445,6 +445,11 @@ static CORE_ADDR singlestep_pc;
    thread here so that we can resume single-stepping it later.  */
 static ptid_t saved_singlestep_ptid;
 static int stepping_past_singlestep_breakpoint;
+
+/* Similarly, if we are stepping another thread past a breakpoint,
+   save the original thread here so that we can resume stepping it later.  */
+static ptid_t stepping_past_breakpoint_ptid;
+static int stepping_past_breakpoint;
 \f
 
 /* Things to clean up if we QUIT out of resume ().  */
@@ -578,15 +583,41 @@ a command like `return' or `jump' to continue execution."));
 
       resume_ptid = RESUME_ALL;        /* Default */
 
-      if ((step || singlestep_breakpoints_inserted_p)
-         && (stepping_past_singlestep_breakpoint
-             || (!breakpoints_inserted && breakpoint_here_p (read_pc ()))))
+      /* If STEP is set, it's a request to use hardware stepping
+        facilities.  But in that case, we should never
+        use singlestep breakpoint.  */
+      gdb_assert (!(singlestep_breakpoints_inserted_p && step));
+
+      if (singlestep_breakpoints_inserted_p
+         && stepping_past_singlestep_breakpoint)
        {
-         /* Stepping past a breakpoint without inserting breakpoints.
-            Make sure only the current thread gets to step, so that
-            other threads don't sneak past breakpoints while they are
-            not inserted. */
+         /* 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;
+       }
 
+      if (step && breakpoint_here_p (read_pc ())
+         && !breakpoint_inserted_here_p (read_pc ()))
+       {
+         /* We're stepping, have breakpoint at PC, and it's 
+            not inserted.  Most likely, proceed has noticed that
+            we have breakpoint and tries to single-step over it,
+            so that it's not hit.  In which case, we need to
+            single-step only this thread, and keep others stopped,
+            as they can miss this breakpoint if allowed to run.  
+
+            The current code either has all breakpoints inserted, 
+            or all removed, so if we let other threads run,
+            we can actually miss any breakpoint, not the one at PC.  */
          resume_ptid = inferior_ptid;
        }
 
@@ -642,7 +673,7 @@ clear_proceed_status (void)
 /* This should be suitable for any targets that support threads. */
 
 static int
-prepare_to_proceed (void)
+prepare_to_proceed (int step)
 {
   ptid_t wait_ptid;
   struct target_waitstatus wait_status;
@@ -650,42 +681,35 @@ prepare_to_proceed (void)
   /* Get the last target status returned by target_wait().  */
   get_last_target_status (&wait_ptid, &wait_status);
 
-  /* Make sure we were stopped either at a breakpoint, or because
-     of a Ctrl-C.  */
+  /* Make sure we were stopped at a breakpoint.  */
   if (wait_status.kind != TARGET_WAITKIND_STOPPED
-      || (wait_status.value.sig != TARGET_SIGNAL_TRAP
-         && wait_status.value.sig != TARGET_SIGNAL_INT))
+      || wait_status.value.sig != TARGET_SIGNAL_TRAP)
     {
       return 0;
     }
 
+  /* Switched over from WAIT_PID.  */
   if (!ptid_equal (wait_ptid, minus_one_ptid)
-      && !ptid_equal (inferior_ptid, wait_ptid))
+      && !ptid_equal (inferior_ptid, wait_ptid)
+      && breakpoint_here_p (read_pc_pid (wait_ptid)))
     {
-      /* Switched over from WAIT_PID.  */
-      CORE_ADDR wait_pc = read_pc_pid (wait_ptid);
-
-      if (wait_pc != read_pc ())
+      /* If stepping, remember current thread to switch back to.  */
+      if (step)
        {
-         /* Switch back to WAIT_PID thread.  */
-         inferior_ptid = wait_ptid;
-
-         /* FIXME: This stuff came from switch_to_thread() in
-            thread.c (which should probably be a public function).  */
-         reinit_frame_cache ();
-         registers_changed ();
-         stop_pc = wait_pc;
+         stepping_past_breakpoint = 1;
+         stepping_past_breakpoint_ptid = inferior_ptid;
        }
 
+      /* Switch back to WAIT_PID thread.  */
+      switch_to_thread (wait_ptid);
+
       /* We return 1 to indicate that there is a breakpoint here,
-         so we need to step over it before continuing to avoid
-         hitting it straight away. */
-      if (breakpoint_here_p (wait_pc))
-       return 1;
+        so we need to step over it before continuing to avoid
+        hitting it straight away. */
+      return 1;
     }
 
   return 0;
-
 }
 
 /* Record the pc of the program the last time it stopped.  This is
@@ -751,7 +775,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
      prepare_to_proceed checks the current thread against the 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. */
-  if (prepare_to_proceed () && breakpoint_here_p (read_pc ()))
+  if (prepare_to_proceed (step))
     oneproc = 1;
 
   if (oneproc)
@@ -872,6 +896,7 @@ init_wait_for_inferior (void)
   clear_proceed_status ();
 
   stepping_past_singlestep_breakpoint = 0;
+  stepping_past_breakpoint = 0;
 }
 \f
 /* This enum encodes possible reasons for doing a target_wait, so that
@@ -882,6 +907,7 @@ enum infwait_states
 {
   infwait_normal_state,
   infwait_thread_hop_state,
+  infwait_step_watch_state,
   infwait_nonstep_watch_state
 };
 
@@ -1141,8 +1167,8 @@ context_switch (struct execution_control_state *ecs)
                         &ecs->stepping_through_solib_catchpoints,
                         &ecs->current_line, &ecs->current_symtab);
     }
-  inferior_ptid = ecs->ptid;
-  reinit_frame_cache ();
+
+  switch_to_thread (ecs->ptid);
 }
 
 static void
@@ -1221,17 +1247,12 @@ adjust_pc_after_break (struct execution_control_state *ecs)
    by an event from the inferior, figure out what it means and take
    appropriate action.  */
 
-int stepped_after_stopped_by_watchpoint;
-
 void
 handle_inferior_event (struct execution_control_state *ecs)
 {
-  /* NOTE: bje/2005-05-02: If you're looking at this code and thinking
-     that the variable stepped_after_stopped_by_watchpoint isn't used,
-     then you're wrong!  See remote.c:remote_stopped_data_address.  */
-
   int sw_single_step_trap_p = 0;
-  int stopped_by_watchpoint = -1;      /* Mark as unknown.  */
+  int stopped_by_watchpoint;
+  int stepped_after_stopped_by_watchpoint = 0;
 
   /* Cache the last pid/waitstatus. */
   target_last_wait_ptid = ecs->ptid;
@@ -1251,7 +1272,14 @@ handle_inferior_event (struct execution_control_state *ecs)
     case infwait_normal_state:
       if (debug_infrun)
         fprintf_unfiltered (gdb_stdlog, "infrun: infwait_normal_state\n");
-      stepped_after_stopped_by_watchpoint = 0;
+      break;
+
+    case infwait_step_watch_state:
+      if (debug_infrun)
+        fprintf_unfiltered (gdb_stdlog,
+                           "infrun: infwait_step_watch_state\n");
+
+      stepped_after_stopped_by_watchpoint = 1;
       break;
 
     case infwait_nonstep_watch_state:
@@ -1338,10 +1366,6 @@ handle_inferior_event (struct execution_control_state *ecs)
 #endif
          target_terminal_inferior ();
 
-         /* Try to reenable shared library breakpoints, additional
-            code segments in shared libraries might be mapped in now. */
-         re_enable_breakpoints_in_shlibs ();
-
          /* If requested, stop when the dynamic linker notifies
             gdb of events.  This allows the user to get control
             and place breakpoints in initializer routines for
@@ -1440,7 +1464,7 @@ handle_inferior_event (struct execution_control_state *ecs)
 
       stop_pc = read_pc ();
 
-      stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid, 0);
+      stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
 
       ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
 
@@ -1488,7 +1512,7 @@ handle_inferior_event (struct execution_control_state *ecs)
       ecs->saved_inferior_ptid = inferior_ptid;
       inferior_ptid = ecs->ptid;
 
-      stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid, 0);
+      stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
 
       ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
       inferior_ptid = ecs->saved_inferior_ptid;
@@ -1604,6 +1628,37 @@ handle_inferior_event (struct execution_control_state *ecs)
 
   stepping_past_singlestep_breakpoint = 0;
 
+  if (stepping_past_breakpoint)
+    {
+      stepping_past_breakpoint = 0;
+
+      /* 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)
+       {
+         if (debug_infrun)
+           fprintf_unfiltered (gdb_stdlog,
+                               "infrun: stepping_past_breakpoint\n");
+
+         /* Pull the single step breakpoints out of the target.  */
+         if (singlestep_breakpoints_inserted_p)
+           {
+             remove_single_step_breakpoints ();
+             singlestep_breakpoints_inserted_p = 0;
+           }
+
+         /* Note: We do not call context_switch at this point, as the
+            context is already set up for stepping the original thread.  */
+         switch_to_thread (stepping_past_breakpoint_ptid);
+         /* Suppress spurious "Switching to ..." message.  */
+         previous_inferior_ptid = inferior_ptid;
+
+         resume (1, TARGET_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.  */
@@ -1770,24 +1825,20 @@ handle_inferior_event (struct execution_control_state *ecs)
       singlestep_breakpoints_inserted_p = 0;
     }
 
-  /* It may not be necessary to disable the watchpoint to stop over
-     it.  For example, the PA can (with some kernel cooperation)
-     single step over a watchpoint without disabling the watchpoint.  */
-  if (HAVE_STEPPABLE_WATCHPOINT && STOPPED_BY_WATCHPOINT (ecs->ws))
+  if (stepped_after_stopped_by_watchpoint)
+    stopped_by_watchpoint = 0;
+  else
+    stopped_by_watchpoint = watchpoints_triggered (&ecs->ws);
+
+  /* If necessary, step over this watchpoint.  We'll be back to display
+     it in a moment.  */
+  if (stopped_by_watchpoint
+      && (HAVE_STEPPABLE_WATCHPOINT
+         || gdbarch_have_nonsteppable_watchpoint (current_gdbarch)))
     {
       if (debug_infrun)
        fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n");
-      resume (1, 0);
-      prepare_to_wait (ecs);
-      return;
-    }
 
-  /* It is far more common to need to disable a watchpoint to step
-     the inferior over it.  FIXME.  What else might a debug
-     register or page protection watchpoint scheme need here?  */
-  if (gdbarch_have_nonsteppable_watchpoint (current_gdbarch)
-      && STOPPED_BY_WATCHPOINT (ecs->ws))
-    {
       /* At this point, we are stopped at an instruction which has
          attempted to write to a piece of memory under control of
          a watchpoint.  The instruction hasn't actually executed
@@ -1797,31 +1848,31 @@ handle_inferior_event (struct execution_control_state *ecs)
 
          In order to make watchpoints work `right', we really need
          to complete the memory write, and then evaluate the
-         watchpoint expression.  The following code does that by
-         removing the watchpoint (actually, all watchpoints and
-         breakpoints), single-stepping the target, re-inserting
-         watchpoints, and then falling through to let normal
-         single-step processing handle proceed.  Since this
-         includes evaluating watchpoints, things will come to a
-         stop in the correct manner.  */
-
-      if (debug_infrun)
-       fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n");
-      remove_breakpoints ();
+         watchpoint expression.  We do this by single-stepping the
+        target.
+
+        It may not be necessary to disable the watchpoint to stop over
+        it.  For example, the PA can (with some kernel cooperation)
+        single step over a watchpoint without disabling the watchpoint.
+
+        It is far more common to need to disable a watchpoint to step
+        the inferior over it.  If we have non-steppable watchpoints,
+        we must disable the current watchpoint; it's simplest to
+        disable all watchpoints and breakpoints.  */
+        
+      if (!HAVE_STEPPABLE_WATCHPOINT)
+       remove_breakpoints ();
       registers_changed ();
       target_resume (ecs->ptid, 1, TARGET_SIGNAL_0);   /* Single step */
-
       ecs->waiton_ptid = ecs->ptid;
-      ecs->wp = &(ecs->ws);
-      ecs->infwait_state = infwait_nonstep_watch_state;
+      if (HAVE_STEPPABLE_WATCHPOINT)
+       ecs->infwait_state = infwait_step_watch_state;
+      else
+       ecs->infwait_state = infwait_nonstep_watch_state;
       prepare_to_wait (ecs);
       return;
     }
 
-  /* It may be possible to simply continue after a watchpoint.  */
-  if (HAVE_CONTINUABLE_WATCHPOINT)
-    stopped_by_watchpoint = STOPPED_BY_WATCHPOINT (ecs->ws);
-
   ecs->stop_func_start = 0;
   ecs->stop_func_end = 0;
   ecs->stop_func_name = 0;
@@ -1943,8 +1994,7 @@ handle_inferior_event (struct execution_control_state *ecs)
       else
        {
          /* See if there is a breakpoint at the current PC.  */
-         stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid,
-                                           stopped_by_watchpoint);
+         stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
 
          /* Following in case break condition called a
             function.  */
@@ -2246,10 +2296,6 @@ process_event_stop_test:
 #endif
          target_terminal_inferior ();
 
-         /* Try to reenable shared library breakpoints, additional
-            code segments in shared libraries might be mapped in now. */
-         re_enable_breakpoints_in_shlibs ();
-
          /* If requested, stop when the dynamic linker notifies
             gdb of events.  This allows the user to get control
             and place breakpoints in initializer routines for
@@ -2706,8 +2752,10 @@ process_event_stop_test:
      function.  Fortunately, those days are nearly upon us.  */
 #endif
   {
-    struct frame_id current_frame = get_frame_id (get_current_frame ());
-    if (!(frame_id_inner (current_frame, step_frame_id)))
+    struct frame_info *frame = get_current_frame ();
+    struct frame_id current_frame = get_frame_id (frame);
+    if (!(frame_id_inner (get_frame_arch (frame), current_frame,
+                         step_frame_id)))
       step_frame_id = current_frame;
   }
 
@@ -2838,6 +2886,7 @@ insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
 {
   struct symtab_and_line sr_sal;
 
+  gdb_assert (return_frame != NULL);
   init_sal (&sr_sal);          /* initialize to zeros */
 
   sr_sal.pc = gdbarch_addr_bits_remove
This page took 0.02923 seconds and 4 git commands to generate.