[RFC] reverse-step, reverse-next

Michael Snyder
2005-09-07T22:44:53Z

This isn't for submission, just for discussion. This is something
that Johan Rydberg (of Virtutech) and I have been working on.

I'd like to hear what everybody thinks about this
bit of infrun implementation for the reverse debugging
that we discussed a few months ago.

This part is enough to get step and next to work in reverse,
based solely on the assumption that the backend (or someone)
provides an interface "get_exec_direction ()", which returns
forward or reverse. It's also assumed that the backend will
know which direction to go (leaving user-interface issues
out of the picture). One can imagine either a "set direction"
interface, or a "reverse-step/reverse-continue".

This is actually tested and working with the Simics simulator.
It steps and nexts backwards like a champ.

The only other bit of explanation that might be required
is that "NO_HISTORY" means you were going backward and
the target ran out of state data (you reached the beginning
of time).

Index: infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.178
diff -p -r1.178 infrun.c
*** infrun.c 27 Sep 2004 17:58:08 -0000 1.178
--- infrun.c 5 Sep 2005 00:38:53 -0000
*************** enum inferior_stop_reason
*** 897,903 ****
/* Inferior exited. */
EXITED,
/* Inferior received signal, and user asked to be notified. */
! SIGNAL_RECEIVED
};

/* This structure contains what used to be local variables in
--- 897,905 ----
/* Inferior exited. */
EXITED,
/* Inferior received signal, and user asked to be notified. */
! SIGNAL_RECEIVED,
! /* Reverse execution -- target ran out of history (FIXME: general?) */
! NO_HISTORY
};

/* This structure contains what used to be local variables in
*************** handle_inferior_event (struct execution_
*** 1516,1521 ****
--- 1518,1528 ----
stop_signal = ecs->ws.value.sig;
break;

+ case TARGET_WAITKIND_NO_HISTORY:
+ 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.
*************** process_event_stop_test:
*** 2073,2078 ****
--- 2080,2096 ----
keep_going (ecs);
return;
}
+ if (stop_pc == ecs->stop_func_start &&
+ get_exec_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:
*************** process_event_stop_test:
*** 2237,2243 ****
within it! */
if (stop_pc >= step_range_start && stop_pc < step_range_end)
{
! keep_going (ecs);
return;
}

--- 2255,2272 ----
within it! */
if (stop_pc >= step_range_start && stop_pc < step_range_end)
{
! if (stop_pc == step_range_start &&
! get_exec_direction () == EXEC_REVERSE)
! {
! /* When stepping backward, stop at beginning of line range. */
! stop_step = 1;
! print_stop_reason (END_STEPPING_RANGE, 0);
! stop_stepping (ecs);
! }
! else
! {
! keep_going (ecs);
! }
return;
}

*************** process_event_stop_test:
*** 2319,2335 ****
/* 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_frame (get_prev_frame
(get_current_f\rame ()));
keep_going (ecs);
return;
}
#endif
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_frame (get_prev_frame
(get_current_f\rame ()));
keep_going (ecs);
return;
}
--- 2348,2388 ----
/* 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_frame
! (get_prev_frame (get_current_frame ()));
keep_going (ecs);
return;
}
#endif
if (step_over_calls == STEP_OVER_ALL)
{
! /* 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 (get_exec_direction () == EXEC_FORWARD)
! {
! insert_step_resume_breakpoint_at_frame
! (get_prev_frame (get_current_frame ()));
! }
! else
! {
! /* 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);
! }
keep_going (ecs);
return;
}
*************** process_event_stop_test:
*** 2384,2392 ****
return;
}

! /* Set a breakpoint at callee's return address (the address at
! which the caller will resume). */
! insert_step_resume_breakpoint_at_frame (get_prev_frame
(get_current_fra\me ()));
keep_going (ecs);
return;
}
--- 2437,2459 ----
return;
}

! if (get_exec_direction () == EXEC_FORWARD)
! {
! /* Set a breakpoint at callee's return address (the address
! at which the caller will resume). */
! insert_step_resume_breakpoint_at_frame
! (get_prev_frame (get_current_frame ()));
! }
! else
! {
! /* 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);
! }
keep_going (ecs);
return;
}
*************** step_into_function (struct execution_con
*** 2568,2574 ****
--- 2635,2668 ----
if (s && s->language != language_asm)
ecs->stop_func_start = SKIP_PROLOGUE (ecs->stop_func_start);

+ /* If we're going backward, we just stepped into the
+ return of a function. Instead of continuing thru
+ the prologue, we want to continue back thru the epilogue. */
+ /* FIXME: Should we just split this out into a separate fn? */
+ if (get_exec_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
4.2). */
*************** step_into_function (struct execution_con
*** 2615,2620 ****
--- 2709,2715 ----
{
/* Put the step-breakpoint there and go until there. */
init_sal (&sr_sal); /* initialize to zeroes */
+
sr_sal.pc = ecs->stop_func_start;
sr_sal.section = find_pc_overlay (ecs->stop_func_start);

*************** print_stop_reason (enum inferior_stop_re
*** 2855,2860 ****
annotate_signal_string_end ();
ui_out_text (uiout, ".\n");
break;
+ case NO_HISTORY:
+ /* Reverse execution: target ran out of history data. */
+ ui_out_text (uiout, "\nNo more reverse-execution history.\n");
+ break;
default:
internal_error (__FILE__, __LINE__,
"print_stop_reason: unrecognized enum value");