2011-06-06 Pedro Alves <pedro@codesourcery.com>
[deliverable/binutils-gdb.git] / gdb / infrun.c
index 1d2a9c74bb56bedb337f7c5b0d3015979bb6ea4f..a5dbcbd5e0d296eef3296de8827a99e19d08429e 100644 (file)
@@ -54,6 +54,7 @@
 #include "inline-frame.h"
 #include "jit.h"
 #include "tracepoint.h"
+#include "continuations.h"
 
 /* Prototypes for local functions */
 
@@ -99,14 +100,10 @@ void _initialize_infrun (void);
 
 void nullify_last_target_wait_ptid (void);
 
-static void insert_step_resume_breakpoint_at_frame (struct frame_info *);
+static void insert_hp_step_resume_breakpoint_at_frame (struct frame_info *);
 
 static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
 
-static void insert_step_resume_breakpoint_at_sal (struct gdbarch *,
-                                                 struct symtab_and_line ,
-                                                 struct frame_id);
-
 static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
 
 /* When set, stop the 'step' command if we enter a function which has
@@ -1601,6 +1598,45 @@ maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc)
   return hw_step;
 }
 
+/* Return a ptid representing the set of threads that we will proceed,
+   in the perspective of the user/frontend.  We may actually resume
+   fewer threads at first, e.g., if a thread is stopped at a
+   breakpoint that needs stepping-off, but that should not be visible
+   to the user/frontend, and neither should the frontend/user be
+   allowed to proceed any of the threads that happen to be stopped for
+   internal run control handling, if a previous command wanted them
+   resumed.  */
+
+ptid_t
+user_visible_resume_ptid (int step)
+{
+  /* By default, resume all threads of all processes.  */
+  ptid_t resume_ptid = RESUME_ALL;
+
+  /* Maybe resume only all threads of the current process.  */
+  if (!sched_multi && target_supports_multi_process ())
+    {
+      resume_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
+    }
+
+  /* Maybe resume a single thread after all.  */
+  if (non_stop)
+    {
+      /* With non-stop mode on, threads are always handled
+        individually.  */
+      resume_ptid = inferior_ptid;
+    }
+  else if ((scheduler_mode == schedlock_on)
+          || (scheduler_mode == schedlock_step
+              && (step || singlestep_breakpoints_inserted_p)))
+    {
+      /* User-settable 'scheduler' mode requires solo thread resume.  */
+      resume_ptid = inferior_ptid;
+    }
+
+  return resume_ptid;
+}
+
 /* Resume the inferior, but allow a QUIT.  This is useful if the user
    wants to interrupt some lengthy single-stepping operation
    (for child processes, the SIGINT goes to the inferior, and so
@@ -1644,8 +1680,10 @@ resume (int step, enum target_signal sig)
   if (debug_infrun)
     fprintf_unfiltered (gdb_stdlog,
                         "infrun: resume (step=%d, signal=%d), "
-                       "trap_expected=%d\n",
-                       step, sig, tp->control.trap_expected);
+                       "trap_expected=%d, current thread [%s] at %s\n",
+                       step, sig, tp->control.trap_expected,
+                       target_pid_to_str (inferior_ptid),
+                       paddress (gdbarch, pc));
 
   /* Normally, by the time we reach `resume', the breakpoints are either
      removed or inserted, as appropriate.  The exception is if we're sitting
@@ -1703,6 +1741,51 @@ a command like `return' or `jump' to continue execution."));
   else if (step)
     step = maybe_software_singlestep (gdbarch, pc);
 
+  /* Currently, our software single-step implementation leads to different
+     results than hardware single-stepping in one situation: when stepping
+     into delivering a signal which has an associated signal handler,
+     hardware single-step will stop at the first instruction of the handler,
+     while software single-step will simply skip execution of the handler.
+
+     For now, this difference in behavior is accepted since there is no
+     easy way to actually implement single-stepping into a signal handler
+     without kernel support.
+
+     However, there is one scenario where this difference leads to follow-on
+     problems: if we're stepping off a breakpoint by removing all breakpoints
+     and then single-stepping.  In this case, the software single-step
+     behavior means that even if there is a *breakpoint* in the signal
+     handler, GDB still would not stop.
+
+     Fortunately, we can at least fix this particular issue.  We detect
+     here the case where we are about to deliver a signal while software
+     single-stepping with breakpoints removed.  In this situation, we
+     revert the decisions to remove all breakpoints and insert single-
+     step breakpoints, and instead we install a step-resume breakpoint
+     at the current address, deliver the signal without stepping, and
+     once we arrive back at the step-resume breakpoint, actually step
+     over the breakpoint we originally wanted to step over.  */
+  if (singlestep_breakpoints_inserted_p
+      && tp->control.trap_expected && sig != TARGET_SIGNAL_0)
+    {
+      /* If we have nested signals or a pending signal is delivered
+        immediately after a handler returns, might might already have
+        a step-resume breakpoint set on the earlier handler.  We cannot
+        set another step-resume breakpoint; just continue on until the
+        original breakpoint is hit.  */
+      if (tp->control.step_resume_breakpoint == NULL)
+       {
+         insert_hp_step_resume_breakpoint_at_frame (get_current_frame ());
+         tp->step_after_step_resume_breakpoint = 1;
+       }
+
+      remove_single_step_breakpoints ();
+      singlestep_breakpoints_inserted_p = 0;
+
+      insert_breakpoints ();
+      tp->control.trap_expected = 0;
+    }
+
   if (should_resume)
     {
       ptid_t resume_ptid;
@@ -1715,15 +1798,7 @@ a command like `return' or `jump' to continue execution."));
       /* Decide the set of threads to ask the target to resume.  Start
         by assuming everything will be resumed, than narrow the set
         by applying increasingly restricting conditions.  */
-
-      /* By default, resume all threads of all processes.  */
-      resume_ptid = RESUME_ALL;
-
-      /* Maybe resume only all threads of the current process.  */
-      if (!sched_multi && target_supports_multi_process ())
-       {
-         resume_ptid = pid_to_ptid (ptid_get_pid (inferior_ptid));
-       }
+      resume_ptid = user_visible_resume_ptid (step);
 
       /* Maybe resume a single thread after all.  */
       if (singlestep_breakpoints_inserted_p
@@ -1757,19 +1832,6 @@ a command like `return' or `jump' to continue execution."));
             breakpoint, not just the one at PC.  */
          resume_ptid = inferior_ptid;
        }
-      else if (non_stop)
-       {
-         /* With non-stop mode on, threads are always handled
-            individually.  */
-         resume_ptid = inferior_ptid;
-       }
-      else if ((scheduler_mode == schedlock_on)
-              || (scheduler_mode == schedlock_step
-                  && (step || singlestep_breakpoints_inserted_p)))
-       {
-         /* User-settable 'scheduler' mode requires solo thread resume.  */
-         resume_ptid = inferior_ptid;
-       }
 
       if (gdbarch_cannot_step_breakpoint (gdbarch))
        {
@@ -1956,6 +2018,12 @@ prepare_to_proceed (int step)
          /* Switch back to WAIT_PID thread.  */
          switch_to_thread (wait_ptid);
 
+         if (debug_infrun)
+           fprintf_unfiltered (gdb_stdlog,
+                               "infrun: prepare_to_proceed (step=%d), "
+                               "switched to [%s]\n",
+                               step, target_pid_to_str (inferior_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.  */
@@ -1995,9 +2063,14 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
     {
       /* The target for some reason decided not to resume.  */
       normal_stop ();
+      if (target_can_async_p ())
+       inferior_event_handler (INF_EXEC_COMPLETE, NULL);
       return;
     }
 
+  /* We'll update this if & when we switch to a new thread.  */
+  previous_inferior_ptid = inferior_ptid;
+
   regcache = get_current_regcache ();
   gdbarch = get_regcache_arch (regcache);
   aspace = get_regcache_aspace (regcache);
@@ -2064,6 +2137,24 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
   /* prepare_to_proceed may change the current thread.  */
   tp = inferior_thread ();
 
+  if (oneproc)
+    {
+      tp->control.trap_expected = 1;
+      /* If displaced stepping is enabled, we can step over the
+        breakpoint without hitting it, so leave all breakpoints
+        inserted.  Otherwise we need to disable all breakpoints, step
+        one instruction, and then re-add them when that step is
+        finished.  */
+      if (!use_displaced_stepping (gdbarch))
+       remove_breakpoints ();
+    }
+
+  /* We can insert breakpoints if we're not trying to step over one,
+     or if we are stepping over one but we're using displaced stepping
+     to do so.  */
+  if (! tp->control.trap_expected || use_displaced_stepping (gdbarch))
+    insert_breakpoints ();
+
   if (!non_stop)
     {
       /* Pass the last stop signal to the thread we're resuming,
@@ -2133,42 +2224,6 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
   /* Reset to normal state.  */
   init_infwait_state ();
 
-  /* Stepping over a breakpoint while at the same time delivering a signal
-     has a problem: we cannot use displaced stepping, but we also cannot
-     use software single-stepping, because we do not know where execution
-     will continue if a signal handler is installed.
-
-     On the other hand, if there is a signal handler we'd have to step
-     over it anyway.  So what we do instead is to install a step-resume
-     handler at the current address right away, deliver the signal without
-     stepping, and once we arrive back at the step-resume breakpoint, step
-     once more over the original breakpoint we wanted to step over.  */
-  if (oneproc && tp->suspend.stop_signal != TARGET_SIGNAL_0
-      && execution_direction != EXEC_REVERSE)
-    {
-      insert_step_resume_breakpoint_at_frame (get_current_frame ());
-      tp->step_after_step_resume_breakpoint = 1;
-      oneproc = 0;
-    }
-
-  if (oneproc)
-    {
-      tp->control.trap_expected = 1;
-      /* If displaced stepping is enabled, we can step over the
-        breakpoint without hitting it, so leave all breakpoints
-        inserted.  Otherwise we need to disable all breakpoints, step
-        one instruction, and then re-add them when that step is
-        finished.  */
-      if (!use_displaced_stepping (gdbarch))
-       remove_breakpoints ();
-    }
-
-  /* We can insert breakpoints if we're not trying to step over one,
-     or if we are stepping over one but we're using displaced stepping
-     to do so.  */
-  if (! tp->control.trap_expected || use_displaced_stepping (gdbarch))
-    insert_breakpoints ();
-
   /* Resume inferior.  */
   resume (oneproc || step || bpstat_should_step (), tp->suspend.stop_signal);
 
@@ -2178,7 +2233,7 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
      does not support asynchronous execution.  */
   if (!target_can_async_p ())
     {
-      wait_for_inferior (0);
+      wait_for_inferior ();
       normal_stop ();
     }
 }
@@ -2209,7 +2264,7 @@ start_remote (int from_tty)
      target_open() return to the caller an indication that the target
      is currently running and GDB state should be set to the same as
      for an async run.  */
-  wait_for_inferior (0);
+  wait_for_inferior ();
 
   /* Now that the inferior has stopped, do any bookkeeping like
      loading shared libraries.  We want to do this before normal_stop,
@@ -2235,7 +2290,7 @@ init_wait_for_inferior (void)
 
   target_last_wait_ptid = minus_one_ptid;
 
-  previous_inferior_ptid = null_ptid;
+  previous_inferior_ptid = inferior_ptid;
   init_infwait_state ();
 
   /* Discard any skipped inlined frames.  */
@@ -2338,12 +2393,10 @@ infrun_thread_stop_requested_callback (struct thread_info *info, void *arg)
 
          normal_stop ();
 
-         /* Finish off the continuations.  The continations
-            themselves are responsible for realising the thread
-            didn't finish what it was supposed to do.  */
+         /* Finish off the continuations.  */
          tp = inferior_thread ();
-         do_all_intermediate_continuations_thread (tp);
-         do_all_continuations_thread (tp);
+         do_all_intermediate_continuations_thread (tp, 1);
+         do_all_continuations_thread (tp, 1);
        }
 
       do_cleanups (old_chain);
@@ -2577,18 +2630,13 @@ prepare_for_detach (void)
 
 /* Wait for control to return from inferior to debugger.
 
-   If TREAT_EXEC_AS_SIGTRAP is non-zero, then handle EXEC signals
-   as if they were SIGTRAP signals.  This can be useful during
-   the startup sequence on some targets such as HP/UX, where
-   we receive an EXEC event instead of the expected SIGTRAP.
-
    If inferior gets a signal, we may decide to start it up again
    instead of returning.  That is why there is a loop in this function.
    When this function actually returns it means the inferior
    should be left stopped and GDB should read more commands.  */
 
 void
-wait_for_inferior (int treat_exec_as_sigtrap)
+wait_for_inferior (void)
 {
   struct cleanup *old_cleanups;
   struct execution_control_state ecss;
@@ -2596,8 +2644,7 @@ wait_for_inferior (int treat_exec_as_sigtrap)
 
   if (debug_infrun)
     fprintf_unfiltered
-      (gdb_stdlog, "infrun: wait_for_inferior (treat_exec_as_sigtrap=%d)\n",
-       treat_exec_as_sigtrap);
+      (gdb_stdlog, "infrun: wait_for_inferior ()\n");
 
   old_cleanups =
     make_cleanup (delete_step_thread_step_resume_breakpoint_cleanup, NULL);
@@ -2605,9 +2652,6 @@ wait_for_inferior (int treat_exec_as_sigtrap)
   ecs = &ecss;
   memset (ecs, 0, sizeof (*ecs));
 
-  /* We'll update this if & when we switch to a new thread.  */
-  previous_inferior_ptid = inferior_ptid;
-
   while (1)
     {
       struct cleanup *old_chain;
@@ -2629,13 +2673,6 @@ wait_for_inferior (int treat_exec_as_sigtrap)
       if (debug_infrun)
        print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
 
-      if (treat_exec_as_sigtrap && ecs->ws.kind == TARGET_WAITKIND_EXECD)
-        {
-          xfree (ecs->ws.value.execd_pathname);
-          ecs->ws.kind = TARGET_WAITKIND_STOPPED;
-          ecs->ws.value.sig = TARGET_SIGNAL_TRAP;
-        }
-
       /* If an error happens while handling the event, propagate GDB's
         knowledge of the executing state to the frontend/user running
         state.  */
@@ -2678,9 +2715,6 @@ fetch_inferior_event (void *client_data)
 
   memset (ecs, 0, sizeof (*ecs));
 
-  /* We'll update this if & when we switch to a new thread.  */
-  previous_inferior_ptid = inferior_ptid;
-
   /* We're handling a live event, so make sure we're doing live
      debugging.  If we're looking at traceframes while the target is
      running, we're going to need to get back to that mode after
@@ -2705,7 +2739,19 @@ fetch_inferior_event (void *client_data)
      status mechanism.  */
 
   overlay_cache_invalid = 1;
-  registers_changed ();
+
+  /* But don't do it if the current thread is already stopped (hence
+     this is either a delayed event that will result in
+     TARGET_WAITKIND_IGNORE, or it's an event for another thread (and
+     we always clear the register and frame caches when the user
+     switches threads anyway).  If we didn't do this, a spurious
+     delayed event in all-stop mode would make the user lose the
+     selected frame.  */
+  if (non_stop || is_executing (inferior_ptid))
+    registers_changed ();
+
+  make_cleanup_restore_integer (&execution_direction);
+  execution_direction = target_execution_direction ();
 
   if (deprecated_target_wait_hook)
     ecs->ptid =
@@ -3271,6 +3317,11 @@ handle_inferior_event (struct execution_control_state *ecs)
          that the user can inspect this again later.  */
       set_internalvar_integer (lookup_internalvar ("_exitcode"),
                               (LONGEST) ecs->ws.value.integer);
+
+      /* Also record this in the inferior itself.  */
+      current_inferior ()->has_exit_code = 1;
+      current_inferior ()->exit_code = (LONGEST) ecs->ws.value.integer;
+
       gdb_flush (gdb_stdout);
       target_mourn_inferior ();
       singlestep_breakpoints_inserted_p = 0;
@@ -3632,6 +3683,8 @@ handle_inferior_event (struct execution_control_state *ecs)
              singlestep_breakpoints_inserted_p = 0;
            }
 
+         ecs->event_thread->control.trap_expected = 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 (deferred_step_ptid);
@@ -4132,7 +4185,7 @@ process_event_stop_test:
                                 "infrun: signal arrived while stepping over "
                                 "breakpoint\n");
 
-         insert_step_resume_breakpoint_at_frame (frame);
+         insert_hp_step_resume_breakpoint_at_frame (frame);
          ecs->event_thread->step_after_step_resume_breakpoint = 1;
          /* Reset trap_expected to ensure breakpoints are re-inserted.  */
          ecs->event_thread->control.trap_expected = 0;
@@ -4162,7 +4215,7 @@ process_event_stop_test:
                                 "infrun: signal may take us out of "
                                 "single-step range\n");
 
-         insert_step_resume_breakpoint_at_frame (frame);
+         insert_hp_step_resume_breakpoint_at_frame (frame);
          /* Reset trap_expected to ensure breakpoints are re-inserted.  */
          ecs->event_thread->control.trap_expected = 0;
          keep_going (ecs);
@@ -4308,6 +4361,38 @@ process_event_stop_test:
           where we are stepping and step out of the right range.  */
        break;
 
+      case BPSTAT_WHAT_STEP_RESUME:
+        if (debug_infrun)
+         fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STEP_RESUME\n");
+
+       delete_step_resume_breakpoint (ecs->event_thread);
+       if (ecs->event_thread->control.proceed_to_finish
+           && execution_direction == EXEC_REVERSE)
+         {
+           struct thread_info *tp = ecs->event_thread;
+
+           /* We are finishing a function in reverse, and just hit
+              the step-resume breakpoint at the start address of the
+              function, and we're almost there -- just need to back
+              up by one more single-step, which should take us back
+              to the function call.  */
+           tp->control.step_range_start = tp->control.step_range_end = 1;
+           keep_going (ecs);
+           return;
+         }
+       if (stop_pc == ecs->stop_func_start
+           && execution_direction == EXEC_REVERSE)
+         {
+           /* We are stepping over a function call in reverse, and
+              just hit the step-resume breakpoint at the start
+              address of the function.  Go back to single-stepping,
+              which should take us back to the function call.  */
+           ecs->event_thread->stepping_over_breakpoint = 1;
+           keep_going (ecs);
+           return;
+         }
+       break;
+
       case BPSTAT_WHAT_STOP_NOISY:
         if (debug_infrun)
          fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STOP_NOISY\n");
@@ -4330,9 +4415,9 @@ process_event_stop_test:
        stop_stepping (ecs);
        return;
 
-      case BPSTAT_WHAT_STEP_RESUME:
+      case BPSTAT_WHAT_HP_STEP_RESUME:
         if (debug_infrun)
-         fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_STEP_RESUME\n");
+         fprintf_unfiltered (gdb_stdlog, "infrun: BPSTAT_WHAT_HP_STEP_RESUME\n");
 
        delete_step_resume_breakpoint (ecs->event_thread);
        if (ecs->event_thread->step_after_step_resume_breakpoint)
@@ -4345,17 +4430,6 @@ process_event_stop_test:
            keep_going (ecs);
            return;
          }
-       if (stop_pc == ecs->stop_func_start
-           && execution_direction == EXEC_REVERSE)
-         {
-           /* We are stepping over a function call in reverse, and
-              just hit the step-resume breakpoint at the start
-              address of the function.  Go back to single-stepping,
-              which should take us back to the function call.  */
-           ecs->event_thread->stepping_over_breakpoint = 1;
-           keep_going (ecs);
-           return;
-         }
        break;
 
       case BPSTAT_WHAT_KEEP_CHECKING:
@@ -5167,14 +5241,16 @@ handle_step_into_function_backward (struct gdbarch *gdbarch,
    This is used to both functions and to skip over code.  */
 
 static void
-insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
-                                     struct symtab_and_line sr_sal,
-                                     struct frame_id sr_id)
+insert_step_resume_breakpoint_at_sal_1 (struct gdbarch *gdbarch,
+                                       struct symtab_and_line sr_sal,
+                                       struct frame_id sr_id,
+                                       enum bptype sr_type)
 {
   /* There should never be more than one step-resume or longjmp-resume
      breakpoint per thread, so we should never be setting a new
      step_resume_breakpoint when one is already active.  */
   gdb_assert (inferior_thread ()->control.step_resume_breakpoint == NULL);
+  gdb_assert (sr_type == bp_step_resume || sr_type == bp_hp_step_resume);
 
   if (debug_infrun)
     fprintf_unfiltered (gdb_stdlog,
@@ -5182,18 +5258,28 @@ insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
                        paddress (gdbarch, sr_sal.pc));
 
   inferior_thread ()->control.step_resume_breakpoint
-    = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, bp_step_resume);
+    = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, sr_type);
+}
+
+void
+insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
+                                     struct symtab_and_line sr_sal,
+                                     struct frame_id sr_id)
+{
+  insert_step_resume_breakpoint_at_sal_1 (gdbarch,
+                                         sr_sal, sr_id,
+                                         bp_step_resume);
 }
 
