2008-10-17 Michael Snyder <msnyder@vmware.com>
authorMichael Snyder <msnyder@vmware.com>
Fri, 17 Oct 2008 19:43:47 +0000 (19:43 +0000)
committerMichael Snyder <msnyder@vmware.com>
Fri, 17 Oct 2008 19:43:47 +0000 (19:43 +0000)
Target interface for reverse debugging.
* target.h (enum target_waitkind):
Add new wait event, TARGET_WAITKIND_NO_HISTORY.
(struct target_ops): New method to_can_execute_reverse.
(target_can_execute_reverse): New macro.
* target.c (update_current_target): Inherit to_can_execute_reverse.

Remote interface for reverse debugging.
* remote.c (remote_can_execute_reverse): New target method.
(remote_resume): Check for reverse exec direction, and send
appropriate command to target.
(remote_wait_as): Check target response for NO_HISTORY status.
Also check for empty reply (target doesn't understand "bs" or "bc).
(remote_vcont_resume): Jump out if attempting reverse execution.

Event handling interface for reverse debugging.
* infrun.c (execution_direction): New state variable.
(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.
(step_into_function): Rename to handle_step_into_function.
(handle_step_into_function_backward): New function.
(set_exec_direction_func, show_exec_direction_func): New funcs.
(proceed): No need to singlestep over a breakpoint
when resuming in reverse.

* inferior.h (enum exec_direction_kind): New enum.
(execution_direction): Export new execution state variable.

* breakpoint.c (make_breakpoint_silent): New function.
* breakpoint.h (make_breakpoint_silent): Export.
* infcmd.c (finish_command): Check for reverse exec direction.
(finish_backward): New function, handle finish cmd in reverse.

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/infcmd.c
gdb/inferior.h
gdb/infrun.c
gdb/remote.c
gdb/reverse.c [new file with mode: 0644]
gdb/target.c
gdb/target.h

index 56c609647b343e3525c8d8e5ac9ef406ff8c722f..29a29ff271942bbd4e3bc0d905877eb09612c563 100644 (file)
@@ -1,3 +1,47 @@
+2008-10-17  Michael Snyder  <msnyder@vmware.com>
+       Target interface for reverse debugging.
+       * target.h (enum target_waitkind): 
+       Add new wait event, TARGET_WAITKIND_NO_HISTORY.
+       (struct target_ops): New method to_can_execute_reverse.
+       (target_can_execute_reverse): New macro.
+       * target.c (update_current_target): Inherit to_can_execute_reverse.
+
+       Remote interface for reverse debugging.
+       * remote.c (remote_can_execute_reverse): New target method.
+       (remote_resume): Check for reverse exec direction, and send 
+       appropriate command to target.
+       (remote_wait_as): Check target response for NO_HISTORY status.
+       Also check for empty reply (target doesn't understand "bs" or "bc).
+       (remote_vcont_resume): Jump out if attempting reverse execution.
+
+       Event handling interface for reverse debugging.
+       * infrun.c (execution_direction): New state variable.
+       (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.
+       (step_into_function): Rename to handle_step_into_function.
+       (handle_step_into_function_backward): New function.
+       (set_exec_direction_func, show_exec_direction_func): New funcs.
+       (proceed): No need to singlestep over a breakpoint
+       when resuming in reverse.
+       
+       * inferior.h (enum exec_direction_kind): New enum.
+       (execution_direction): Export new execution state variable.
+
+       * breakpoint.c (make_breakpoint_silent): New function.
+       * breakpoint.h (make_breakpoint_silent): Export.
+       * infcmd.c (finish_command): Check for reverse exec direction.
+       (finish_backward): New function, handle finish cmd in reverse.
+
+       User interface for reverse execution.
+       * Makefile.in (reverse.c): New file.
+       * reverse.c: New file.  User interface for reverse execution.
+
 2008-10-17  Pedro Alves  <pedro@codesourcery.com>
 
        * remote.c (record_currthread): Add inferior before child threads.
index 93f9d94d9899fae96159a06425f4e925f321f7b5..fe569c3e4bbf71bfe195d2fe3d763898b6853bbd 100644 (file)
@@ -641,7 +641,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 \
@@ -780,7 +780,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
        findcmd.o \
        std-regs.o \
        signals.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 \
index 27ed621b5a6f27f4de1789851c222296e56cb8fa..180f6c9dee960099fa8d2976fb1c0f9ca4f6e46c 100644 (file)
@@ -7863,6 +7863,13 @@ set_ignore_count (int bptnum, int count, int from_tty)
   error (_("No breakpoint number %d."), bptnum);
 }
 
+void
+make_breakpoint_silent (struct breakpoint *b)
+{
+  /* Silence the breakpoint.  */
+  b->silent = 1;
+}
+
 /* Command to set ignore-count of breakpoint N to COUNT.  */
 
 static void
index 0e1aa0e60b723135a6be777dade38965a6ebc0f1..5c05aa9043077a204ef0971c69a868f2f2838cd9 100644 (file)
@@ -897,4 +897,7 @@ extern int breakpoints_always_inserted_mode (void);
    in our opinion won't ever trigger.  */
 extern void breakpoint_retire_moribund (void);
 
+/* Tell a breakpoint to be quiet.  */
+extern void make_breakpoint_silent (struct breakpoint *);
+
 #endif /* !defined (BREAKPOINT_H) */
index 6ed6341fa2b730c140b2cabfc28224ea4adc0737..5179a2fef681ac2c7a53d2f1635d7f9381d0193d 100644 (file)
@@ -1366,19 +1366,109 @@ finish_command_continuation_free_arg (void *arg)
   xfree (arg);
 }
 
+/* finish_backward -- helper function for finish_command.  */
+
+static void
+finish_backward (struct symbol *function)
+{
+  struct symtab_and_line sal;
+  struct thread_info *tp = inferior_thread ();
+  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);
+
+  /* We don't need a return value.  */
+  tp->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.  */
+      make_breakpoint_silent (breakpoint);
+      old_chain = make_cleanup_delete_breakpoint (breakpoint);
+      proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+      /* We will be stopped when proceed returns.  */
+      back_up = bpstat_find_breakpoint (tp->stop_bpstat, breakpoint) != NULL;
+      do_cleanups (old_chain);
+    }
+  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.  */
+      tp->step_range_start = tp->step_range_end = 1;
+      proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
+    }
+  return;
+}
+
+/* finish_forward -- helper function for finish_command.  */
+
+static void
+finish_forward (struct symbol *function, struct frame_info *frame)
+{
+  struct symtab_and_line sal;
+  struct thread_info *tp = inferior_thread ();
+  struct breakpoint *breakpoint;
+  struct cleanup *old_chain;
+  struct finish_command_continuation_args *cargs;
+
+  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);
+
+  tp->proceed_to_finish = 1;    /* We want stop_registers, please...  */
+  make_cleanup_restore_integer (&suppress_stop_observer);
+  suppress_stop_observer = 1;
+  proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
+
+  cargs = xmalloc (sizeof (*cargs));
+
+  cargs->breakpoint = breakpoint;
+  cargs->function = function;
+  add_continuation (tp, finish_command_continuation, cargs,
+                    finish_command_continuation_free_arg);
+
+  discard_cleanups (old_chain);
+  if (!target_can_async_p ())
+    do_all_continuations ();
+}
+
 /* "finish": Set a temporary breakpoint at the place the selected
    frame will return to, then continue.  */
 
 static void
 finish_command (char *arg, int from_tty)
 {
-  struct symtab_and_line sal;
   struct frame_info *frame;
   struct symbol *function;
-  struct breakpoint *breakpoint;
-  struct cleanup *old_chain;
-  struct finish_command_continuation_args *cargs;
-  struct thread_info *tp;
 
   int async_exec = 0;
 
@@ -1391,6 +1481,10 @@ finish_command (char *arg, int from_tty)
   if (async_exec && !target_can_async_p ())
     error (_("Asynchronous execution not supported on this target."));
 
+  /* Don't try to async in reverse.  */
+  if (async_exec && execution_direction == EXEC_REVERSE)
+    error (_("Asynchronous 'finish' not supported in reverse."));
+
   /* If we are not asked to run in the bg, then prepare to run in the
      foreground, synchronously.  */
   if (!async_exec && target_can_async_p ())
@@ -1408,17 +1502,8 @@ finish_command (char *arg, int from_tty)
   if (frame == 0)
     error (_("\"finish\" not meaningful in the outermost frame."));
 
-  tp = inferior_thread ();
-
   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)));
