From b2175913c5f01e75fdb9b19699049737abb2f37c Mon Sep 17 00:00:00 2001 From: Michael Snyder Date: Fri, 17 Oct 2008 19:43:47 +0000 Subject: [PATCH] 2008-10-17 Michael Snyder 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 | 44 ++++++++++ gdb/Makefile.in | 5 +- gdb/breakpoint.c | 7 ++ gdb/breakpoint.h | 3 + gdb/infcmd.c | 138 +++++++++++++++++++++++------- gdb/inferior.h | 10 +++ gdb/infrun.c | 214 ++++++++++++++++++++++++++++++++++++++++++----- gdb/remote.c | 32 ++++++- gdb/reverse.c | 143 +++++++++++++++++++++++++++++++ gdb/target.c | 1 + gdb/target.h | 14 +++- 11 files changed, 556 insertions(+), 55 deletions(-) create mode 100644 gdb/reverse.c diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 56c609647b3..29a29ff2719 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,47 @@ +2008-10-17 Michael Snyder + 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 * remote.c (record_currthread): Add inferior before child threads. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 93f9d94d989..fe569c3e4bb 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -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 \ diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 27ed621b5a6..180f6c9dee9 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -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 diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 0e1aa0e60b7..5c05aa90430 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -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) */ diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 6ed6341fa2b..5179a2fef68 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -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); } diff --git a/gdb/inferior.h b/gdb/inferior.h index 43f902238ff..8b03b63d18b 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -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. */ diff --git a/gdb/infrun.c b/gdb/infrun.c index 150bf4c93a1..511450115d9 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -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) } +/* 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); diff --git a/gdb/remote.c b/gdb/remote.c index 727d0842eac..f334c0b9b57 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -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 index 00000000000..dbee326bba6 --- /dev/null +++ b/gdb/reverse.c @@ -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.")); +} diff --git a/gdb/target.c b/gdb/target.c index 654d3c06c9f..a6bb23e773a 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -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); diff --git a/gdb/target.h b/gdb/target.h index e3537c4cd5a..cdc898fe191 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 @@ -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. */ -- 2.30.2