diff options
author | Michael Snyder <msnyder@vmware.com> | 2008-06-10 00:13:15 +0000 |
---|---|---|
committer | Michael Snyder <msnyder@vmware.com> | 2008-06-10 00:13:15 +0000 |
commit | da33b5bf66650ddadd113bbe6eeed1ed5da37b25 (patch) | |
tree | 26ee102218c4a0e9ac6464509bdd8f4a2ec11c41 | |
parent | 7a77c2d31f4ba76492b1d1ecb88fcd4a0387bbc5 (diff) | |
download | gdb-da33b5bf66650ddadd113bbe6eeed1ed5da37b25.zip gdb-da33b5bf66650ddadd113bbe6eeed1ed5da37b25.tar.gz gdb-da33b5bf66650ddadd113bbe6eeed1ed5da37b25.tar.bz2 |
Daniel Jacobowitz' update to Michael Snyder's reverse debugging patchesdrow-reverse-20070409-branch
-rw-r--r-- | gdb/ChangeLog.reverse | 70 | ||||
-rw-r--r-- | gdb/Makefile.in | 7 | ||||
-rw-r--r-- | gdb/arm-tdep.c | 52 | ||||
-rw-r--r-- | gdb/breakpoint.c | 7 | ||||
-rw-r--r-- | gdb/breakpoint.h | 3 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 104 | ||||
-rw-r--r-- | gdb/infcall.c | 8 | ||||
-rw-r--r-- | gdb/infcmd.c | 95 | ||||
-rw-r--r-- | gdb/infrun.c | 159 | ||||
-rw-r--r-- | gdb/remote.c | 62 | ||||
-rw-r--r-- | gdb/reverse.c | 197 | ||||
-rw-r--r-- | gdb/target.c | 6 | ||||
-rw-r--r-- | gdb/target.h | 33 |
13 files changed, 765 insertions, 38 deletions
diff --git a/gdb/ChangeLog.reverse b/gdb/ChangeLog.reverse new file mode 100644 index 0000000..9fe045f --- /dev/null +++ b/gdb/ChangeLog.reverse @@ -0,0 +1,70 @@ +2007-07-13 Daniel Jacobowitz <dan@codesourcery.com> + + * infcall.c (call_function_by_hand): Always restore inferior state + for now. + * infrun.c (save_inferior_status): Call to_doing_call. + (restore_inferior_status): Likewise. + (discard_inferior_status): Warn; this should not be reached. + * remote.c (remote_doing_call): New. + (init_remote_ops, init_remote_async_ops): Use it. + * target.c (update_current_target): Handle to_doing_call. + * target.h (struct target_ops): Add to_doing_call. + +2007-04-18 Daniel Jacobowitz <dan@codesourcery.com> + + gdb/ + * infcmd.c (finish_backwards): Correct check for whether to back + up after finishing. + +2007-04-18 Daniel Jacobowitz <dan@codesourcery.com> + + gdb/ + * arm-tdep.c (arm_scan_epilogue): New. + (arm_make_prologue_cache): Use it. + (arm_epilogue_unwind_sniffer): New. + (arm_gdbarch_init): Register it. + +2007-04-13 Daniel Jacobowitz <dan@codesourcery.com> + + Updated patch based on: + 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. + +2007-04-13 Daniel Jacobowitz <dan@codesourcery.com> + + Updated patch based on: + 2006-03-31 Michael Snyder <msnyder@redhat.com> + Execution interface for reverse execution. + * breakpoint.c (breakpoint_silence): New function. + * breakpoint.h (breakpoint_silence): Export. + * 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. + +2007-04-13 Daniel Jacobowitz <dan@codesourcery.com> + + Updated patch based on: + 2006-03-31 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_set_execution_direction): New macro. + (target_get_execution_direction): New macro. + * target.c (update_current_target): Inherit new execdir methods. + + * remote.c (remote_get_execdir, remote_set_execdir): New methods. + (_initialize_remote): Add new methods to remote target vector. + (remote_resume): Check for reverse exec direction, and send + appropriate command to target. + (remote_wait): Check target response for NO_HISTORY status. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 002750c..603d462 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -552,7 +552,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 \ @@ -952,7 +952,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 elfread.o \ dwarf2read.o mipsread.o stabsread.o corefile.o \ dwarf2expr.o dwarf2loc.o dwarf2-frame.o \ @@ -2546,6 +2547,8 @@ remote-sim.o: remote-sim.c $(defs_h) $(inferior_h) $(value_h) \ remote-utils.o: remote-utils.c $(defs_h) $(gdb_string_h) $(gdbcmd_h) \ $(target_h) $(serial_h) $(gdbcore_h) $(inferior_h) $(remote_utils_h) \ $(regcache_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/arm-tdep.c b/gdb/arm-tdep.c index 22a08f8..899dd06 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -888,6 +888,45 @@ arm_scan_prologue (struct frame_info *next_frame, struct arm_prologue_cache *cac cache->frameoffset = 0; } +/* This function tries to guess whether we are in the epilogue of an + ARM function. We need to detect whether the stack frame has + already been destroyed - if it has, then neither the prologue + scanner nor GCC's unwind tables will be valid. This is very hokey + and generally unsafe. */ + +static int +arm_scan_epilogue (struct frame_info *next_frame, + struct arm_prologue_cache *cache) +{ + unsigned int insn; + gdb_byte buf[4]; + CORE_ADDR prev_pc = frame_pc_unwind (next_frame); + + /* Assume there is no frame until proven otherwise. */ + if (cache != NULL) + { + cache->framereg = ARM_SP_REGNUM; + cache->framesize = 0; + cache->frameoffset = 0; + } + + /* Check for Thumb epilogue. */ + if (arm_pc_is_thumb (prev_pc)) + /* Not yet implemented. */ + return 0; + + if (target_read_memory (prev_pc, buf, 4) != 0) + return 0; + insn = extract_unsigned_integer (buf, 4); + + if (insn == 0xe12fff1e) /* bx lr */ + /* If this is a return, we have no frame left and no saved + registers. */ + return 1; + + return 0; +} + static struct arm_prologue_cache * arm_make_prologue_cache (struct frame_info *next_frame) { @@ -898,7 +937,8 @@ arm_make_prologue_cache (struct frame_info *next_frame) cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache); cache->saved_regs = trad_frame_alloc_saved_regs (next_frame); - arm_scan_prologue (next_frame, cache); + if (arm_scan_epilogue (next_frame, cache) == 0) + arm_scan_prologue (next_frame, cache); unwound_fp = frame_unwind_register_unsigned (next_frame, cache->framereg); if (unwound_fp == 0) @@ -995,6 +1035,15 @@ arm_prologue_unwind_sniffer (struct frame_info *next_frame) return &arm_prologue_unwind; } +static const struct frame_unwind * +arm_epilogue_unwind_sniffer (struct frame_info *next_frame) +{ + if (arm_scan_epilogue (next_frame, NULL)) + return &arm_prologue_unwind; + else + return NULL; +} + static struct arm_prologue_cache * arm_make_stub_cache (struct frame_info *next_frame) { @@ -2996,6 +3045,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) /* Add some default predicates. */ frame_unwind_append_sniffer (gdbarch, arm_stub_unwind_sniffer); + frame_unwind_append_sniffer (gdbarch, arm_epilogue_unwind_sniffer); frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer); frame_unwind_append_sniffer (gdbarch, arm_prologue_unwind_sniffer); diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index fc8e389..defeb2a 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -7534,6 +7534,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 1230f49..e5ed453 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -850,4 +850,7 @@ extern int deprecated_exception_catchpoints_are_fragile; reinitialized -- e.g. when program is re-run. */ extern int deprecated_exception_support_initialized; +/* 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 2c567df..9a78447 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -127,6 +127,7 @@ Copyright (C) 1988-2006 Free Software Foundation, Inc. * 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 @@ -4387,6 +4388,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/infcall.c b/gdb/infcall.c index c4435e5..80144cc 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -780,7 +780,7 @@ You must use a pointer to function type variable. Command ignored."), arg_name); signal. Further execution of the FUNCTION is not allowed. */ - if (unwind_on_signal_p) + if (unwind_on_signal_p || 1 /* FIXME restoring state */) { /* The user wants the context restored. */ @@ -819,6 +819,12 @@ Evaluation of the expression containing the function (%s) will be abandoned."), } } + if (!stop_stack_dummy && 1 /* FIXME restoring state */) + { + error (_("\ +The program being debugged stopped while in a function called from GDB.\n\ +GDB has restored the context to what it was before the call.")); + } if (!stop_stack_dummy) { /* We hit a breakpoint inside the FUNCTION. */ diff --git a/gdb/infcmd.c b/gdb/infcmd.c index a1a64c0..ddc69ec 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -1266,6 +1266,8 @@ finish_command_continuation (struct continuation_arg *arg) /* "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) { @@ -1306,16 +1308,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); - - if (!target_can_async_p ()) - old_chain = make_cleanup_delete_breakpoint (breakpoint); - else - old_chain = make_exec_cleanup_delete_breakpoint (breakpoint); - /* Find the function we will return from. */ function = find_pc_function (get_frame_pc (get_selected_frame (NULL))); @@ -1324,10 +1316,31 @@ 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); + + if (!target_can_async_p ()) + old_chain = make_cleanup_delete_breakpoint (breakpoint); + else + old_chain = make_exec_cleanup_delete_breakpoint (breakpoint); + /* If running asynchronously and the target support asynchronous execution, set things up for the rest of the finish command to be completed later on, when gdb has detected that the target has @@ -1384,6 +1397,66 @@ finish_command (char *arg, int from_tty) do_cleanups (old_chain); } } + +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. */ + back_up = bpstat_find_breakpoint (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. */ + /* (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 diff --git a/gdb/infrun.c b/gdb/infrun.c index 8fd0376..672fa81 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -899,7 +899,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 @@ -1517,6 +1519,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. @@ -2182,6 +2190,17 @@ process_event_stop_test: 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->another_trap = 1; + keep_going (ecs); + return; + } break; case BPSTAT_WHAT_THROUGH_SIGTRAMP: @@ -2365,7 +2384,22 @@ process_event_stop_test: 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; } @@ -2454,10 +2488,31 @@ process_event_stop_test: 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; } @@ -2518,9 +2573,22 @@ process_event_stop_test: 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; } @@ -2649,17 +2717,38 @@ process_event_stop_test: if (ecs->stop_func_end && ecs->sal.end >= ecs->stop_func_end) { - /* If this is the last line of the function, don't keep stepping - (it would probably step us out of the function). - This is particularly necessary for a one-line function, - in which after skipping the prologue we better stop even though - we will be in mid-line. */ - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "infrun: stepped to a different function\n"); - stop_step = 1; - print_stop_reason (END_STEPPING_RANGE, 0); - stop_stepping (ecs); - return; + if (target_get_execution_direction () != EXEC_REVERSE) + { + /* If this is the last line of the function, don't keep + stepping (it would probably step us out of the function). + This is particularly necessary for a one-line function, + in which after skipping the prologue we better stop even + though we will be in mid-line. */ + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: stepped to a different function\n"); + stop_step = 1; + print_stop_reason (END_STEPPING_RANGE, 0); + stop_stepping (ecs); + return; + } + else + { + /* If we stepped backward into the last line of a function, + then we've presumably stepped thru a return. We want to + keep stepping backward until we reach the beginning of + the new line. */ + step_range_start = ecs->sal.pc; + step_range_end = ecs->sal.end; + step_frame_id = get_frame_id (get_current_frame ()); + ecs->current_line = ecs->sal.line; + ecs->current_symtab = ecs->sal.symtab; + /* Adjust for prologue, in case of a one-line function. */ + if (in_prologue (step_range_start, ecs->stop_func_start)) + step_range_start = SKIP_PROLOGUE (step_range_start); + keep_going (ecs); + return; + } } step_range_start = ecs->sal.pc; step_range_end = ecs->sal.end; @@ -2722,6 +2811,28 @@ step_into_function (struct execution_control_state *ecs) if (s && s->language != language_asm) ecs->stop_func_start = SKIP_PROLOGUE (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 @@ -3042,6 +3153,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")); @@ -3675,6 +3790,7 @@ save_inferior_status (int restore_stack_info) inf_status->registers = regcache_dup (current_regcache); inf_status->selected_frame_id = get_frame_id (get_selected_frame (NULL)); + current_target.to_doing_call (1); return inf_status; } @@ -3723,6 +3839,8 @@ restore_inferior_status (struct inferior_status *inf_status) regcache_xfree (stop_registers); stop_registers = inf_status->stop_registers; + current_target.to_doing_call (0); + /* The inferior can be gone if the user types "print exit(0)" (and perhaps other times). */ if (target_has_execution) @@ -3770,6 +3888,7 @@ make_cleanup_restore_inferior_status (struct inferior_status *inf_status) void discard_inferior_status (struct inferior_status *inf_status) { + warning (_("Discarding inferior status")); /* See save_inferior_status for info on stop_bpstat. */ bpstat_clear (&inf_status->stop_bpstat); regcache_xfree (inf_status->registers); diff --git a/gdb/remote.c b/gdb/remote.c index 866c477..0fd8c24 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -2851,7 +2851,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); @@ -3125,6 +3133,11 @@ remote_wait (ptid_t ptid, struct target_waitstatus *status) switch (buf[0]) { case 'E': /* Error of some sort. */ + if (buf[1] == '0' && buf[2] == '6') + { + status->kind = TARGET_WAITKIND_NO_HISTORY; + goto got_status; + } warning (_("Remote failure reply: %s"), buf); continue; case 'F': /* File-I/O request. */ @@ -6110,6 +6123,47 @@ remote_read_description (struct target_ops *target) return NULL; } +/* 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 +remote_doing_call (int starting) +{ + struct remote_state *rs = get_remote_state (); + + if (starting) + strcpy (rs->buf, "QStartCall"); + else + strcpy (rs->buf, "QEndCall"); + remote_send (&rs->buf, &rs->buf_size); +} + static void init_remote_ops (void) { @@ -6157,11 +6211,14 @@ 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; remote_ops.to_flash_done = remote_flash_done; remote_ops.to_read_description = remote_read_description; + remote_ops.to_doing_call = remote_doing_call; } /* Set up the extended remote vector by making a copy of the standard @@ -6294,7 +6351,8 @@ Specify the serial device it is connected to (e.g. /dev/ttya)."; remote_async_ops.to_memory_map = remote_memory_map; remote_async_ops.to_flash_erase = remote_flash_erase; remote_async_ops.to_flash_done = remote_flash_done; - remote_ops.to_read_description = remote_read_description; + remote_async_ops.to_read_description = remote_read_description; + remote_async_ops.to_doing_call = remote_doing_call; } /* Set up the async extended remote vector by making a copy of the standard diff --git a/gdb/reverse.c b/gdb/reverse.c new file mode 100644 index 0000000..29f7320 --- /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 b4857ca..b3e2a9d 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -466,6 +466,9 @@ 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); + INHERIT (to_doing_call, t); /* Do not inherit to_read_description. */ INHERIT (to_magic, t); /* Do not inherit to_memory_map. */ @@ -644,6 +647,9 @@ update_current_target (void) de_fault (to_async, (void (*) (void (*) (enum inferior_event_type, void*), void*)) tcomplain); + de_fault (to_doing_call, + (void (*) (int)) + target_ignore); current_target.to_read_description = NULL; #undef de_fault diff --git a/gdb/target.h b/gdb/target.h index d9f1ca1..454193e 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -131,7 +131,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 @@ -150,6 +154,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 @@ -500,6 +512,13 @@ struct target_ops was available. */ const struct target_desc *(*to_read_description) (struct target_ops *ops); + /* 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); + + void (*to_doing_call) (int starting); + int to_magic; /* Need sub-structure for target machine related rather than comm related? */ @@ -1194,6 +1213,18 @@ extern int target_stopped_data_address_p (struct target_ops *); #define target_stopped_data_address_p(CURRENT_TARGET) (1) #endif +/* 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 *); /* Routines for maintenance of the target structures... |