This patch adds support for replay_seek_step monitor command. This command loads one of the snapshots and replays the execution until the specified step is met.
Signed-off-by: Pavel Dovgalyuk <pavel.dovga...@ispras.ru> --- hmp-commands.hx | 14 ++++++++++++++ monitor.c | 18 ++++++++++++++++++ qapi-schema.json | 13 +++++++++++++ qmp-commands.hx | 25 +++++++++++++++++++++++++ replay/replay-internal.h | 3 +++ replay/replay-qmp.c | 15 +++++++++++++++ replay/replay.c | 43 +++++++++++++++++++++++++++++++++++++++++++ replay/replay.h | 4 ++++ 8 files changed, 135 insertions(+), 0 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 3eaa80e..57ce5da 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1815,6 +1815,20 @@ Stops replaying at the specified @var{step}. ETEXI + { + .name = "replay_seek", + .args_type = "step:l", + .params = "step", + .help = "seeks the replay log to the specified step", + .mhandler.cmd = do_replay_seek, + }, + +STEXI +@item replay_seek @var{step} +Seeks the replay log to the specified @var{step}. + +ETEXI + STEXI @end table ETEXI diff --git a/monitor.c b/monitor.c index 1ba9096..fdbd5a6 100644 --- a/monitor.c +++ b/monitor.c @@ -1213,6 +1213,24 @@ static void do_replay_break(Monitor *mon, const QDict *qdict) } } +static void do_replay_seek(Monitor *mon, const QDict *qdict) +{ + if (replay_mode == REPLAY_MODE_PLAY) { + uint64_t step = qdict_get_int(qdict, "step"); + if (replay_seek_step(step)) { + monitor_printf(mon, "Seeking for step: %" PRId64 "\n", step); + if (!runstate_is_running()) { + vm_start(); + } + } else { + monitor_printf(mon, "Cannot seek to the specified step.\n"); + } + } else { + monitor_printf(mon, "You can go to 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 e56e399..d8755a3 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3567,3 +3567,16 @@ # Since: 2.2 ## { 'command': 'replay-break', 'data': { 'step': 'uint64' } } + +## +# @replay-seek +# +# Seeks the replay log to the specified step. +# Loads nearest snapshot and replays the execution until +# the destination step is reached, +# +# @step: destination replay step +# +# Since: 2.2 +## +{ 'command': 'replay-seek', 'data': { 'step': 'uint64' } } diff --git a/qmp-commands.hx b/qmp-commands.hx index 712cb5b..cf642fa 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3805,3 +3805,28 @@ Example: <- { "return": {} } EQMP + + { + .name = "replay-seek", + .args_type = "step:l", + .mhandler.cmd_new = qmp_marshal_input_replay_seek, + }, + +SQMP +replay-seek +----------- + +Seeks the replay log to the specified step. +Loads nearest snapshot and replays the execution until +the destination step is reached, + +Arguments: + +- "step": destination replay step + +Example: + +-> { "execute": "replay-seek", "arguments": { "step": 1024 } } +<- { "return": {} } + +EQMP diff --git a/replay/replay-internal.h b/replay/replay-internal.h index eeab40c..e529e9a 100755 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -135,6 +135,9 @@ void skip_async_events_until(unsigned int kind); If the parameter is -1, the clock value is read to the cache anyway. */ void replay_read_next_clock(unsigned int kind); +/*! Finds saved state info which is nearest before the specified step. */ +SavedStateInfo *find_nearest_state(uint64_t step); + /* Asynchronous events queue */ /*! Initializes events' processing internals */ diff --git a/replay/replay-qmp.c b/replay/replay-qmp.c index f48b816..04a20d5 100755 --- a/replay/replay-qmp.c +++ b/replay/replay-qmp.c @@ -41,3 +41,18 @@ void qmp_replay_break(uint64_t step, Error **errp) error_setg(errp, "replay_break can be used only in PLAY mode"); } } + +void qmp_replay_seek(uint64_t step, Error **errp) +{ + if (replay_mode == REPLAY_MODE_PLAY) { + if (replay_seek_step(step)) { + if (!runstate_is_running()) { + vm_start(); + } + } else { + error_setg(errp, "Cannot seek to the specified step"); + } + } else { + error_setg(errp, "replay_seek can be used only in PLAY mode"); + } +} diff --git a/replay/replay.c b/replay/replay.c index fac5ecd..eaaee78 100755 --- a/replay/replay.c +++ b/replay/replay.c @@ -670,3 +670,46 @@ uint64_t replay_get_break_step(void) { return replay_break_step; } + +SavedStateInfo *find_nearest_state(uint64_t step) +{ + SavedStateInfo *first = saved_states; + SavedStateInfo *last = saved_states + saved_states_count; + while (first < last - 1) { + SavedStateInfo *next = first + (last - first) / 2; + if (next->step > step) { + last = next; + } else { + first = next; + } + } + + return first; +} + +int replay_seek_step(uint64_t step) +{ + if (replay_mode != REPLAY_MODE_PLAY) { + return 0; + } + + /* load VM state, if possible */ + if (saved_states_count > 0) { + /* find VM state to load */ + SavedStateInfo *first = find_nearest_state(step); + + if (first->step <= step + && (replay_get_current_step() > step + || replay_get_current_step() < first->step)) { + replay_loadvm(first - saved_states); + } + } + + /* setup the breakpoint */ + if (step >= replay_get_current_step()) { + replay_set_break(step); + return 1; + } + + return 0; +} diff --git a/replay/replay.h b/replay/replay.h index e737eba..70f8d84 100755 --- a/replay/replay.h +++ b/replay/replay.h @@ -57,6 +57,10 @@ void replay_finish(void); void replay_set_break(uint64_t step); /*! Retrieves current breakpoint step. */ uint64_t replay_get_break_step(void); +/*! Seeks to the specified step. + Loads VM state, if possible, and sets break to specified step. + Returns nonzero, when seeking was successful. */ +int replay_seek_step(uint64_t step); /* Processing the instructions */