]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Reverse execution branch
authorMichael Snyder <msnyder@vmware.com>
Tue, 10 Jun 2008 02:11:18 +0000 (02:11 +0000)
committerMichael Snyder <msnyder@vmware.com>
Tue, 10 Jun 2008 02:11:18 +0000 (02:11 +0000)
2008-06-09  Michael Snyder  <msnyder@specifix.com>
2006-05-02  Michael Snyder  <msnyder@redhat.com>

* Target interface for reverse execution.
* target.h (enum target_waitkind):
Add new wait event, TARGET_WAITKIND_NO_HISTORY.
(enum exec_direction_kind): New enum.
(struct target_ops): New methods to_set_execdir, to_get_execdir.
* target.c (target_get_execdir): New generic method.
(target_set_execdir): Ditto.
* remote.c (remote_get_execdir, remote_set_execdir): New methods.
(remote_vcont_resume): Jump out if attempting reverse execution.
(remote_resume): Check for reverse exec direction, and send
appropriate command to target.
(remote_wait): Check target response for NO_HISTORY status.
Also check for empty reply (target doesn't understand "bs" or "bc).
(_initialize_remote): Add new methods to remote target vector.

* breakpoint.h (breakpoint_silence): Export.
* breakpoint.c (breakpoint_silence): New function.
* infcmd.c (finish_command): Check for reverse exec direction.
(finish_backward): New function, handle finish cmd in reverse.
* infrun.c (enum inferior_stop_reason): Add NO_HISTORY reason.
(handle_inferior_event): Handle TARGET_WAITKIND_NO_HISTORY.
Handle stepping over a function call in reverse.
Handle stepping thru a line range in reverse.
Handle setting a step-resume breakpoint in reverse.
Handle stepping into a function in reverse.
Handle stepping between line ranges in reverse.
(print_stop_reason): Print reason for NO_HISTORY.

2006-03-31  Michael Snyder  <msnyder@redhat.com>
User interface for reverse execution.
* Makefile.in (reverse.c): New file.
* reverse.c: New file.  User interface for reverse execution.

gdb/ChangeLog
gdb/Makefile.in
gdb/breakpoint.c
gdb/breakpoint.h
gdb/doc/gdb.texinfo
gdb/infcmd.c
gdb/infrun.c
gdb/remote.c
gdb/reverse.c [new file with mode: 0644]
gdb/target.c
gdb/target.h

index 71e9df5f9e1e082f3b4a94e9f07ac8ddc325dce2..3deec46401bd9a3153acd683f4ed3a23fbc361b1 100644 (file)
@@ -1,3 +1,39 @@
+2008-06-09  Michael Snyder  <msnyder@specifix.com>
+       2006-05-02  Michael Snyder  <msnyder@redhat.com>
+
+       * Target interface for reverse execution.
+       * target.h (enum target_waitkind): 
+       Add new wait event, TARGET_WAITKIND_NO_HISTORY.
+       (enum exec_direction_kind): New enum.
+       (struct target_ops): New methods to_set_execdir, to_get_execdir.
+       * target.c (target_get_execdir): New generic method.
+       (target_set_execdir): Ditto.
+       * remote.c (remote_get_execdir, remote_set_execdir): New methods.
+       (remote_vcont_resume): Jump out if attempting reverse execution.
+       (remote_resume): Check for reverse exec direction, and send
+       appropriate command to target.
+       (remote_wait): Check target response for NO_HISTORY status.
+       Also check for empty reply (target doesn't understand "bs" or "bc).
+       (_initialize_remote): Add new methods to remote target vector.
+
+       * breakpoint.h (breakpoint_silence): Export.
+       * breakpoint.c (breakpoint_silence): New function.
+       * infcmd.c (finish_command): Check for reverse exec direction.
+       (finish_backward): New function, handle finish cmd in reverse.
+       * infrun.c (enum inferior_stop_reason): Add NO_HISTORY reason.
+       (handle_inferior_event): Handle TARGET_WAITKIND_NO_HISTORY.
+       Handle stepping over a function call in reverse.
+       Handle stepping thru a line range in reverse.
+       Handle setting a step-resume breakpoint in reverse.
+       Handle stepping into a function in reverse.
+       Handle stepping between line ranges in reverse.
+       (print_stop_reason): Print reason for NO_HISTORY.
+
+       2006-03-31  Michael Snyder  <msnyder@redhat.com>
+       User interface for reverse execution.
+       * Makefile.in (reverse.c): New file.
+       * reverse.c: New file.  User interface for reverse execution.
+
 2008-06-09  Doug Evans  <dje@google.com>
 
        * remote.c (remote_wait): Include beginning of malformed packet
index c14dc1ab4accbe8d2fccfadee6f8c5e2bb063dbd..891b31aae099cc03e74521e2c7bbc21ebe0629fe 100644 (file)
@@ -625,7 +625,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c  \
        objfiles.c osabi.c observer.c \
        p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.c \
        prologue-value.c \
-       regcache.c reggroups.c remote.c remote-fileio.c \
+       regcache.c reggroups.c remote.c remote-fileio.c reverse.c \
        scm-exp.c scm-lang.c scm-valprint.c \
        sentinel-frame.c \
        serial.c ser-base.c ser-unix.c \
@@ -1064,7 +1064,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
        std-regs.o \
        signals.o \
        gdb-events.o \
-       exec.o bcache.o objfiles.o observer.o minsyms.o maint.o demangle.o \
+       exec.o reverse.o \
+       bcache.o objfiles.o observer.o minsyms.o maint.o demangle.o \
        dbxread.o coffread.o coff-pe-read.o \
        dwarf2read.o mipsread.o stabsread.o corefile.o \
        dwarf2expr.o dwarf2loc.o dwarf2-frame.o \
@@ -2685,6 +2686,8 @@ remote-sim.o: remote-sim.c $(defs_h) $(inferior_h) $(value_h) \
        $(gdb_callback_h) $(gdb_remote_sim_h) $(command_h) \
        $(regcache_h) $(gdb_assert_h) $(sim_regno_h) $(arch_utils_h) \
        $(readline_h)
+reverse.o: reverse.c $(defs_h) $(gdb_string_h) $(target_h) $(cli_cmds_h) \
+       $(cli_decode_h) $(top_h)
 rs6000-nat.o: rs6000-nat.c $(defs_h) $(inferior_h) $(target_h) $(gdbcore_h) \
        $(xcoffsolib_h) $(symfile_h) $(objfiles_h) $(libbfd_h) $(bfd_h) \
        $(exceptions_h) $(gdb_stabs_h) $(regcache_h) $(arch_utils_h) \
index 051b753d6392fe6640d58fb13b5bfbc7da2f4899..809ef26a0602168cd8d395ec91a0b48bb9ea5583 100644 (file)
@@ -7650,6 +7650,13 @@ breakpoint_clear_ignore_counts (void)
     b->ignore_count = 0;
 }
 
+void
+breakpoint_silence (struct breakpoint *b)
+{
+  /* Silence the breakpoint.  */
+  b->silent = 1;
+}
+
 /* Command to set ignore-count of breakpoint N to COUNT.  */
 
 static void
index 80544e4dedd698afc4a19cade6d5425f82851879..8c45999d4941006a08addef2daf0260659c48ddd 100644 (file)
@@ -865,4 +865,7 @@ void breakpoint_restore_shadows (gdb_byte *buf, ULONGEST memaddr,
 
 extern int breakpoints_always_inserted_mode (void);
 
+/* Tell a breakpoint to be quiet.  */
+extern void breakpoint_silence (struct breakpoint *);
+
 #endif /* !defined (BREAKPOINT_H) */
index 5cef05b5efb9b9e75757a3de84621ac4e33ecfe9..757a8884359221dd25f14d2884362fe1594a13d1 100644 (file)
@@ -143,6 +143,7 @@ software in general.  We will miss him.
 * Commands::                    @value{GDBN} commands
 * Running::                     Running programs under @value{GDBN}
 * Stopping::                    Stopping and continuing
+* Reverse Execution::           Running programs backward
 * Stack::                       Examining the stack
 * Source::                      Examining source files
 * Data::                        Examining data
@@ -4534,6 +4535,109 @@ Display the current scheduler locking mode.
 @end table
 
 
+@node Reverse Execution
+@chapter Running programs backward
+
+When you are debugging a program, it is not unusual to realize that
+you have gone too far, and some event of interest has already happened.
+If the target environment supports it, @value{GDBN} can allow you to
+``rewind'' the program by running it backward.
+
+A target environment that supports reverse execution should be able
+to ``undo'' the changes in machine state that have taken place as the
+program was executing normally.  Variables, registers etc. should
+revert to their previous values.  Obviously this requires a great
+deal of sophistication on the part of the target environment; not
+all target environments can support reverse execution.
+
+When a program is executed in reverse, the instructions that
+have most recently been executed are ``un-executed'', in reverse
+order.  The program counter runs backward, following the previous
+thread of execution in reverse.  As each instruction is ``un-executed'',
+the values of memory and/or registers that were changed by that
+instruction are reverted to their previous states.  After executing
+a piece of source code in reverse, all side effects of that code
+should be ``undone'', and all variables should be returned to their
+prior values.
+
+Assuming you are debugging in a target environment that supports
+reverse execution, @value{GDBN} provides the following commands.
+
+@table @code
+@kindex reverse-continue
+@kindex rc @r{(@code{reverse-continue})}
+@item reverse-continue @r{[}@var{ignore-count}@r{]}
+@itemx rc @r{[}@var{ignore-count}@r{]}
+Beginning at the point where your program last stopped, start executing
+in reverse.  Reverse execution will stop for breakpoints and synchronous
+exceptions (signals), just like normal execution.  Behavior of
+asynchronous signals depends on the target environment.
+
+@kindex reverse-step
+@kindex rs @r{(@code{step})}
+@item reverse-step @r{[}@var{count}@r{]}
+Run the program backward until control reaches the start of a
+different source line; then stop it, and return control to @value{GDBN}.
+
+Like the @code{step} command, @code{reverse-step} will only stop
+at the beginning of a source line.  It ``un-executes'' the previously
+executed source line.  If the previous source line included calls to
+debuggable functions, @code{reverse-step} will step (backward) into
+the called function, stopping at the beginning of the @emph{last}
+statement in the called function (typically a return statement).
+
+Also, as with the @code{step} command, if non-debuggable functions are
+called, @code{reverse-step} will run thru them backward without stopping.
+
+@kindex reverse-stepi
+@kindex rsi @r{(@code{reverse-stepi})}
+@item reverse-stepi @r{[}@var{count}@r{]}
+Reverse-execute one machine instruction.  Note that the instruction
+to be reverse-executed is @emph{not} the one pointed to by the program
+counter, but the instruction executed prior to that one.  For instance,
+if the last instruction was a jump, @code{reverse-stepi} will take you
+back from the destination of the jump to the jump instruction itself.
+
+@kindex reverse-next
+@kindex rn @r{(@code{reverse-next})}
+@item reverse-next @r{[}@var{count}@r{]}
+Run backward to the beginning of the previous line executed in
+the current (innermost) stack frame.  If the line contains function
+calls, they will be ``un-executed'' without stopping.  Starting from
+the first line of a function, @code{reverse-next} will take you back
+to the caller of that function, @emph{before} the function was called.
+
+@kindex reverse-nexti
+@kindex rni @r{(@code{reverse-nexti})}
+@item reverse-nexti @r{[}@var{count}@r{]}
+Like @code{nexti}, @code{reverse-nexti} executes a single instruction
+in reverse, except that called functions are ``un-executed'' atomically.
+That is, if the previously executed instruction was a return from
+another instruction, @code{reverse-nexti} will continue to execute
+in reverse until the call to that function (from the current stack
+frame) is reached.
+
+@kindex reverse-finish
+@item reverse-finish
+Just as the @code{finish} command takes you to the point where the
+current function returns, @code{reverse-finish} takes you to the point
+where it was called.  Instead of ending up at the end of the current
+function invocation, you end up at the beginning.
+
+@item set exec-direction
+Set the direction of target execution.
+@itemx set exec-direction reverse
+@cindex execute forward or backward in time
+@value{GDBN} will perform all execution commands in reverse, until the
+exec-direction mode is changed to ``forward''.  Affected commands include
+@code{step, stepi, next, nexti, continue, and finish}.  The @code{return}
+command cannot be used in reverse mode.
+@item set exec-direction forward
+@value{GDBN} will perform all execution commands in the normal fashion.
+This is the default.
+@end table
+
+
 @node Stack
 @chapter Examining the Stack
 
index 2397c30a09e12492b678d528039273652ad6f004..9fcf8ceebaf85f866b56cc699934973f2f42c072 100644 (file)
@@ -1303,6 +1303,8 @@ finish_command_continuation (struct continuation_arg *arg, int error_p)
 /* "finish": Set a temporary breakpoint at the place the selected
    frame will return to, then continue.  */
 
+static void finish_backwards (struct symbol *);
+
 static void
 finish_command (char *arg, int from_tty)
 {
@@ -1343,13 +1345,6 @@ finish_command (char *arg, int from_tty)
 
   clear_proceed_status ();
 
-  sal = find_pc_line (get_frame_pc (frame), 0);
-  sal.pc = get_frame_pc (frame);
-
-  breakpoint = set_momentary_breakpoint (sal, get_frame_id (frame), bp_finish);
-
-  old_chain = make_cleanup_delete_breakpoint (breakpoint);
-
   /* Find the function we will return from.  */
 
   function = find_pc_function (get_frame_pc (get_selected_frame (NULL)));
@@ -1358,10 +1353,28 @@ finish_command (char *arg, int from_tty)
      source.  */
   if (from_tty)
     {
-      printf_filtered (_("Run till exit from "));
+      if (target_get_execution_direction () == EXEC_REVERSE)
+       printf_filtered ("Run back to call of ");
+      else
+       printf_filtered ("Run till exit from ");
+
       print_stack_frame (get_selected_frame (NULL), 1, LOCATION);
     }
 
+  if (target_get_execution_direction () == EXEC_REVERSE)
+    {
+      /* Split off at this point.  */
+      finish_backwards (function);
+      return;
+    }
+
+  sal = find_pc_line (get_frame_pc (frame), 0);
+  sal.pc = get_frame_pc (frame);
+
+  breakpoint = set_momentary_breakpoint (sal, get_frame_id (frame), bp_finish);
+
+  old_chain = make_cleanup_delete_breakpoint (breakpoint);
+
   proceed_to_finish = 1;       /* We want stop_registers, please...  */
   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
 
@@ -1435,6 +1448,66 @@ It stopped at a breakpoint that has since been deleted.\n"));
 Type \"info stack\" or \"info registers\" for more information.\n"));
     }
 }
