diff options
author | Michael Snyder <msnyder@vmware.com> | 2008-10-17 19:43:47 +0000 |
---|---|---|
committer | Michael Snyder <msnyder@vmware.com> | 2008-10-17 19:43:47 +0000 |
commit | b2175913c5f01e75fdb9b19699049737abb2f37c (patch) | |
tree | 7e157c65ec30335451ef139353a79a5565eeb56e /gdb/infrun.c | |
parent | 153ccabd86d78a26fb9021051fc39b63b649105b (diff) | |
download | gdb-b2175913c5f01e75fdb9b19699049737abb2f37c.zip gdb-b2175913c5f01e75fdb9b19699049737abb2f37c.tar.gz gdb-b2175913c5f01e75fdb9b19699049737abb2f37c.tar.bz2 |
2008-10-17 Michael Snyder <msnyder@vmware.com>
Target interface for reverse debugging.
* target.h (enum target_waitkind):
Add new wait event, TARGET_WAITKIND_NO_HISTORY.
(struct target_ops): New method to_can_execute_reverse.
(target_can_execute_reverse): New macro.
* target.c (update_current_target): Inherit to_can_execute_reverse.
Remote interface for reverse debugging.
* remote.c (remote_can_execute_reverse): New target method.
(remote_resume): Check for reverse exec direction, and send
appropriate command to target.
(remote_wait_as): Check target response for NO_HISTORY status.
Also check for empty reply (target doesn't understand "bs" or "bc).
(remote_vcont_resume): Jump out if attempting reverse execution.
Event handling interface for reverse debugging.
* infrun.c (execution_direction): New state variable.
(enum inferior_stop_reason): Add NO_HISTORY reason.
(handle_inferior_event): Handle TARGET_WAITKIND_NO_HISTORY.
Handle stepping over a function call in reverse.
Handle stepping thru a line range in reverse.
Handle setting a step-resume breakpoint in reverse.
Handle stepping into a function in reverse.
Handle stepping between line ranges in reverse.
(print_stop_reason): Print reason for NO_HISTORY.
(step_into_function): Rename to handle_step_into_function.
(handle_step_into_function_backward): New function.
(set_exec_direction_func, show_exec_direction_func): New funcs.
(proceed): No need to singlestep over a breakpoint
when resuming in reverse.
* inferior.h (enum exec_direction_kind): New enum.
(execution_direction): Export new execution state variable.
* breakpoint.c (make_breakpoint_silent): New function.
* breakpoint.h (make_breakpoint_silent): Export.
* infcmd.c (finish_command): Check for reverse exec direction.
(finish_backward): New function, handle finish cmd in reverse.
User interface for reverse execution.
* Makefile.in (reverse.c): New file.
* reverse.c: New file. User interface for reverse execution.
Diffstat (limited to 'gdb/infrun.c')
-rw-r--r-- | gdb/infrun.c | 214 |
1 files changed, 193 insertions, 21 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c index 150bf4c..5114501 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); |