mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-27 22:48:57 +08:00
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:
@ -1,3 +1,47 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
2008-10-17 Pedro Alves <pedro@codesourcery.com>
|
2008-10-17 Pedro Alves <pedro@codesourcery.com>
|
||||||
|
|
||||||
* remote.c (record_currthread): Add inferior before child threads.
|
* remote.c (record_currthread): Add inferior before child threads.
|
||||||
|
@ -641,7 +641,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \
|
|||||||
objfiles.c osabi.c observer.c \
|
objfiles.c osabi.c observer.c \
|
||||||
p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.c \
|
p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.c \
|
||||||
prologue-value.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 \
|
scm-exp.c scm-lang.c scm-valprint.c \
|
||||||
sentinel-frame.c \
|
sentinel-frame.c \
|
||||||
serial.c ser-base.c ser-unix.c \
|
serial.c ser-base.c ser-unix.c \
|
||||||
@ -780,7 +780,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
|
|||||||
findcmd.o \
|
findcmd.o \
|
||||||
std-regs.o \
|
std-regs.o \
|
||||||
signals.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 \
|
dbxread.o coffread.o coff-pe-read.o \
|
||||||
dwarf2read.o mipsread.o stabsread.o corefile.o \
|
dwarf2read.o mipsread.o stabsread.o corefile.o \
|
||||||
dwarf2expr.o dwarf2loc.o dwarf2-frame.o \
|
dwarf2expr.o dwarf2loc.o dwarf2-frame.o \
|
||||||
|
@ -7863,6 +7863,13 @@ set_ignore_count (int bptnum, int count, int from_tty)
|
|||||||
error (_("No breakpoint number %d."), bptnum);
|
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. */
|
/* Command to set ignore-count of breakpoint N to COUNT. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -897,4 +897,7 @@ extern int breakpoints_always_inserted_mode (void);
|
|||||||
in our opinion won't ever trigger. */
|
in our opinion won't ever trigger. */
|
||||||
extern void breakpoint_retire_moribund (void);
|
extern void breakpoint_retire_moribund (void);
|
||||||
|
|
||||||
|
/* Tell a breakpoint to be quiet. */
|
||||||
|
extern void make_breakpoint_silent (struct breakpoint *);
|
||||||
|
|
||||||
#endif /* !defined (BREAKPOINT_H) */
|
#endif /* !defined (BREAKPOINT_H) */
|
||||||
|
138
gdb/infcmd.c
138
gdb/infcmd.c
@ -1366,19 +1366,109 @@ finish_command_continuation_free_arg (void *arg)
|
|||||||
xfree (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
|
/* "finish": Set a temporary breakpoint at the place the selected
|
||||||
frame will return to, then continue. */
|
frame will return to, then continue. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
finish_command (char *arg, int from_tty)
|
finish_command (char *arg, int from_tty)
|
||||||
{
|
{
|
||||||
struct symtab_and_line sal;
|
|
||||||
struct frame_info *frame;
|
struct frame_info *frame;
|
||||||
struct symbol *function;
|
struct symbol *function;
|
||||||
struct breakpoint *breakpoint;
|
|
||||||
struct cleanup *old_chain;
|
|
||||||
struct finish_command_continuation_args *cargs;
|
|
||||||
struct thread_info *tp;
|
|
||||||
|
|
||||||
int async_exec = 0;
|
int async_exec = 0;
|
||||||
|
|
||||||
@ -1391,6 +1481,10 @@ finish_command (char *arg, int from_tty)
|
|||||||
if (async_exec && !target_can_async_p ())
|
if (async_exec && !target_can_async_p ())
|
||||||
error (_("Asynchronous execution not supported on this target."));
|
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
|
/* If we are not asked to run in the bg, then prepare to run in the
|
||||||
foreground, synchronously. */
|
foreground, synchronously. */
|
||||||
if (!async_exec && target_can_async_p ())
|
if (!async_exec && target_can_async_p ())
|
||||||
@ -1408,17 +1502,8 @@ finish_command (char *arg, int from_tty)
|
|||||||
if (frame == 0)
|
if (frame == 0)
|
||||||
error (_("\"finish\" not meaningful in the outermost frame."));
|
error (_("\"finish\" not meaningful in the outermost frame."));
|
||||||
|
|
||||||
tp = inferior_thread ();
|
|
||||||
|
|
||||||
clear_proceed_status ();
|
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. */
|
/* Find the function we will return from. */
|
||||||
|
|
||||||
function = find_pc_function (get_frame_pc (get_selected_frame (NULL)));
|
function = find_pc_function (get_frame_pc (get_selected_frame (NULL)));
|
||||||
@ -1427,25 +1512,18 @@ finish_command (char *arg, int from_tty)
|
|||||||
source. */
|
source. */
|
||||||
if (from_tty)
|
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);
|
print_stack_frame (get_selected_frame (NULL), 1, LOCATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
tp->proceed_to_finish = 1; /* We want stop_registers, please... */
|
if (execution_direction == EXEC_REVERSE)
|
||||||
make_cleanup_restore_integer (&suppress_stop_observer);
|
finish_backward (function);
|
||||||
suppress_stop_observer = 1;
|
else
|
||||||
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
|
finish_forward (function, frame);
|
||||||
|
|
||||||
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 ();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -339,6 +339,16 @@ enum stop_kind
|
|||||||
STOP_QUIETLY_NO_SIGSTOP
|
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
|
/* Nonzero if proceed is being used for a "finish" command or a similar
|
||||||
situation when stop_registers should be saved. */
|
situation when stop_registers should be saved. */
|
||||||
|
|
||||||
|
214
gdb/infrun.c
214
gdb/infrun.c
@ -1242,11 +1242,17 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step)
|
|||||||
|
|
||||||
if (addr == (CORE_ADDR) -1)
|
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,
|
/* There is a breakpoint at the address we will resume at,
|
||||||
step one instruction before inserting breakpoints so that
|
step one instruction before inserting breakpoints so that
|
||||||
we do not stop right away (and report a second hit at this
|
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;
|
oneproc = 1;
|
||||||
else if (gdbarch_single_step_through_delay_p (gdbarch)
|
else if (gdbarch_single_step_through_delay_p (gdbarch)
|
||||||
&& gdbarch_single_step_through_delay (gdbarch,
|
&& gdbarch_single_step_through_delay (gdbarch,
|
||||||
@ -1475,7 +1481,9 @@ enum inferior_stop_reason
|
|||||||
/* Inferior exited. */
|
/* Inferior exited. */
|
||||||
EXITED,
|
EXITED,
|
||||||
/* Inferior received signal, and user asked to be notified. */
|
/* 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.*/
|
/* 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);
|
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_frame (struct frame_info *step_frame);
|
||||||
static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
|
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,
|
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;
|
ecs->event_thread->stop_signal = ecs->ws.value.sig;
|
||||||
break;
|
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
|
/* We had an event in the inferior, but we are not interested
|
||||||
in handling it at this level. The lower layers have already
|
in handling it at this level. The lower layers have already
|
||||||
done what needs to be done, if anything.
|
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);
|
keep_going (ecs);
|
||||||
return;
|
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;
|
break;
|
||||||
|
|
||||||
case BPSTAT_WHAT_CHECK_SHLIBS:
|
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)
|
&& stop_pc < ecs->event_thread->step_range_end)
|
||||||
{
|
{
|
||||||
if (debug_infrun)
|
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_start),
|
||||||
paddr_nz (ecs->event_thread->step_range_end));
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3145,8 +3185,9 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
|
|||||||
previous frame must have valid frame IDs. */
|
previous frame must have valid frame IDs. */
|
||||||
if (!frame_id_eq (get_frame_id (get_current_frame ()),
|
if (!frame_id_eq (get_frame_id (get_current_frame ()),
|
||||||
ecs->event_thread->step_frame_id)
|
ecs->event_thread->step_frame_id)
|
||||||
&& frame_id_eq (frame_unwind_id (get_current_frame ()),
|
&& (frame_id_eq (frame_unwind_id (get_current_frame ()),
|
||||||
ecs->event_thread->step_frame_id))
|
ecs->event_thread->step_frame_id)
|
||||||
|
|| execution_direction == EXEC_REVERSE))
|
||||||
{
|
{
|
||||||
CORE_ADDR real_stop_pc;
|
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)
|
if (ecs->event_thread->step_over_calls == STEP_OVER_ALL)
|
||||||
{
|
{
|
||||||
/* We're doing a "next", set a breakpoint at callee's return
|
/* We're doing a "next".
|
||||||
address (the address at which the caller will
|
|
||||||
resume). */
|
Normal (forward) execution: set a breakpoint at the
|
||||||
insert_step_resume_breakpoint_at_caller (get_current_frame ());
|
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);
|
keep_going (ecs);
|
||||||
return;
|
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);
|
tmp_sal = find_pc_line (ecs->stop_func_start, 0);
|
||||||
if (tmp_sal.line != 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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3232,9 +3294,20 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set a breakpoint at callee's return address (the address at
|
if (execution_direction == EXEC_REVERSE)
|
||||||
which the caller will resume). */
|
{
|
||||||
insert_step_resume_breakpoint_at_caller (get_current_frame ());
|
/* 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);
|
keep_going (ecs);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -3386,19 +3459,20 @@ currently_stepping (struct thread_info *tp)
|
|||||||
|| bpstat_should_step ());
|
|| bpstat_should_step ());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subroutine call with source code we should not step over. Do step
|
/* Inferior has stepped into a subroutine call with source code that
|
||||||
to the first line of code in it. */
|
we should not step over. Do step to the first line of code in
|
||||||
|
it. */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
step_into_function (struct execution_control_state *ecs)
|
handle_step_into_function (struct execution_control_state *ecs)
|
||||||
{
|
{
|
||||||
struct symtab *s;
|
struct symtab *s;
|
||||||
struct symtab_and_line stop_func_sal, sr_sal;
|
struct symtab_and_line stop_func_sal, sr_sal;
|
||||||
|
|
||||||
s = find_pc_symtab (stop_pc);
|
s = find_pc_symtab (stop_pc);
|
||||||
if (s && s->language != language_asm)
|
if (s && s->language != language_asm)
|
||||||
ecs->stop_func_start = gdbarch_skip_prologue
|
ecs->stop_func_start = gdbarch_skip_prologue (current_gdbarch,
|
||||||
(current_gdbarch, ecs->stop_func_start);
|
ecs->stop_func_start);
|
||||||
|
|
||||||
stop_func_sal = find_pc_line (ecs->stop_func_start, 0);
|
stop_func_sal = find_pc_line (ecs->stop_func_start, 0);
|
||||||
/* Use the step_resume_break to step until the end of the prologue,
|
/* 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);
|
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.
|
/* Insert a "step-resume breakpoint" at SR_SAL with frame ID SR_ID.
|
||||||
This is used to both functions and to skip over code. */
|
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 ();
|
annotate_signal_string_end ();
|
||||||
ui_out_text (uiout, ".\n");
|
ui_out_text (uiout, ".\n");
|
||||||
break;
|
break;
|
||||||
|
case NO_HISTORY:
|
||||||
|
/* Reverse execution: target ran out of history info. */
|
||||||
|
ui_out_text (uiout, "\nNo more reverse-execution history.\n");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
internal_error (__FILE__, __LINE__,
|
internal_error (__FILE__, __LINE__,
|
||||||
_("print_stop_reason: unrecognized enum value"));
|
_("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;
|
int non_stop = 0;
|
||||||
static int non_stop_1 = 0;
|
static int non_stop_1 = 0;
|
||||||
|
|
||||||
@ -4924,6 +5088,14 @@ breakpoints, even if such is supported by the target."),
|
|||||||
&maintenance_set_cmdlist,
|
&maintenance_set_cmdlist,
|
||||||
&maintenance_show_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 */
|
/* ptid initializations */
|
||||||
null_ptid = ptid_build (0, 0, 0);
|
null_ptid = ptid_build (0, 0, 0);
|
||||||
minus_one_ptid = ptid_build (-1, 0, 0);
|
minus_one_ptid = ptid_build (-1, 0, 0);
|
||||||
|
32
gdb/remote.c
32
gdb/remote.c
@ -3443,7 +3443,15 @@ remote_resume (ptid_t ptid, int step, enum target_signal siggnal)
|
|||||||
set_continue_thread (ptid);
|
set_continue_thread (ptid);
|
||||||
|
|
||||||
buf = rs->buf;
|
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[0] = step ? 'S' : 'C';
|
||||||
buf[1] = tohex (((int) siggnal >> 4) & 0xf);
|
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;
|
ptid_t event_ptid = null_ptid;
|
||||||
ULONGEST addr;
|
ULONGEST addr;
|
||||||
int solibs_changed = 0;
|
int solibs_changed = 0;
|
||||||
|
int replay_event = 0;
|
||||||
char *buf, *p;
|
char *buf, *p;
|
||||||
|
|
||||||
status->kind = TARGET_WAITKIND_IGNORE;
|
status->kind = TARGET_WAITKIND_IGNORE;
|
||||||
@ -3786,6 +3795,16 @@ Packet: '%s'\n"),
|
|||||||
solibs_changed = 1;
|
solibs_changed = 1;
|
||||||
p = p_temp;
|
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
|
else
|
||||||
{
|
{
|
||||||
/* Silently skip unknown optional info. */
|
/* Silently skip unknown optional info. */
|
||||||
@ -3831,6 +3850,8 @@ Packet: '%s'\n"),
|
|||||||
case 'S': /* Old style status, just signal only. */
|
case 'S': /* Old style status, just signal only. */
|
||||||
if (solibs_changed)
|
if (solibs_changed)
|
||||||
status->kind = TARGET_WAITKIND_LOADED;
|
status->kind = TARGET_WAITKIND_LOADED;
|
||||||
|
else if (replay_event)
|
||||||
|
status->kind = TARGET_WAITKIND_NO_HISTORY;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
status->kind = TARGET_WAITKIND_STOPPED;
|
status->kind = TARGET_WAITKIND_STOPPED;
|
||||||
@ -7628,6 +7649,14 @@ remote_command (char *args, int from_tty)
|
|||||||
help_list (remote_cmdlist, "remote ", -1, gdb_stdout);
|
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
|
static void
|
||||||
init_remote_ops (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_registers = 1;
|
||||||
remote_ops.to_has_execution = 1;
|
remote_ops.to_has_execution = 1;
|
||||||
remote_ops.to_has_thread_control = tc_schedlock; /* can lock scheduler */
|
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_magic = OPS_MAGIC;
|
||||||
remote_ops.to_memory_map = remote_memory_map;
|
remote_ops.to_memory_map = remote_memory_map;
|
||||||
remote_ops.to_flash_erase = remote_flash_erase;
|
remote_ops.to_flash_erase = remote_flash_erase;
|
||||||
|
143
gdb/reverse.c
Normal file
143
gdb/reverse.c
Normal file
@ -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."));
|
||||||
|
}
|
@ -455,6 +455,7 @@ update_current_target (void)
|
|||||||
INHERIT (to_find_memory_regions, t);
|
INHERIT (to_find_memory_regions, t);
|
||||||
INHERIT (to_make_corefile_notes, t);
|
INHERIT (to_make_corefile_notes, t);
|
||||||
INHERIT (to_get_thread_local_address, t);
|
INHERIT (to_get_thread_local_address, t);
|
||||||
|
INHERIT (to_can_execute_reverse, t);
|
||||||
/* Do not inherit to_read_description. */
|
/* Do not inherit to_read_description. */
|
||||||
/* Do not inherit to_search_memory. */
|
/* Do not inherit to_search_memory. */
|
||||||
INHERIT (to_magic, t);
|
INHERIT (to_magic, t);
|
||||||
|
14
gdb/target.h
14
gdb/target.h
@ -128,7 +128,11 @@ enum target_waitkind
|
|||||||
inferior, rather than being stuck in the remote_async_wait()
|
inferior, rather than being stuck in the remote_async_wait()
|
||||||
function. This way the event loop is responsive to other events,
|
function. This way the event loop is responsive to other events,
|
||||||
like for instance the user typing. */
|
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
|
struct target_waitstatus
|
||||||
@ -523,6 +527,9 @@ struct target_ops
|
|||||||
const gdb_byte *pattern, ULONGEST pattern_len,
|
const gdb_byte *pattern, ULONGEST pattern_len,
|
||||||
CORE_ADDR *found_addrp);
|
CORE_ADDR *found_addrp);
|
||||||
|
|
||||||
|
/* Can target execute in reverse? */
|
||||||
|
int (*to_can_execute_reverse) ();
|
||||||
|
|
||||||
int to_magic;
|
int to_magic;
|
||||||
/* Need sub-structure for target machine related rather than comm related?
|
/* 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) \
|
#define target_watchpoint_addr_within_range(target, addr, start, length) \
|
||||||
(*target.to_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 *);
|
extern const struct target_desc *target_read_description (struct target_ops *);
|
||||||
|
|
||||||
/* Utility implementation of searching memory. */
|
/* Utility implementation of searching memory. */
|
||||||
|
Reference in New Issue
Block a user