+
+static void
+finish_backwards (struct symbol *function)
+{
+  struct symtab_and_line sal;
+  struct breakpoint *breakpoint;
+  struct cleanup *old_chain;
+  CORE_ADDR func_addr;
+  int back_up;
+
+  if (find_pc_partial_function (get_frame_pc (get_current_frame ()),
+                               NULL, &func_addr, NULL) == 0)
+    internal_error (__FILE__, __LINE__,
+                   "Finish: couldn't find function.");
+
+  sal = find_pc_line (func_addr, 0);
+
+  /* Let's cheat and not worry about async until later.  */
+
+  /* We don't need a return value.  */
+  proceed_to_finish = 0;
+  /* Special case: if we're sitting at the function entry point,
+     then all we need to do is take a reverse singlestep.  We
+     don't need to set a breakpoint, and indeed it would do us
+     no good to do so.
+
+     Note that this can only happen at frame #0, since there's
+     no way that a function up the stack can have a return address
+     that's equal to its entry point.  */
+
+  if (sal.pc != read_pc ())
+    {
+      /* Set breakpoint and continue.  */
+      breakpoint =
+       set_momentary_breakpoint (sal,
+                                 get_frame_id (get_selected_frame (NULL)),
+                                 bp_breakpoint);
+      /* Tell the breakpoint to keep quiet.  We won't be done
+         until we've done another reverse single-step.  */
+      breakpoint_silence (breakpoint);
+      old_chain = make_cleanup_delete_breakpoint (breakpoint);
+      proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+      /* We will be stopped when proceed returns.  */
+      do_cleanups (old_chain);
+      back_up = bpstat_find_breakpoint (stop_bpstat, breakpoint) != NULL;
+    }
+  else
+    back_up = 1;
+  if (back_up)
+    {
+      /* If in fact we hit the step-resume breakpoint (and not
+        some other breakpoint), then we're almost there --
+        we just need to back up by one more single-step.  */
+      /* (Kludgy way of letting wait_for_inferior know...) */
+      step_range_start = step_range_end = 1;
+      proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
+    }
+  return;
+}
+
 \f
 static void
 environment_info (char *var, int from_tty)
