From 0d07eec401e947d49a5086b5c55e2f4511a2dece Mon Sep 17 00:00:00 2001 From: Michael Snyder Date: Tue, 10 Jun 2008 02:11:18 +0000 Subject: [PATCH] Reverse execution branch 2008-06-09 Michael Snyder 2006-05-02 Michael Snyder * 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 User interface for reverse execution. * Makefile.in (reverse.c): New file. * reverse.c: New file. User interface for reverse execution. --- gdb/ChangeLog | 36 ++++++++ gdb/Makefile.in | 7 +- gdb/breakpoint.c | 7 ++ gdb/breakpoint.h | 3 + gdb/doc/gdb.texinfo | 104 +++++++++++++++++++++++ gdb/infcmd.c | 89 ++++++++++++++++++-- gdb/infrun.c | 112 +++++++++++++++++++++++-- gdb/remote.c | 52 +++++++++++- gdb/reverse.c | 197 ++++++++++++++++++++++++++++++++++++++++++++ gdb/target.c | 2 + gdb/target.h | 31 ++++++- 11 files changed, 617 insertions(+), 23 deletions(-) create mode 100644 gdb/reverse.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 71e9df5f9e1..3deec46401b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,39 @@ +2008-06-09 Michael Snyder + 2006-05-02 Michael Snyder + + * 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 + 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 * remote.c (remote_wait): Include beginning of malformed packet diff --git a/gdb/Makefile.in b/gdb/Makefile.in index c14dc1ab4ac..891b31aae09 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -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) \ diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 051b753d639..809ef26a060 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -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 diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 80544e4dedd..8c45999d494 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -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) */ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 5cef05b5efb..757a8884359 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 2397c30a09e..9fcf8ceebaf 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -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; +} + static void environment_info (char *var, int from_tty) diff --git a/gdb/infrun.c b/gdb/infrun.c index 2960acb2a8f..e7d1decc65e 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -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")); diff --git a/gdb/remote.c b/gdb/remote.c index 3f7d07b831d..24deaa09da7 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -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 index 00000000000..29f7320c263 --- /dev/null +++ b/gdb/reverse.c @@ -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.")); +} diff --git a/gdb/target.c b/gdb/target.c index be7f3a11e22..36c36d5c0ee 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -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); diff --git a/gdb/target.h b/gdb/target.h index f1e414750ed..296781bb66c 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -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. */ -- 2.47.2