@@ -1427,25 +1512,18 @@ finish_command (char *arg, int from_tty)
      source.  */
   if (from_tty)
     {
-      printf_filtered (_("Run till exit from "));
+      if (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);
     }
 
-  tp->proceed_to_finish = 1;   /* We want stop_registers, please...  */
-  make_cleanup_restore_integer (&suppress_stop_observer);
-  suppress_stop_observer = 1;
-  proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
-
-  cargs = xmalloc (sizeof (*cargs));
-
-  cargs->breakpoint = breakpoint;
-  cargs->function = function;
-  add_continuation (tp, finish_command_continuation, cargs,
-                   finish_command_continuation_free_arg);
-
-  discard_cleanups (old_chain);
-  if (!target_can_async_p ())
-    do_all_continuations ();
+  if (execution_direction == EXEC_REVERSE)
+    finish_backward (function);
+  else
+    finish_forward (function, frame);
 }
 \f
 
index 43f902238ff72133730aa606dbfd8b1908b541d7..8b03b63d18beac535b891483141eaacd8c7bf296 100644 (file)
@@ -339,6 +339,16 @@ enum stop_kind
     STOP_QUIETLY_NO_SIGSTOP
   };
 
+/* Reverse execution.  */
+enum exec_direction_kind
+  {
+    EXEC_FORWARD,
+    EXEC_REVERSE,
+    EXEC_ERROR
+  };
+
+extern enum exec_direction_kind execution_direction;
+
 /* Nonzero if proceed is being used for a "finish" command or a similar
    situation when stop_registers should be saved.  */
 
