This patch adds support for replay_break monitor command. This command sets the step (measured in executed instructions) where replay should be stopped.
Signed-off-by: Pavel Dovgalyuk <pavel.dovga...@ispras.ru> --- hmp-commands.hx | 14 ++++++++++++++ monitor.c | 20 ++++++++++++++++++++ qapi-schema.json | 19 ++++++++++++++++++- qmp-commands.hx | 34 +++++++++++++++++++++++++++++++--- replay/replay-qmp.c | 14 ++++++++++++++ replay/replay.c | 29 ++++++++++++++++++++++++++++- replay/replay.h | 4 ++++ 7 files changed, 129 insertions(+), 5 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 19174f1..3eaa80e 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1801,6 +1801,20 @@ STEXI Shows information about replay process. ETEXI + { + .name = "replay_break", + .args_type = "step:l", + .params = "step", + .help = "stop replaying at the specified replay step", + .mhandler.cmd = do_replay_break, + }, + +STEXI +@item replay_break @var{step} +Stops replaying at the specified @var{step}. + +ETEXI + STEXI @end table ETEXI diff --git a/monitor.c b/monitor.c index 61fb11c..1ba9096 100644 --- a/monitor.c +++ b/monitor.c @@ -1184,8 +1184,12 @@ static void do_replay_info(Monitor *mon, const QDict *qdict) monitor_printf(mon, "Replay mode: %s ", ReplayMode_lookup[replay_mode]); if (replay_mode == REPLAY_MODE_PLAY) { + uint64_t bp = replay_get_break_step(); monitor_printf(mon, "(%s)", ReplaySubmode_lookup[replay_get_play_submode()]); + if (bp != -1ULL) { + monitor_printf(mon, "\n\tbreakpoint step: %" PRId64 "\n", bp); + } } monitor_printf(mon, "\n\tcurrent step: %" PRId64 "\n", replay_get_current_step()); @@ -1193,6 +1197,22 @@ static void do_replay_info(Monitor *mon, const QDict *qdict) } } +static void do_replay_break(Monitor *mon, const QDict *qdict) +{ + if (replay_mode == REPLAY_MODE_PLAY) { + uint64_t step = qdict_get_int(qdict, "step"); + if (step >= replay_get_current_step()) { + monitor_printf(mon, "Setting break at step: %" PRId64 "\n", step); + replay_set_break(step); + } else { + monitor_printf(mon, "Cannot stop on the preceding step.\n"); + } + } else { + monitor_printf(mon, "You can stop at the specific step " + "only in PLAY mode.\n"); + } +} + static void monitor_printc(Monitor *mon, int c) { monitor_printf(mon, "'"); diff --git a/qapi-schema.json b/qapi-schema.json index 93a7ff7..e56e399 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3535,10 +3535,13 @@ # # @step: current step of record or play # +# @break-step: step where execution should stop +# # Since: 2.2 ## { 'type': 'ReplayInfo', - 'data': {'mode': 'ReplayMode', 'submode': 'ReplaySubmode', 'step': 'uint64'} } + 'data': { 'mode': 'ReplayMode', 'submode': 'ReplaySubmode', + 'step': 'uint64', 'break-step': 'uint64' } } ## # @replay-info @@ -3550,3 +3553,17 @@ # Since: 2.2 ## { 'command': 'replay-info', 'returns': 'ReplayInfo' } + +## +# @replay-break +# +# Sets breakpoint at the specified step of replaying. +# This command can be used in replay to stop execution when +# the specified step is reached. +# Current step of the execution can be queried with replay-info command. +# +# @step: step where breakpoint should be set +# +# Since: 2.2 +## +{ 'command': 'replay-break', 'data': { 'step': 'uint64' } } diff --git a/qmp-commands.hx b/qmp-commands.hx index 67dd886..712cb5b 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3768,12 +3768,40 @@ replay-info Shows information about replay process. This information includes current execution step (number of executed processor instructions), -replay mode (record, play or none), and replay submode (reverse or -normal execution in play mode). +replay mode (record, play or none), replay submode (reverse or +normal execution in play mode), and breakpoint step. Example: -> { "execute": "replay-info" } -<- { "return": { "step": 651488674, "mode": "record", "submode": "unknown" } } +<- { "return": { "step": 651488674, + "mode": "record", "submode": "unknown" + "break-step": 651490000 } } + +EQMP + + { + .name = "replay-break", + .args_type = "step:l", + .mhandler.cmd_new = qmp_marshal_input_replay_break, + }, + +SQMP +replay-break +------------ + +Sets breakpoint at the specified step of replaying. +This command can be used in replay to stop execution when +the specified step is reached. +Current step of the execution can be queried with replay-info command. + +Arguments: + +- "step": step where breakpoint should be set + +Example: + +-> { "execute": "replay-break", "arguments": { "step": 1024 } } +<- { "return": {} } EQMP diff --git a/replay/replay-qmp.c b/replay/replay-qmp.c index 4a0017d..f48b816 100755 --- a/replay/replay-qmp.c +++ b/replay/replay-qmp.c @@ -24,6 +24,20 @@ ReplayInfo *qmp_replay_info(Error **errp) info->mode = replay_mode; info->submode = replay_get_play_submode(); info->step = replay_get_current_step(); + info->break_step = replay_get_break_step(); return info; } + +void qmp_replay_break(uint64_t step, Error **errp) +{ + if (replay_mode == REPLAY_MODE_PLAY) { + if (step >= replay_get_current_step()) { + replay_set_break(step); + } else { + error_setg(errp, "Cannot stop on the preceding step"); + } + } else { + error_setg(errp, "replay_break can be used only in PLAY mode"); + } +} diff --git a/replay/replay.c b/replay/replay.c index 4151330..fac5ecd 100755 --- a/replay/replay.c +++ b/replay/replay.c @@ -31,6 +31,8 @@ static char *replay_filename; char *replay_image_suffix; ReplayState replay_state; +/*! Step for stopping execution at. */ +static uint64_t replay_break_step = -1; /* Auto-saving for VM states data @@ -285,6 +287,19 @@ void replay_instruction(int process_events) } else if (replay_mode == REPLAY_MODE_PLAY) { skip_async_events_until(EVENT_INSTRUCTION); if (first_cpu->instructions_count >= 1) { + if (replay_get_current_step() == replay_break_step) { + replay_break_step = -1; + + /* for stopping VM */ + if (play_submode == REPLAY_SUBMODE_NORMAL) { + first_cpu->exception_index = EXCP_DEBUG; + monitor_printf(default_mon, "Execution has stopped.\n"); + vm_stop(EXCP_DEBUG); + } + /* for breaking execution loop */ + cpu_exit(first_cpu); + return; + } ++replay_state.current_step; --first_cpu->instructions_count; if (first_cpu->instructions_count == 0) { @@ -313,7 +328,8 @@ bool replay_has_async_request(void) } if (replay_mode == REPLAY_MODE_PLAY) { - if (skip_async_events(EVENT_ASYNC)) { + if (skip_async_events(EVENT_ASYNC) + || replay_get_current_step() == replay_break_step) { return true; } @@ -471,6 +487,7 @@ static void replay_enable(const char *fname, int mode) replay_state.skipping_instruction = 0; replay_state.current_step = 0; current_saved_state = 0; + replay_break_step = -1; replay_net_init(); @@ -643,3 +660,13 @@ void replay_data_buffer(unsigned char *data, size_t size) replay_put_array(data, size); } } + +void replay_set_break(uint64_t step) +{ + replay_break_step = step; +} + +uint64_t replay_get_break_step(void) +{ + return replay_break_step; +} diff --git a/replay/replay.h b/replay/replay.h index 590f9e6..e737eba 100755 --- a/replay/replay.h +++ b/replay/replay.h @@ -53,6 +53,10 @@ void replay_configure(struct QemuOpts *opts, int mode); void replay_init_timer(void); /*! Closes replay log file and frees other resources. */ void replay_finish(void); +/*! Sets step where execution should be stopped. */ +void replay_set_break(uint64_t step); +/*! Retrieves current breakpoint step. */ +uint64_t replay_get_break_step(void); /* Processing the instructions */