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 | 15 +++++++++++++++ qapi-schema.json | 11 +++++++++++ qmp-commands.hx | 23 +++++++++++++++++++++++ replay/replay-qmp.c | 13 +++++++++++++ replay/replay.c | 24 +++++++++++++++++++++++- replay/replay.h | 2 ++ 7 files changed, 101 insertions(+), 1 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 f336b91..4710a59 100644 --- a/monitor.c +++ b/monitor.c @@ -1190,6 +1190,21 @@ static void do_replay_info(Monitor *mon, const QDict *qdict) } } +static void do_replay_break(Monitor *mon, const QDict *qdict) +{ + if (replay_mode == REPLAY_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 c45f795..d0a4651 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3518,3 +3518,14 @@ # Since: 2.2 ## { 'command': 'replay_info', 'returns': 'ReplayInfo' } + +## +# @replay_break +# +# Sets breakpoint at the specified step of replaying +# +# @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 d475633..575db76 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3769,3 +3769,26 @@ replay_info Shows information about replay process. EQMP + + { + .name = "replay_break", + .args_type = "step:l", + .mhandler.cmd_new = qmp_marshal_input_replay_break, + }, + +SQMP +replay_break +------------ + +Sets breakpoint at the specified replay step. + +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 966bd4d..a9f30da 100755 --- a/replay/replay-qmp.c +++ b/replay/replay-qmp.c @@ -27,3 +27,16 @@ ReplayInfo *qmp_replay_info(Error **errp) return info; } + +void qmp_replay_break(uint64_t step, Error **errp) +{ + if (replay_mode == REPLAY_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 f711c26..d6949d6 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 @@ -284,6 +286,19 @@ void replay_instruction(int process_events) } else if (replay_mode == REPLAY_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_PLAY_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) { @@ -312,7 +327,8 @@ bool replay_has_async_request(void) } if (replay_mode == REPLAY_PLAY) { - if (skip_async_events(EVENT_ASYNC)) { + if (skip_async_events(EVENT_ASYNC) + || replay_get_current_step() == replay_break_step) { return true; } @@ -470,6 +486,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(); @@ -663,3 +680,8 @@ const char *replay_get_play_submode_name(void) return "unknown"; } } + +void replay_set_break(uint64_t step) +{ + replay_break_step = step; +} diff --git a/replay/replay.h b/replay/replay.h index ee0460f..9a20a3b 100755 --- a/replay/replay.h +++ b/replay/replay.h @@ -65,6 +65,8 @@ 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); /* Processing the instructions */