index 150bf4c93a1d314bfefdee9cad42ce185eb91fa2..511450115d97fb2306723af45d370c3122201b0b 100644 (file)
@@ -1242,11 +1242,17 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
 
   if (addr == (CORE_ADDR) -1)
     {
-      if (pc == stop_pc && breakpoint_here_p (pc))
+      if (pc == stop_pc && breakpoint_here_p (pc) 
+         && execution_direction != EXEC_REVERSE)
        /* There is a breakpoint at the address we will resume at,
           step one instruction before inserting breakpoints so that
           we do not stop right away (and report a second hit at this
-          breakpoint).  */
+          breakpoint).
+
+          Note, we don't do this in reverse, because we won't
+          actually be executing the breakpoint insn anyway.
+          We'll be (un-)executing the previous instruction.  */
+
        oneproc = 1;
       else if (gdbarch_single_step_through_delay_p (gdbarch)
               && gdbarch_single_step_through_delay (gdbarch,
@@ -1475,7 +1481,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
 };
 
 /* The PTID we'll do a target_wait on.*/
@@ -1506,7 +1514,8 @@ void init_execution_control_state (struct execution_control_state *ecs);
 
 void handle_inferior_event (struct execution_control_state *ecs);
 
-static void step_into_function (struct execution_control_state *ecs);
+static void handle_step_into_function (struct execution_control_state *ecs);
+static void handle_step_into_function_backward (struct execution_control_state *ecs);
 static void insert_step_resume_breakpoint_at_frame (struct frame_info *step_frame);
 static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
 static void insert_step_resume_breakpoint_at_sal (struct symtab_and_line sr_sal,
@@ -2197,6 +2206,12 @@ handle_inferior_event (struct execution_control_state *ecs)
       ecs->event_thread->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.
@@ -2917,6 +2932,17 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
            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_CHECK_SHLIBS:
@@ -3082,10 +3108,24 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
       && stop_pc < ecs->event_thread->step_range_end)
     {
       if (debug_infrun)
-        fprintf_unfiltered (gdb_stdlog, "infrun: stepping inside range [0x%s-0x%s]\n",
+       fprintf_unfiltered (gdb_stdlog, "infrun: stepping inside range [0x%s-0x%s]\n",
                            paddr_nz (ecs->event_thread->step_range_start),
                            paddr_nz (ecs->event_thread->step_range_end));
-      keep_going (ecs);
+
+      /* When stepping backward, stop at beginning of line range
+        (unless it's the function entry point, in which case
+        keep going back to the call point).  */
+      if (stop_pc == ecs->event_thread->step_range_start
+         && stop_pc != ecs->stop_func_start
+         && execution_direction == EXEC_REVERSE)
+       {
+         ecs->event_thread->stop_step = 1;
+         print_stop_reason (END_STEPPING_RANGE, 0);
+         stop_stepping (ecs);
+       }
+      else
+       keep_going (ecs);
+
       return;
     }
 
@@ -3145,8 +3185,9 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
      previous frame must have valid frame IDs.  */
   if (!frame_id_eq (get_frame_id (get_current_frame ()),
                    ecs->event_thread->step_frame_id)
-      && frame_id_eq (frame_unwind_id (get_current_frame ()),
-                     ecs->event_thread->step_frame_id))
+      && (frame_id_eq (frame_unwind_id (get_current_frame ()),
+                      ecs->event_thread->step_frame_id)
+         || execution_direction == EXEC_REVERSE))
     {
       CORE_ADDR real_stop_pc;
 
@@ -3172,10 +3213,28 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
 
       if (ecs->event_thread->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 (execution_direction == EXEC_REVERSE)
+           {
+             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;
        }
@@ -3215,7 +3274,10 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
        tmp_sal = find_pc_line (ecs->stop_func_start, 0);
        if (tmp_sal.line != 0)
          {
-           step_into_function (ecs);
+           if (execution_direction == EXEC_REVERSE)
+             handle_step_into_function_backward (ecs);
+           else
+             handle_step_into_function (ecs);
            return;
          }
       }
@@ -3232,9 +3294,20 @@ 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 (execution_direction == EXEC_REVERSE)
+       {
+         /* Set a breakpoint at callee's start address.
+            From there we can step once and be back in the caller.  */
+         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;
     }
@@ -3386,19 +3459,20 @@ currently_stepping (struct thread_info *tp)
          || bpstat_should_step ());
 }
 