index 2960acb2a8f216732229ddf748730114578bf01e..e7d1decc65e6763c8695a03da5330c265f5fa0d4 100644 (file)
@@ -1360,7 +1360,9 @@ enum inferior_stop_reason
   /* Inferior exited. */
   EXITED,
   /* Inferior received signal, and user asked to be notified. */
-  SIGNAL_RECEIVED
+  SIGNAL_RECEIVED,
+  /* Reverse execution -- target ran out of history info.  */
+  NO_HISTORY
 };
 
 /* This structure contains what used to be local variables in
@@ -1980,6 +1982,12 @@ handle_inferior_event (struct execution_control_state *ecs)
       stop_signal = ecs->ws.value.sig;
       break;
 
+    case TARGET_WAITKIND_NO_HISTORY:
+      /* Reverse execution: target ran out of history info.  */
+      print_stop_reason (NO_HISTORY, 0);
+      stop_stepping (ecs);
+      return;
+
       /* We had an event in the inferior, but we are not interested
          in handling it at this level. The lower layers have already
          done what needs to be done, if anything.
@@ -2687,6 +2695,17 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
            keep_going (ecs);
            return;
          }
+       if (stop_pc == ecs->stop_func_start &&
+           target_get_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->stepping_over_breakpoint = 1;
+           keep_going (ecs);
+           return;
+         }
        break;
 
       case BPSTAT_WHAT_CHECK_SHLIBS:
@@ -2852,7 +2871,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
         fprintf_unfiltered (gdb_stdlog, "infrun: stepping inside range [0x%s-0x%s]\n",
                            paddr_nz (step_range_start),
                            paddr_nz (step_range_end));
-      keep_going (ecs);
+
+      /* When stepping backward, stop at beginning of line range
+        (unles it's the function entry point, in which case
+        keep going back to the call point).  */
+      if (stop_pc == step_range_start &&
+         stop_pc != ecs->stop_func_start &&
+         target_get_execution_direction () == EXEC_REVERSE)
+       {
+         stop_step = 1;
+         print_stop_reason (END_STEPPING_RANGE, 0);
+         stop_stepping (ecs);
+       }
+      else
+       {
+         keep_going (ecs);
+       }
       return;
     }
 
