replay: introduce breakpoint at the specified step

This patch introduces replay_break, replay_delete_break
qmp and hmp commands.
These commands allow stopping at the specified instruction.
It may be useful for debugging when there are some known
events that should be investigated.
replay_break command has one argument - number of instructions
executed since the start of the replay.
replay_delete_break removes previously set breakpoint.

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

--

v4 changes:
 - removed useless error_free call
Message-Id: <160174520606.12451.7056879546045599378.stgit@pasha-ThinkPad-X280>

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

View file

@ -12,10 +12,13 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "sysemu/replay.h"
#include "sysemu/runstate.h"
#include "replay-internal.h"
#include "monitor/hmp.h"
#include "monitor/monitor.h"
#include "qapi/qapi-commands-replay.h"
#include "qapi/qmp/qdict.h"
#include "qemu/timer.h"
void hmp_info_replay(Monitor *mon, const QDict *qdict)
{
@ -41,3 +44,84 @@ ReplayInfo *qmp_query_replay(Error **errp)
retval->icount = replay_get_current_icount();
return retval;
}
static void replay_break(uint64_t icount, QEMUTimerCB callback, void *opaque)
{
assert(replay_mode == REPLAY_MODE_PLAY);
assert(replay_mutex_locked());
assert(replay_break_icount >= replay_get_current_icount());
assert(callback);
replay_break_icount = icount;
if (replay_break_timer) {
timer_del(replay_break_timer);
}
replay_break_timer = timer_new_ns(QEMU_CLOCK_REALTIME,
callback, opaque);
}
static void replay_delete_break(void)
{
assert(replay_mode == REPLAY_MODE_PLAY);
assert(replay_mutex_locked());
if (replay_break_timer) {
timer_del(replay_break_timer);
timer_free(replay_break_timer);
replay_break_timer = NULL;
}
replay_break_icount = -1ULL;
}
static void replay_stop_vm(void *opaque)
{
vm_stop(RUN_STATE_PAUSED);
replay_delete_break();
}
void qmp_replay_break(int64_t icount, Error **errp)
{
if (replay_mode == REPLAY_MODE_PLAY) {
if (icount >= replay_get_current_icount()) {
replay_break(icount, replay_stop_vm, NULL);
} else {
error_setg(errp,
"cannot set breakpoint at the instruction in the past");
}
} else {
error_setg(errp, "setting the breakpoint is allowed only in play mode");
}
}
void hmp_replay_break(Monitor *mon, const QDict *qdict)
{
int64_t icount = qdict_get_try_int(qdict, "icount", -1LL);
Error *err = NULL;
qmp_replay_break(icount, &err);
if (err) {
error_report_err(err);
return;
}
}
void qmp_replay_delete_break(Error **errp)
{
if (replay_mode == REPLAY_MODE_PLAY) {
replay_delete_break();
} else {
error_setg(errp, "replay breakpoints are allowed only in play mode");
}
}
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qmp_replay_delete_break(&err);
if (err) {
error_report_err(err);
return;
}
}

View file

@ -94,6 +94,10 @@ extern ReplayState replay_state;
/* File for replay writing */
extern FILE *replay_file;
/* Instruction count of the replay breakpoint */
extern uint64_t replay_break_icount;
/* Timer for the replay breakpoint callback */
extern QEMUTimer *replay_break_timer;
void replay_put_byte(uint8_t byte);
void replay_put_event(uint8_t event);

View file

@ -34,6 +34,10 @@ static char *replay_filename;
ReplayState replay_state;
static GSList *replay_blockers;
/* Replay breakpoints */
uint64_t replay_break_icount = -1ULL;
QEMUTimer *replay_break_timer;
bool replay_next_event_is(int event)
{
bool res = false;
@ -73,6 +77,13 @@ int replay_get_instructions(void)
replay_mutex_lock();
if (replay_next_event_is(EVENT_INSTRUCTION)) {
res = replay_state.instruction_count;
if (replay_break_icount != -1LL) {
uint64_t current = replay_get_current_icount();
assert(replay_break_icount >= current);
if (current + res > replay_break_icount) {
res = replay_break_icount - current;
}
}
}
replay_mutex_unlock();
return res;
@ -99,6 +110,12 @@ void replay_account_executed_instructions(void)
will be read from the log. */
qemu_notify_event();
}
/* Execution reached the break step */
if (replay_break_icount == replay_state.current_icount) {
/* Cannot make callback directly from the vCPU thread */
timer_mod_ns(replay_break_timer,
qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
}
}
}
}