-/* Subroutine call with source code we should not step over.  Do step
-   to the first line of code in it.  */
+/* Inferior has stepped into a subroutine call with source code that
+   we should not step over.  Do step to the first line of code in
+   it.  */
 
 static void
-step_into_function (struct execution_control_state *ecs)
+handle_step_into_function (struct execution_control_state *ecs)
 {
   struct symtab *s;
   struct symtab_and_line stop_func_sal, sr_sal;
 
   s = find_pc_symtab (stop_pc);
   if (s && s->language != language_asm)
-    ecs->stop_func_start = gdbarch_skip_prologue
-                            (current_gdbarch, ecs->stop_func_start);
+    ecs->stop_func_start = gdbarch_skip_prologue (current_gdbarch, 
+                                                 ecs->stop_func_start);
 
   stop_func_sal = find_pc_line (ecs->stop_func_start, 0);
   /* Use the step_resume_break to step until the end of the prologue,
@@ -3461,6 +3535,43 @@ step_into_function (struct execution_control_state *ecs)
   keep_going (ecs);
 }
 
+/* Inferior has stepped backward into a subroutine call with source
+   code that we should not step over.  Do step to the beginning of the
+   last line of code in it.  */
+
+static void
+handle_step_into_function_backward (struct execution_control_state *ecs)
+{
+  struct symtab *s;
+  struct symtab_and_line stop_func_sal, sr_sal;
+
+  s = find_pc_symtab (stop_pc);
+  if (s && s->language != language_asm)
+    ecs->stop_func_start = gdbarch_skip_prologue (current_gdbarch, 
+                                                 ecs->stop_func_start);
+
+  stop_func_sal = find_pc_line (stop_pc, 0);
+
+  /* OK, we're just going to keep stepping here.  */
+  if (stop_func_sal.pc == stop_pc)
+    {
+      /* We're there already.  Just stop stepping now.  */
+      ecs->event_thread->stop_step = 1;
+      print_stop_reason (END_STEPPING_RANGE, 0);
+      stop_stepping (ecs);
+    }
+  else
+    {
+      /* 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.  */
+      ecs->event_thread->step_range_start = stop_func_sal.pc;
+      ecs->event_thread->step_range_end = stop_func_sal.end;
+      keep_going (ecs);
+    }
+  return;
+}
+
 /* Insert a "step-resume breakpoint" at SR_SAL with frame ID SR_ID.
    This is used to both functions and to skip over code.  */
 
@@ -3768,6 +3879,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"));
@@ -4699,6 +4814,55 @@ save_inferior_ptid (void)
 }
 \f
 
+/* User interface for reverse debugging:
+   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;
+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_can_execute_reverse)
+    {
+      if (!strcmp (exec_direction, exec_forward))
+       execution_direction = EXEC_FORWARD;
+      else if (!strcmp (exec_direction, exec_reverse))
+       execution_direction = EXEC_REVERSE;
+    }
+}
+
+static void
+show_exec_direction_func (struct ui_file *out, int from_tty,
+                         struct cmd_list_element *cmd, const char *value)
+{
+  switch (execution_direction) {
+  case EXEC_FORWARD:
+    fprintf_filtered (out, _("Forward.\n"));
+    break;
+  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;
+  }
+}
+
+/* User interface for non-stop mode.  */
+
 int non_stop = 0;
 static int non_stop_1 = 0;
 
@@ -4924,6 +5088,14 @@ breakpoints, even if such is supported by the target."),
                           &maintenance_set_cmdlist,
                           &maintenance_show_cmdlist);
 
+  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);
+
   /* ptid initializations */
   null_ptid = ptid_build (0, 0, 0);
   minus_one_ptid = ptid_build (-1, 0, 0);