@@ -2941,10 +2975,31 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
 
       if (step_over_calls == STEP_OVER_ALL)
        {
-         /* We're doing a "next", set a breakpoint at callee's return
-            address (the address at which the caller will
-            resume).  */
-         insert_step_resume_breakpoint_at_caller (get_current_frame ());
+         /* We're doing a "next".
+
+            Normal (forward) execution: set a breakpoint at the
+            callee's return address (the address at which the caller
+            will resume).
+
+            Reverse (backward) execution.  set the step-resume
+            breakpoint at the start of the function that we just
+            stepped into (backwards), and continue to there.  When we
+            get there, we'll need to single-step back to the
+            caller.  */
+
+         if (target_get_execution_direction () == EXEC_REVERSE)
+           {
+             /* FIXME: I'm not sure if we've handled the frame for
+                recursion.  */
+
+             struct symtab_and_line sr_sal;
+             init_sal (&sr_sal);
+             sr_sal.pc = ecs->stop_func_start;
+             insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
+           }
+         else
+           insert_step_resume_breakpoint_at_caller (get_current_frame ());
+
          keep_going (ecs);
          return;
        }
@@ -3006,9 +3061,22 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
          return;
        }
 
-      /* Set a breakpoint at callee's return address (the address at
-         which the caller will resume).  */
-      insert_step_resume_breakpoint_at_caller (get_current_frame ());
+      if (target_get_execution_direction () == EXEC_REVERSE)
+       {
+         /* Set a breakpoint at callee's start address.
+            From there we can step once and be back in the caller.  */
+         /* FIXME: I'm not sure we've handled the frame for recursion.  */
+         struct symtab_and_line sr_sal;
+         init_sal (&sr_sal);
+         sr_sal.pc = ecs->stop_func_start;
+         insert_step_resume_breakpoint_at_sal (sr_sal, null_frame_id);
+       }
+      else
+       {
+         /* Set a breakpoint at callee's return address (the address
+            at which the caller will resume).  */
+         insert_step_resume_breakpoint_at_caller (get_current_frame ());
+       }
       keep_going (ecs);
       return;
     }
