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:
parent
e3b09ad2b6
commit
e751067179
@ -1804,6 +1804,38 @@ SRST
|
|||||||
Set QOM property *property* of object at location *path* to value *value*
|
Set QOM property *property* of object at location *path* to value *value*
|
||||||
ERST
|
ERST
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "replay_break",
|
||||||
|
.args_type = "icount:i",
|
||||||
|
.params = "icount",
|
||||||
|
.help = "set breakpoint at the specified instruction count",
|
||||||
|
.cmd = hmp_replay_break,
|
||||||
|
},
|
||||||
|
|
||||||
|
SRST
|
||||||
|
``replay_break`` *icount*
|
||||||
|
Set replay breakpoint at instruction count *icount*.
|
||||||
|
Execution stops when the specified instruction is reached.
|
||||||
|
There can be at most one breakpoint. When breakpoint is set, any prior
|
||||||
|
one is removed. The breakpoint may be set only in replay mode and only
|
||||||
|
"in the future", i.e. at instruction counts greater than the current one.
|
||||||
|
The current instruction count can be observed with ``info replay``.
|
||||||
|
ERST
|
||||||
|
|
||||||
|
{
|
||||||
|
.name = "replay_delete_break",
|
||||||
|
.args_type = "",
|
||||||
|
.params = "",
|
||||||
|
.help = "remove replay breakpoint",
|
||||||
|
.cmd = hmp_replay_delete_break,
|
||||||
|
},
|
||||||
|
|
||||||
|
SRST
|
||||||
|
``replay_delete_break``
|
||||||
|
Remove replay breakpoint which was previously set with ``replay_break``.
|
||||||
|
The command is ignored when there are no replay breakpoints.
|
||||||
|
ERST
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "info",
|
.name = "info",
|
||||||
.args_type = "item:s?",
|
.args_type = "item:s?",
|
||||||
|
@ -130,5 +130,7 @@ void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict);
|
|||||||
void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict);
|
void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict);
|
||||||
void hmp_info_sev(Monitor *mon, const QDict *qdict);
|
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_delete_break(Monitor *mon, const QDict *qdict);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -63,3 +63,39 @@
|
|||||||
##
|
##
|
||||||
{ 'command': 'query-replay',
|
{ 'command': 'query-replay',
|
||||||
'returns': 'ReplayInfo' }
|
'returns': 'ReplayInfo' }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @replay-break:
|
||||||
|
#
|
||||||
|
# Set replay breakpoint at instruction count @icount.
|
||||||
|
# Execution stops when the specified instruction is reached.
|
||||||
|
# There can be at most one breakpoint. When breakpoint is set, any prior
|
||||||
|
# one is removed. The breakpoint may be set only in replay mode and only
|
||||||
|
# "in the future", i.e. at instruction counts greater than the current one.
|
||||||
|
# The current instruction count can be observed with @query-replay.
|
||||||
|
#
|
||||||
|
# @icount: instruction count to stop at
|
||||||
|
#
|
||||||
|
# Since: 5.2
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# -> { "execute": "replay-break", "data": { "icount": 220414 } }
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'command': 'replay-break', 'data': { 'icount': 'int' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @replay-delete-break:
|
||||||
|
#
|
||||||
|
# Remove replay breakpoint which was set with @replay-break.
|
||||||
|
# The command is ignored when there are no replay breakpoints.
|
||||||
|
#
|
||||||
|
# Since: 5.2
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
#
|
||||||
|
# -> { "execute": "replay-delete-break" }
|
||||||
|
#
|
||||||
|
##
|
||||||
|
{ 'command': 'replay-delete-break' }
|
||||||
|
@ -12,10 +12,13 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "sysemu/replay.h"
|
#include "sysemu/replay.h"
|
||||||
|
#include "sysemu/runstate.h"
|
||||||
#include "replay-internal.h"
|
#include "replay-internal.h"
|
||||||
#include "monitor/hmp.h"
|
#include "monitor/hmp.h"
|
||||||
#include "monitor/monitor.h"
|
#include "monitor/monitor.h"
|
||||||
#include "qapi/qapi-commands-replay.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)
|
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();
|
retval->icount = replay_get_current_icount();
|
||||||
return retval;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -94,6 +94,10 @@ extern ReplayState replay_state;
|
|||||||
|
|
||||||
/* File for replay writing */
|
/* File for replay writing */
|
||||||
extern FILE *replay_file;
|
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_byte(uint8_t byte);
|
||||||
void replay_put_event(uint8_t event);
|
void replay_put_event(uint8_t event);
|
||||||
|
@ -34,6 +34,10 @@ static char *replay_filename;
|
|||||||
ReplayState replay_state;
|
ReplayState replay_state;
|
||||||
static GSList *replay_blockers;
|
static GSList *replay_blockers;
|
||||||
|
|
||||||
|
/* Replay breakpoints */
|
||||||
|
uint64_t replay_break_icount = -1ULL;
|
||||||
|
QEMUTimer *replay_break_timer;
|
||||||
|
|
||||||
bool replay_next_event_is(int event)
|
bool replay_next_event_is(int event)
|
||||||
{
|
{
|
||||||
bool res = false;
|
bool res = false;
|
||||||
@ -73,6 +77,13 @@ int replay_get_instructions(void)
|
|||||||
replay_mutex_lock();
|
replay_mutex_lock();
|
||||||
if (replay_next_event_is(EVENT_INSTRUCTION)) {
|
if (replay_next_event_is(EVENT_INSTRUCTION)) {
|
||||||
res = replay_state.instruction_count;
|
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();
|
replay_mutex_unlock();
|
||||||
return res;
|
return res;
|
||||||
@ -99,6 +110,12 @@ void replay_account_executed_instructions(void)
|
|||||||
will be read from the log. */
|
will be read from the log. */
|
||||||
qemu_notify_event();
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user