index 727d0842eac4b9c57fecea07330b096f5107abe8..f334c0b9b5708e41d9211746a6a25c3dfdda6097 100644 (file)
@@ -3443,7 +3443,15 @@ remote_resume (ptid_t ptid, int step, enum target_signal siggnal)
     set_continue_thread (ptid);
 
   buf = rs->buf;
-  if (siggnal != TARGET_SIGNAL_0)
+  if (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);
@@ -3671,6 +3679,7 @@ remote_wait_as (ptid_t ptid, struct target_waitstatus *status)
   ptid_t event_ptid = null_ptid;
   ULONGEST addr;
   int solibs_changed = 0;
+  int replay_event = 0;
   char *buf, *p;
 
   status->kind = TARGET_WAITKIND_IGNORE;
@@ -3786,6 +3795,16 @@ Packet: '%s'\n"),
                    solibs_changed = 1;
                    p = p_temp;
                  }
+               else if (strncmp (p, "replaylog", p1 - p) == 0)
+                 {
+                   /* NO_HISTORY event.
+                      p1 will indicate "begin" or "end", but
+                      it makes no difference for now, so ignore it.  */
+                   replay_event = 1;
+                   p_temp = strchr (p1 + 1, ';');
+                   if (p_temp)
+                     p = p_temp;
+                 }
                else
                  {
                    /* Silently skip unknown optional info.  */
@@ -3831,6 +3850,8 @@ Packet: '%s'\n"),
     case 'S':          /* Old style status, just signal only.  */
       if (solibs_changed)
        status->kind = TARGET_WAITKIND_LOADED;
+      else if (replay_event)
+            status->kind = TARGET_WAITKIND_NO_HISTORY;
       else
        {
          status->kind = TARGET_WAITKIND_STOPPED;
@@ -7628,6 +7649,14 @@ remote_command (char *args, int from_tty)
   help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
 }
 
+static int remote_target_can_reverse = 1;
+
+static int
+remote_can_execute_reverse (void)
+{
+  return remote_target_can_reverse;
+}
+
 static void
 init_remote_ops (void)
 {
@@ -7676,6 +7705,7 @@ 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_can_execute_reverse = remote_can_execute_reverse;
   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..dbee326
--- /dev/null
@@ -0,0 +1,143 @@
+/* Reverse execution and reverse debugging.
+
+   Copyright (C) 2006, 2007, 2008 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"
+#include "inferior.h"
+
+/* User interface:
+   reverse-step, reverse-next etc.  */
+
+static void exec_direction_default (void *notused)
+{
+  /* Return execution direction to default state.  */
+  execution_direction = EXEC_FORWARD;
+}
+
+/* exec_reverse_once -- accepts an arbitrary gdb command (string), 
+   and executes it with exec-direction set to 'reverse'.
+
+   Used to implement reverse-next etc. commands.  */
+
+static void
+exec_reverse_once (char *cmd, char *args, int from_tty)
+{
+  char *reverse_command;
+  enum exec_direction_kind dir = execution_direction;
+  struct cleanup *old_chain;
+
+  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_can_execute_reverse)
+    error (_("Target %s does not support this command."), target_shortname);
+
+  reverse_command = xstrprintf ("%s %s", cmd, args ? args : "");
+  old_chain = make_cleanup (exec_direction_default, NULL);
+  make_cleanup (xfree, reverse_command);
+  execution_direction = EXEC_REVERSE;
+  execute_command (reverse_command, from_tty);
+  do_cleanups (old_chain);
+}
+
+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_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 but run it 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 654d3c06c9f66a8d99e8e3ef0275106e6932ac02..a6bb23e773a6b421060064eaca4db4ac09056ca2 100644 (file)
@@ -455,6 +455,7 @@ 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_can_execute_reverse, t);
       /* Do not inherit to_read_description.  */
       /* Do not inherit to_search_memory.  */
       INHERIT (to_magic, t);
index e3537c4cd5a05e702b205bc5d9a82e21b710f13d..cdc898fe191c48119cadb80e3f2af60a8654dc77 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
@@ -523,6 +527,9 @@ struct target_ops
                             const gdb_byte *pattern, ULONGEST pattern_len,
                             CORE_ADDR *found_addrp);
 
+    /* Can target execute in reverse?  */
+    int (*to_can_execute_reverse) ();
+
     int to_magic;
     /* Need sub-structure for target machine related rather than comm related?
      */
@@ -1127,6 +1134,11 @@ 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)
 
+/* Target can execute in reverse?  */
+#define target_can_execute_reverse \
+     (current_target.to_can_execute_reverse ? \
+      current_target.to_can_execute_reverse () : 0)
+
 extern const struct target_desc *target_read_description (struct target_ops *);
 
 /* Utility implementation of searching memory.  */