@@ -3201,6 +3269,28 @@ step_into_function (struct execution_control_state *ecs)
     ecs->stop_func_start = gdbarch_skip_prologue
                             (current_gdbarch, ecs->stop_func_start);
 
+  if (target_get_execution_direction () == EXEC_REVERSE)
+    {
+      ecs->sal = find_pc_line (stop_pc, 0);
+
+      /* OK, we're just gonna keep stepping here.  */
+      if (ecs->sal.pc == stop_pc)
+       {
+         /* We're there already.  Just stop stepping now.  */
+         stop_step = 1;
+         print_stop_reason (END_STEPPING_RANGE, 0);
+         stop_stepping (ecs);
+         return;
+       }
+      /* Else just reset the step range and keep going.
+        No step-resume breakpoint, they don't work for
+        epilogues, which can have multiple entry paths.  */
+      step_range_start = ecs->sal.pc;
+      step_range_end   = ecs->sal.end;
+      keep_going (ecs);
+      return;
+    }
+  /* else... */
   ecs->sal = find_pc_line (ecs->stop_func_start, 0);
   /* Use the step_resume_break to step until the end of the prologue,
      even if that involves jumps (as it seems to on the vax under
@@ -3566,6 +3656,10 @@ print_stop_reason (enum inferior_stop_reason stop_reason, int stop_info)
       annotate_signal_string_end ();
       ui_out_text (uiout, ".\n");
       break;
+    case NO_HISTORY:
+      /* Reverse execution: target ran out of history info.  */
+      ui_out_text (uiout, "\nNo more reverse-execution history.\n");
+      break;
     default:
       internal_error (__FILE__, __LINE__,
                      _("print_stop_reason: unrecognized enum value"));