-/* Insert a "step-resume breakpoint" at RETURN_FRAME.pc.  This is used
-   to skip a potential signal handler.
+/* Insert a "high-priority step-resume breakpoint" at RETURN_FRAME.pc.
+   This is used to skip a potential signal handler.
 
    This is called with the interrupted function's frame.  The signal
    handler, when it returns, will resume the interrupted function at
    RETURN_FRAME.pc.  */
 
 static void
-insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
+insert_hp_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
 {
   struct symtab_and_line sr_sal;
   struct gdbarch *gdbarch;
@@ -5206,14 +5292,14 @@ insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
   sr_sal.section = find_pc_overlay (sr_sal.pc);
   sr_sal.pspace = get_frame_program_space (return_frame);
 
-  insert_step_resume_breakpoint_at_sal (gdbarch, sr_sal,
-                                       get_stack_frame_id (return_frame));
+  insert_step_resume_breakpoint_at_sal_1 (gdbarch, sr_sal,
+                                         get_stack_frame_id (return_frame),
+                                         bp_hp_step_resume);
 }
 
-/* Similar to insert_step_resume_breakpoint_at_frame, except
-   but a breakpoint at the previous frame's PC.  This is used to
-   skip a function after stepping into it (for "next" or if the called
-   function has no debugging information).
+/* Insert a "step-resume breakpoint" at the previous frame's PC.  This
+   is used to skip a function after stepping into it (for "next" or if
+   the called function has no debugging information).
 
    The current function has almost always been reached by single
    stepping a call or return instruction.  NEXT_FRAME belongs to the
@@ -5221,7 +5307,7 @@ insert_step_resume_breakpoint_at_frame (struct frame_info *return_frame)
    resume address.
 
    This is a separate function rather than reusing
-   insert_step_resume_breakpoint_at_frame in order to avoid
+   insert_hp_step_resume_breakpoint_at_frame in order to avoid
    get_prev_frame, which may stop prematurely (see the implementation
    of frame_unwind_caller_id for an example).  */
 
