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

@ -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>
* remote.c (record_currthread): Add inferior before child threads.

View File

@ -641,7 +641,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c \
objfiles.c osabi.c observer.c \
p-exp.y p-lang.c p-typeprint.c p-valprint.c parse.c printcmd.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 \
sentinel-frame.c \
serial.c ser-base.c ser-unix.c \
@ -780,7 +780,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
findcmd.o \
std-regs.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 \
dwarf2read.o mipsread.o stabsread.o corefile.o \
dwarf2expr.o dwarf2loc.o dwarf2-frame.o \

View File

@ -7863,6 +7863,13 @@ set_ignore_count (int bptnum, int count, int from_tty)
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. */
static void

View File

@ -897,4 +897,7 @@ extern int breakpoints_always_inserted_mode (void);
in our opinion won't ever trigger. */
extern void breakpoint_retire_moribund (void);
/* Tell a breakpoint to be quiet. */
extern void make_breakpoint_silent (struct breakpoint *);
#endif /* !defined (BREAKPOINT_H) */

View File

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

View File

@ -339,6 +339,16 @@ enum stop_kind
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
situation when stop_registers should be saved. */

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:
@ -3085,7 +3111,21 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\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));
/* 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). */
/* 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). */
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);

View File

@ -3443,7 +3443,15 @@ remote_resume (ptid_t ptid, int step, enum target_signal siggnal)
set_continue_thread (ptid);
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[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;
ULONGEST addr;
int solibs_changed = 0;
int replay_event = 0;
char *buf, *p;
status->kind = TARGET_WAITKIND_IGNORE;
@ -3786,6 +3795,16 @@ Packet: '%s'\n"),
solibs_changed = 1;
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
{
/* Silently skip unknown optional info. */
@ -3831,6 +3850,8 @@ Packet: '%s'\n"),
case 'S': /* Old style status, just signal only. */
if (solibs_changed)
status->kind = TARGET_WAITKIND_LOADED;
else if (replay_event)
status->kind = TARGET_WAITKIND_NO_HISTORY;
else
{
status->kind = TARGET_WAITKIND_STOPPED;
@ -7628,6 +7649,14 @@ remote_command (char *args, int from_tty)
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
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_execution = 1;
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_memory_map = remote_memory_map;
remote_ops.to_flash_erase = remote_flash_erase;

143
gdb/reverse.c Normal file
View 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."));
}

View File

@ -455,6 +455,7 @@ update_current_target (void)
INHERIT (to_find_memory_regions, t);
INHERIT (to_make_corefile_notes, t);
INHERIT (to_get_thread_local_address, t);
INHERIT (to_can_execute_reverse, t);
/* Do not inherit to_read_description. */
/* Do not inherit to_search_memory. */
INHERIT (to_magic, t);

View File

@ -128,7 +128,11 @@ enum target_waitkind
inferior, rather than being stuck in the remote_async_wait()
function. This way the event loop is responsive to other events,
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
@ -523,6 +527,9 @@ struct target_ops
const gdb_byte *pattern, ULONGEST pattern_len,
CORE_ADDR *found_addrp);
/* Can target execute in reverse? */
int (*to_can_execute_reverse) ();
int to_magic;
/* 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) \
(*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 *);
/* Utility implementation of searching memory. */