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.
This commit is contained in:
Michael Snyder
2008-10-17 19:43:47 +00:00
parent 153ccabd86
commit b2175913c5
11 changed files with 556 additions and 55 deletions

View File

@@ -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);