@@ -5799,7 +5885,8 @@ normal_stop (void)
 
   /* Save the function value return registers, if we care.
      We might be about to restore their previous contents.  */
-  if (inferior_thread ()->control.proceed_to_finish)
+  if (inferior_thread ()->control.proceed_to_finish
+      && execution_direction != EXEC_REVERSE)
     {
       /* This should not be necessary.  */
       if (stop_registers)
@@ -6807,7 +6894,7 @@ save_inferior_ptid (void)
    Set exec-direction / show exec-direction commands
    (returns error unless target implements to_set_exec_direction method).  */
 
-enum exec_direction_kind execution_direction = EXEC_FORWARD;
+int execution_direction = EXEC_FORWARD;
 static const char exec_forward[] = "forward";
 static const char exec_reverse[] = "reverse";
 static const char *exec_direction = exec_forward;
@@ -6846,12 +6933,10 @@ show_exec_direction_func (struct ui_file *out, int from_tty,
   case EXEC_REVERSE:
     fprintf_filtered (out, _("Reverse.\n"));
     break;
-  case EXEC_ERROR:
   default:
-    fprintf_filtered (out, _("Forward (target `%s' does not "
-                            "support exec-direction).\n"),
-                     target_shortname);
-    break;
+    internal_error (__FILE__, __LINE__,
+                   _("bogus execution_direction value: %d"),
+                   (int) execution_direction);
   }
 }
 
This page took 0.033128 seconds and 4 git commands to generate.