replay: implement replay-seek command

This patch adds hmp/qmp commands replay_seek/replay-seek that proceed
the execution to the specified instruction count.
The command automatically loads nearest snapshot and replays the execution
to find the desired instruction count.

Signed-off-by: Pavel Dovgalyuk <Pavel.Dovgalyuk@ispras.ru>
Acked-by: Markus Armbruster <armbru@redhat.com>

--

v4 changes:
 - fixed HMP command description indent
 - removed useless error_free call
Message-Id: <160174521180.12451.14033112911009278753.stgit@pasha-ThinkPad-X280>

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Pavel Dovgalyuk 2020-10-03 20:13:31 +03:00 committed by Paolo Bonzini
parent e751067179
commit f6baed3d14
4 changed files with 126 additions and 0 deletions

View File

@ -1836,6 +1836,24 @@ SRST
The command is ignored when there are no replay breakpoints. The command is ignored when there are no replay breakpoints.
ERST ERST
{
.name = "replay_seek",
.args_type = "icount:i",
.params = "icount",
.help = "replay execution to the specified instruction count",
.cmd = hmp_replay_seek,
},
SRST
``replay_seek`` *icount*
Automatically proceed to the instruction count *icount*, when
replaying the execution. The command automatically loads nearest
snapshot and replays the execution to find the desired instruction.
When there is no preceding snapshot or the execution is not replayed,
then the command fails.
*icount* for the reference may be observed with ``info replay`` command.
ERST
{ {
.name = "info", .name = "info",
.args_type = "item:s?", .args_type = "item:s?",

View File

@ -132,5 +132,6 @@ void hmp_info_sev(Monitor *mon, const QDict *qdict);
void hmp_info_replay(Monitor *mon, const QDict *qdict); void hmp_info_replay(Monitor *mon, const QDict *qdict);
void hmp_replay_break(Monitor *mon, const QDict *qdict); void hmp_replay_break(Monitor *mon, const QDict *qdict);
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict); void hmp_replay_delete_break(Monitor *mon, const QDict *qdict);
void hmp_replay_seek(Monitor *mon, const QDict *qdict);
#endif #endif

View File

@ -99,3 +99,23 @@
# #
## ##
{ 'command': 'replay-delete-break' } { 'command': 'replay-delete-break' }
##
# @replay-seek:
#
# Automatically proceed to the instruction count @icount, when
# replaying the execution. The command automatically loads nearest
# snapshot and replays the execution to find the desired instruction.
# When there is no preceding snapshot or the execution is not replayed,
# then the command fails.
# icount for the reference may be obtained with @query-replay command.
#
# @icount: target instruction count
#
# Since: 5.2
#
# Example:
#
# -> { "execute": "replay-seek", "data": { "icount": 220414 } }
##
{ 'command': 'replay-seek', 'data': { 'icount': 'int' } }

View File

@ -19,6 +19,8 @@
#include "qapi/qapi-commands-replay.h" #include "qapi/qapi-commands-replay.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "block/snapshot.h"
#include "migration/snapshot.h"
void hmp_info_replay(Monitor *mon, const QDict *qdict) void hmp_info_replay(Monitor *mon, const QDict *qdict)
{ {
@ -125,3 +127,88 @@ void hmp_replay_delete_break(Monitor *mon, const QDict *qdict)
return; return;
} }
} }
static char *replay_find_nearest_snapshot(int64_t icount,
int64_t *snapshot_icount)
{
BlockDriverState *bs;
QEMUSnapshotInfo *sn_tab;
QEMUSnapshotInfo *nearest = NULL;
char *ret = NULL;
int nb_sns, i;
AioContext *aio_context;
*snapshot_icount = -1;
bs = bdrv_all_find_vmstate_bs();
if (!bs) {
goto fail;
}
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
aio_context_release(aio_context);
for (i = 0; i < nb_sns; i++) {
if (bdrv_all_find_snapshot(sn_tab[i].name, &bs) == 0) {
if (sn_tab[i].icount != -1ULL
&& sn_tab[i].icount <= icount
&& (!nearest || nearest->icount < sn_tab[i].icount)) {
nearest = &sn_tab[i];
}
}
}
if (nearest) {
ret = g_strdup(nearest->name);
*snapshot_icount = nearest->icount;
}
g_free(sn_tab);
fail:
return ret;
}
static void replay_seek(int64_t icount, QEMUTimerCB callback, Error **errp)
{
char *snapshot = NULL;
int64_t snapshot_icount;
if (replay_mode != REPLAY_MODE_PLAY) {
error_setg(errp, "replay must be enabled to seek");
return;
}
snapshot = replay_find_nearest_snapshot(icount, &snapshot_icount);
if (snapshot) {
if (icount < replay_get_current_icount()
|| replay_get_current_icount() < snapshot_icount) {
vm_stop(RUN_STATE_RESTORE_VM);
load_snapshot(snapshot, errp);
}
g_free(snapshot);
}
if (replay_get_current_icount() <= icount) {
replay_break(icount, callback, NULL);
vm_start();
} else {
error_setg(errp, "cannot seek to the specified instruction count");
}
}
void qmp_replay_seek(int64_t icount, Error **errp)
{
replay_seek(icount, replay_stop_vm, errp);
}
void hmp_replay_seek(Monitor *mon, const QDict *qdict)
{
int64_t icount = qdict_get_try_int(qdict, "icount", -1LL);
Error *err = NULL;
qmp_replay_seek(icount, &err);
if (err) {
error_report_err(err);
return;
}
}