index 3f7d07b831d5dc2aff136a1431b178ce5eb52f72..24deaa09da724e1e192e453724a357fc51c77f61 100644 (file)
@@ -3127,7 +3127,15 @@ remote_resume (ptid_t ptid, int step, enum target_signal siggnal)
     set_thread (pid, 0);       /* Run this thread.  */
 
   buf = rs->buf;
-  if (siggnal != TARGET_SIGNAL_0)
+  if (target_get_execution_direction () == EXEC_REVERSE)
+    {
+      /* We don't pass signals to the target in reverse exec mode.  */
+      if (info_verbose && siggnal != TARGET_SIGNAL_0)
+       warning (" - Can't pass signal %d to target in reverse: ignored.\n",
+                siggnal);
+      strcpy (buf, step ? "bs" : "bc");
+    }
+  else if (siggnal != TARGET_SIGNAL_0)
     {
       buf[0] = step ? 'S' : 'C';
       buf[1] = tohex (((int) siggnal >> 4) & 0xf);
@@ -3402,8 +3410,15 @@ remote_wait (ptid_t ptid, struct target_waitstatus *status)
          /* We're out of sync with the target now.  Did it continue or not?
             Not is more likely, so report a stop.  */
          warning (_("Remote failure reply: %s"), buf);
-         status->kind = TARGET_WAITKIND_STOPPED;
-         status->value.sig = TARGET_SIGNAL_0;
+         if (buf[1] == '0' && buf[2] == '6')
+           {
+             status->kind = TARGET_WAITKIND_NO_HISTORY;
+           }
+         else
+           {
+             status->kind = TARGET_WAITKIND_STOPPED;
+             status->value.sig = TARGET_SIGNAL_0;
+           }
          goto got_status;
        case 'F':               /* File-I/O request.  */
          remote_fileio_request (buf);
@@ -7024,6 +7039,35 @@ remote_command (char *args, int from_tty)
   help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
 }
 
