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/infcmd.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/infcmd.c')
-rw-r--r-- | gdb/infcmd.c | 138 |
1 files changed, 108 insertions, 30 deletions
diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 6ed6341..5179a2f 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); } |