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 | 17 +++++++++++++++++ qapi-schema.json | 11 +++++++++++ qmp-commands.hx | 23 +++++++++++++++++++++++ replay/replay-internal.h | 3 +++ replay/replay-qmp.c | 15 +++++++++++++++ replay/replay.c | 43 +++++++++++++++++++++++++++++++++++++++++++ replay/replay.h | 4 ++++ 8 files changed, 130 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 4710a59..031decf 100644 --- a/monitor.c +++ b/monitor.c @@ -1205,6 +1205,23 @@ static void do_replay_break(Monitor *mon, const QDict *qdict) } } +static void do_replay_seek(Monitor *mon, const QDict *qdict) +{ + if (replay_mode == REPLAY_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 d0a4651..9d3fda3 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3529,3 +3529,14 @@ # Since: 2.2 ## { 'command': 'replay_break', 'data': { 'step': 'uint64' } } + +## +# @replay_seek +# +# Seeks the replay log to the specified step +# +# @step: destination replay step +# +# Since: 2.2 +## +{ 'command': 'replay_seek', 'data': { 'step': 'uint64' } } diff --git a/qmp-commands.hx b/qmp-commands.hx index 575db76..a144327 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3792,3 +3792,26 @@ 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. + +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 56b6ad9..c7655e7 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 a9f30da..5cb04fb 100755 --- a/replay/replay-qmp.c +++ b/replay/replay-qmp.c @@ -40,3 +40,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_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 d6949d6..e6b3e39 100755 --- a/replay/replay.c +++ b/replay/replay.c @@ -685,3 +685,46 @@ void replay_set_break(uint64_t step) { replay_break_step = 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_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 9a20a3b..356e99b 100755 --- a/replay/replay.h +++ b/replay/replay.h @@ -67,6 +67,10 @@ void replay_init_timer(void); void replay_finish(void); /*! Sets step where execution should be stopped. */ void replay_set_break(uint64_t step); +/*! 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 */