+/* Reverse execution.
+   FIXME: set up as a capability.  */
+static enum exec_direction_kind remote_execdir = EXEC_FORWARD;
+
+static enum exec_direction_kind remote_get_execdir (void)
+{
+  if (remote_debug && info_verbose)
+    printf_filtered ("remote execdir is %s\n",
+                    remote_execdir == EXEC_FORWARD ? "forward" :
+                    remote_execdir == EXEC_REVERSE ? "reverse" :
+                    "unknown");
+  return remote_execdir;
+}
+
+static int remote_set_execdir (enum exec_direction_kind dir)
+{
+  if (remote_debug && info_verbose)
+    printf_filtered ("Set remote execdir: %s\n",
+                    dir == EXEC_FORWARD ? "forward" :
+                    dir == EXEC_REVERSE ? "reverse" :
+                    "bad direction");
+
+  /* FIXME: check target for capability.  */
+  if (dir == EXEC_FORWARD || dir == EXEC_REVERSE)
+    return (remote_execdir = dir);
+  else
+    return EXEC_ERROR;
+}
+
 static void
 init_remote_ops (void)
 {
@@ -7072,6 +7116,8 @@ Specify the serial device it is connected to\n\
   remote_ops.to_has_registers = 1;
   remote_ops.to_has_execution = 1;
   remote_ops.to_has_thread_control = tc_schedlock;     /* can lock scheduler */
+  remote_ops.to_get_execdir = remote_get_execdir;
+  remote_ops.to_set_execdir = remote_set_execdir;
   remote_ops.to_magic = OPS_MAGIC;
   remote_ops.to_memory_map = remote_memory_map;
   remote_ops.to_flash_erase = remote_flash_erase;
diff --git a/gdb/reverse.c b/gdb/reverse.c
new file mode 100644 (file)
index 0000000..29f7320
--- /dev/null
@@ -0,0 +1,197 @@
+/* Reverse execution and reverse debugging.
+
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+#include "defs.h"
+#include "gdb_string.h"
+#include "target.h"
+#include "top.h"
+#include "cli/cli-cmds.h"
+#include "cli/cli-decode.h"
+
+/* User interface for reverse debugging:
+   Set exec-direction / show exec-direction commands
+   (returns error unles target implements to_set_execdir method).  */
+
+static const char exec_forward[] = "forward";
+static const char exec_reverse[] = "reverse";
+static const char *exec_direction = exec_forward;
+static const char *exec_direction_names[] = {
+  exec_forward,
+  exec_reverse,
+  NULL
+};
+
+static void
+set_exec_direction_func (char *args, int from_tty,
+                        struct cmd_list_element *cmd)
+{
+  if (target_get_execution_direction () != EXEC_ERROR)
+    {
+      enum exec_direction_kind dir = EXEC_ERROR;
+
+      if (!strcmp (exec_direction, exec_forward))
+       dir = EXEC_FORWARD;
+      else if (!strcmp (exec_direction, exec_reverse))
+       dir = EXEC_REVERSE;
+
+      if (target_set_execution_direction (dir) != EXEC_ERROR)
+       return;
+    }
+  error (_("Target `%s' does not support execution-direction."),
+        target_shortname);
+}
+
+static void
+show_exec_direction_func (struct ui_file *out, int from_tty,
+                         struct cmd_list_element *cmd, const char *value)
+{
+  enum exec_direction_kind dir = target_get_execution_direction ();
+
+  switch (dir) {
+  case EXEC_FORWARD:
+    fprintf_filtered (out, "Forward.\n");
+    break;
+  case EXEC_REVERSE:
+    fprintf_filtered (out, "Reverse.\n");
+    break;
+  case EXEC_ERROR:
+  default:
+    error (_("Target `%s' does not support execution-direction."),
+          target_shortname);
+    break;
+  }
+}
+
+/* User interface:
+   reverse-step, reverse-next etc.
+   (returns error unles target implements to_set_execdir method).  */
+
+static void execdir_default (void *notused)
+{
+  /* Return execution direction to default state.  */
+  target_set_execution_direction (EXEC_FORWARD);
+}
+
+static void
+exec_reverse_once (char *cmd, char *args, int from_tty)
+{
+  /* String buffer for command consing.  */
+  char reverse_command[512];
+  enum exec_direction_kind dir = target_get_execution_direction ();
+
+  if (dir == EXEC_ERROR)
+    error (_("Target %s does not support this command."), target_shortname);
+
+  if (dir == EXEC_REVERSE)
+    error (_("Already in reverse mode.  Use '%s' or 'set exec-dir forward'."),
+          cmd);
+
+  if (target_set_execution_direction (EXEC_REVERSE) == EXEC_ERROR)
+    error (_("Target %s does not support this command."), target_shortname);
+
+  make_cleanup (execdir_default, NULL);
+  sprintf (reverse_command, "%s %s", cmd, args ? args : "");
+  execute_command (reverse_command, from_tty);
+}
+
+static void
+reverse_step (char *args, int from_tty)
+{
+  exec_reverse_once ("step", args, from_tty);
+}
+
+static void
+reverse_stepi (char *args, int from_tty)
+{
+  exec_reverse_once ("stepi", args, from_tty);
+}
+
+static void
+reverse_next (char *args, int from_tty)
+{
+  exec_reverse_once ("next", args, from_tty);
+}
+
+static void
+reverse_nexti (char *args, int from_tty)
+{
+  exec_reverse_once ("nexti", args, from_tty);
+}
+
+static void
+reverse_continue (char *args, int from_tty)
+{
+  exec_reverse_once ("continue", args, from_tty);
+}
+
+static void
+reverse_finish (char *args, int from_tty)
+{
+  exec_reverse_once ("finish", args, from_tty);
+}
+
+void
+_initialize_reverse (void)
+{
+  add_setshow_enum_cmd ("exec-direction", class_run, exec_direction_names,
+                       &exec_direction, "Set direction of execution.\n\
+Options are 'forward' or 'reverse'.",
+                       "Show direction of execution (forward/reverse).",
+                       "Tells gdb whether to execute forward or backward.",
+                       set_exec_direction_func, show_exec_direction_func,
+                       &setlist, &showlist);
+
+  add_com ("reverse-step", class_run, reverse_step, _("\
+Step program backward until it reaches the beginning of another source line.\n\
+Argument N means do this N times (or till program stops for another reason).")
+          );
+  add_com_alias ("rs", "reverse-step", class_alias, 1);
+
+  add_com ("reverse-next", class_run, reverse_next, _("\
+Step program backward, proceeding through subroutine calls.\n\
+Like the \"reverse-step\" command as long as subroutine calls do not happen;\n\
+when they do, the call is treated as one instruction.\n\
+Argument N means do this N times (or till program stops for another reason).")
+          );
+  add_com_alias ("rn", "reverse-next", class_alias, 1);
+
+  add_com ("reverse-stepi", class_run, reverse_stepi, _("\
+Step backward exactly one instruction.\n\
+Argument N means do this N times (or till program stops for another reason).")
+          );
+  add_com_alias ("rsi", "reverse-stepi", class_alias, 0);
+
+  add_com ("reverse-nexti", class_run, reverse_nexti, _("\
+Step backward one instruction, but proceed through called subroutines.\n\
+Argument N means do this N times (or till program stops for another reason).")
+          );
+  add_com_alias ("rni", "reverse-nexti", class_alias, 0);
+
+  add_com ("reverse-continue", class_run, reverse_continue, _("\
+Continue program being debugged, running in reverse.\n\
+If proceeding from breakpoint, a number N may be used as an argument,\n\
+which means to set the ignore count of that breakpoint to N - 1 (so that\n\
+the breakpoint won't break until the Nth time it is reached)."));
+  add_com_alias ("rc", "reverse-continue", class_alias, 0);
+
+  add_com ("reverse-finish", class_run, reverse_finish, _("\
+Execute backward until just before selected stack frame is called."));
+}
index be7f3a11e229ee33fde5227de9f3e7f9a50b31bd..36c36d5c0ee59418dffb554ffbed7dd60d7a906c 100644 (file)
@@ -475,6 +475,8 @@ update_current_target (void)
       INHERIT (to_find_memory_regions, t);
       INHERIT (to_make_corefile_notes, t);
       INHERIT (to_get_thread_local_address, t);
+      INHERIT (to_get_execdir, t);
+      INHERIT (to_set_execdir, t);
       /* Do not inherit to_read_description.  */
       /* Do not inherit to_search_memory.  */
       INHERIT (to_magic, t);
index f1e414750edc67ff178e2f21b500ea812d258961..296781bb66c1823f6bb88f97d42c901dc188f34b 100644 (file)
@@ -128,7 +128,11 @@ enum target_waitkind
        inferior, rather than being stuck in the remote_async_wait()
        function. This way the event loop is responsive to other events,
        like for instance the user typing.  */
-    TARGET_WAITKIND_IGNORE
+    TARGET_WAITKIND_IGNORE,
+
+    /* The target has run out of history information,
+       and cannot run backward any further.  */
+    TARGET_WAITKIND_NO_HISTORY
   };
 
 struct target_waitstatus
@@ -147,6 +151,14 @@ struct target_waitstatus
     value;
   };
 
+/* Reverse execution.  */
+enum exec_direction_kind
+  {
+    EXEC_FORWARD,
+    EXEC_REVERSE,
+    EXEC_ERROR
+  };
+
 /* Possible types of events that the inferior handler will have to
    deal with.  */
 enum inferior_event_type
@@ -516,6 +528,11 @@ struct target_ops
                             const gdb_byte *pattern, ULONGEST pattern_len,
                             CORE_ADDR *found_addrp);
 
+    /* Set execution direction (forward/reverse).  */
+    int (*to_set_execdir) (enum exec_direction_kind);
+    /* Get execution direction (forward/reverse).  */
+    enum exec_direction_kind (*to_get_execdir) (void);
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1111,6 +1128,18 @@ extern int target_stopped_data_address_p (struct target_ops *);
 #define target_watchpoint_addr_within_range(target, addr, start, length) \
   (*target.to_watchpoint_addr_within_range) (target, addr, start, length)
 
+/* Forward/reverse execution direction.
+   These will only be implemented by a target that supports reverse execution.
+*/
+#define target_get_execution_direction() \
+    (current_target.to_get_execdir ? \
+     (*current_target.to_get_execdir) () : EXEC_ERROR)
+
+#define target_set_execution_direction(DIR) \
+    (current_target.to_set_execdir ? \
+     (*current_target.to_set_execdir) (DIR) : EXEC_ERROR)
+
+
 extern const struct target_desc *target_read_description (struct target_ops *);
 
 /* Utility implementation of searching memory.  */