Import QEMU upstream snapshot d2e570c

Upstream: https://gitlab.com/qemu-project/qemu.git

Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
2026-08-31 02:15:30 +02:00
commit cf256aa081
11315 changed files with 3598369 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
system_ss.add(when: 'CONFIG_TCG', if_true: files(
'replay.c',
'replay-internal.c',
'replay-events.c',
'replay-time.c',
'replay-input.c',
'replay-char.c',
'replay-snapshot.c',
'replay-net.c',
'replay-audio.c',
'replay-random.c',
'replay-debugging.c',
), if_false: files('stubs-system.c'))
+81
View File
@@ -0,0 +1,81 @@
/*
* replay-audio.c
*
* Copyright (c) 2010-2017 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "system/replay.h"
#include "replay-internal.h"
void replay_audio_out(size_t *played)
{
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_put_event(EVENT_AUDIO_OUT);
replay_put_qword(*played);
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
if (replay_next_event_is(EVENT_AUDIO_OUT)) {
*played = replay_get_qword();
replay_finish_event();
} else {
error_report("Missing audio out event in the replay log");
abort();
}
}
}
void replay_audio_in_start(size_t *nsamples)
{
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_put_event(EVENT_AUDIO_IN);
replay_put_qword(*nsamples);
replay_state.n_audio_samples = *nsamples;
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
if (replay_next_event_is(EVENT_AUDIO_IN)) {
*nsamples = replay_get_qword();
replay_state.n_audio_samples = *nsamples;
} else {
error_report("Missing audio in event in the replay log");
abort();
}
}
}
void replay_audio_in_sample_lr(uint64_t *left, uint64_t *right)
{
if (replay_mode == REPLAY_MODE_RECORD) {
replay_put_qword(*left);
replay_put_qword(*right);
} else if (replay_mode == REPLAY_MODE_PLAY) {
*left = replay_get_qword();
*right = replay_get_qword();
} else {
return;
}
assert(replay_state.n_audio_samples > 0);
replay_state.n_audio_samples--;
}
void replay_audio_in_finish(void)
{
assert(replay_state.n_audio_samples == 0);
if (replay_mode == REPLAY_MODE_PLAY) {
replay_finish_event();
}
}
+156
View File
@@ -0,0 +1,156 @@
/*
* replay-char.c
*
* Copyright (c) 2010-2016 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "system/replay.h"
#include "replay-internal.h"
#include "chardev/char.h"
/* Char drivers that generate qemu_chr_be_write events
that should be saved into the log. */
static Chardev **char_drivers;
static int drivers_count;
/* Char event attributes. */
typedef struct CharEvent {
int id;
uint8_t *buf;
size_t len;
} CharEvent;
static int find_char_driver(Chardev *chr)
{
int i = 0;
for ( ; i < drivers_count ; ++i) {
if (char_drivers[i] == chr) {
return i;
}
}
return -1;
}
void replay_register_char_driver(Chardev *chr)
{
if (replay_mode == REPLAY_MODE_NONE) {
return;
}
char_drivers = g_realloc(char_drivers,
sizeof(*char_drivers) * (drivers_count + 1));
char_drivers[drivers_count++] = chr;
}
void replay_chr_be_write(Chardev *s, const uint8_t *buf, int len)
{
CharEvent *event = g_new0(CharEvent, 1);
event->id = find_char_driver(s);
if (event->id < 0) {
fprintf(stderr, "Replay: cannot find char driver\n");
exit(1);
}
event->buf = g_malloc(len);
memcpy(event->buf, buf, len);
event->len = len;
replay_add_event(REPLAY_ASYNC_EVENT_CHAR_READ, event, NULL, 0);
}
void replay_event_char_read_run(void *opaque)
{
CharEvent *event = (CharEvent *)opaque;
qemu_chr_be_write_impl(char_drivers[event->id], event->buf,
(int)event->len);
g_free(event->buf);
g_free(event);
}
void replay_event_char_read_save(void *opaque)
{
CharEvent *event = (CharEvent *)opaque;
replay_put_byte(event->id);
replay_put_array(event->buf, event->len);
}
void *replay_event_char_read_load(void)
{
CharEvent *event = g_new0(CharEvent, 1);
event->id = replay_get_byte();
replay_get_array_alloc(&event->buf, &event->len);
return event;
}
void replay_char_write_event_save(int res, int offset)
{
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_put_event(EVENT_CHAR_WRITE);
replay_put_dword(res);
replay_put_dword(offset);
}
void replay_char_write_event_load(int *res, int *offset)
{
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
if (replay_next_event_is(EVENT_CHAR_WRITE)) {
*res = replay_get_dword();
*offset = replay_get_dword();
replay_finish_event();
} else {
replay_sync_error("Missing character write event in the replay log");
}
}
int replay_char_read_all_load(uint8_t *buf)
{
g_assert(replay_mutex_locked());
if (replay_next_event_is(EVENT_CHAR_READ_ALL)) {
size_t size;
int res;
replay_get_array(buf, &size);
replay_finish_event();
assert(size <= INT_MAX);
res = (int)size;
return res;
} else if (replay_next_event_is(EVENT_CHAR_READ_ALL_ERROR)) {
int res = replay_get_dword();
replay_finish_event();
return res;
} else {
replay_sync_error("Missing character read all event in the replay log");
}
}
void replay_char_read_all_save_error(int res)
{
g_assert(replay_mutex_locked());
assert(res < 0);
replay_save_instructions();
replay_put_event(EVENT_CHAR_READ_ALL_ERROR);
replay_put_dword(res);
}
void replay_char_read_all_save_buf(uint8_t *buf, int offset)
{
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_put_event(EVENT_CHAR_READ_ALL);
replay_put_array(buf, offset);
}
+330
View File
@@ -0,0 +1,330 @@
/*
* replay-debugging.c
*
* Copyright (c) 2010-2020 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "system/replay.h"
#include "system/runstate.h"
#include "replay-internal.h"
#include "monitor/hmp.h"
#include "monitor/monitor.h"
#include "qapi/qapi-commands-replay.h"
#include "qobject/qdict.h"
#include "qemu/timer.h"
#include "block/snapshot.h"
#include "migration/snapshot.h"
static bool replay_is_debugging;
static int64_t replay_last_breakpoint;
static int64_t replay_last_snapshot;
bool replay_running_debug(void)
{
return replay_is_debugging;
}
void hmp_info_replay(Monitor *mon, const QDict *qdict)
{
if (replay_mode == REPLAY_MODE_NONE) {
monitor_printf(mon, "Record/replay is not active\n");
} else {
monitor_printf(mon,
"%s execution '%s': instruction count = %"PRId64"\n",
replay_mode == REPLAY_MODE_RECORD ? "Recording" : "Replaying",
replay_get_filename(), replay_get_current_icount());
}
}
ReplayInfo *qmp_query_replay(Error **errp)
{
ReplayInfo *retval = g_new0(ReplayInfo, 1);
retval->mode = replay_mode;
if (replay_get_filename()) {
retval->filename = g_strdup(replay_get_filename());
}
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_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;
}
}
static char *replay_find_nearest_snapshot(int64_t icount,
int64_t *snapshot_icount)
{
BlockDriverState *bs;
g_autofree QEMUSnapshotInfo *sn_tab = NULL;
QEMUSnapshotInfo *nearest = NULL;
int rv;
int nb_sns, i;
*snapshot_icount = -1;
bs = bdrv_all_find_vmstate_bs(NULL, false, NULL, NULL);
if (!bs) {
return NULL;
}
nb_sns = bdrv_snapshot_list(bs, &sn_tab);
if (nb_sns < 0) {
return NULL;
}
for (i = 0; i < nb_sns; i++) {
rv = bdrv_all_has_snapshot(sn_tab[i].name, false, NULL, NULL);
if (rv < 0) {
return NULL;
}
if (rv == 1) {
if (sn_tab[i].icount != -1ULL
&& sn_tab[i].icount <= icount
&& (!nearest || nearest->icount < sn_tab[i].icount)) {
nearest = &sn_tab[i];
}
}
}
if (!nearest) {
return NULL;
}
*snapshot_icount = nearest->icount;
return g_strdup(nearest->name);
}
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, NULL, false, NULL, 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;
}
}
static void replay_stop_vm_debug(void *opaque)
{
replay_is_debugging = false;
vm_stop(RUN_STATE_DEBUG);
replay_delete_break();
}
bool replay_reverse_step(void)
{
Error *err = NULL;
assert(replay_mode == REPLAY_MODE_PLAY);
if (replay_get_current_icount() != 0) {
replay_seek(replay_get_current_icount() - 1,
replay_stop_vm_debug, &err);
if (err) {
error_free(err);
return false;
}
replay_is_debugging = true;
return true;
}
return false;
}
static void replay_continue_end(void)
{
replay_is_debugging = false;
vm_stop(RUN_STATE_DEBUG);
replay_delete_break();
}
static void replay_continue_stop(void *opaque)
{
Error *err = NULL;
if (replay_last_breakpoint != -1LL) {
replay_seek(replay_last_breakpoint, replay_stop_vm_debug, &err);
if (err) {
error_free(err);
replay_continue_end();
}
return;
}
/*
* No breakpoints since the last snapshot.
* Find previous snapshot and try again.
*/
if (replay_last_snapshot != 0) {
replay_seek(replay_last_snapshot - 1, replay_continue_stop, &err);
if (err) {
error_free(err);
replay_continue_end();
}
replay_last_snapshot = replay_get_current_icount();
} else {
/* Seek to the very first step */
replay_seek(0, replay_stop_vm_debug, &err);
if (err) {
error_free(err);
replay_continue_end();
}
}
}
bool replay_reverse_continue(void)
{
Error *err = NULL;
assert(replay_mode == REPLAY_MODE_PLAY);
if (replay_get_current_icount() != 0) {
replay_seek(replay_get_current_icount() - 1,
replay_continue_stop, &err);
if (err) {
error_free(err);
return false;
}
replay_last_breakpoint = -1LL;
replay_is_debugging = true;
replay_last_snapshot = replay_get_current_icount();
return true;
}
return false;
}
void replay_breakpoint(void)
{
assert(replay_mode == REPLAY_MODE_PLAY);
replay_last_breakpoint = replay_get_current_icount();
}
void replay_gdb_attached(void)
{
/*
* Create VM snapshot on temporary overlay to allow reverse
* debugging even if snapshots were not enabled.
*/
if (replay_mode == REPLAY_MODE_PLAY
&& !replay_snapshot) {
if (!save_snapshot("start_debugging", true, NULL, false, NULL, NULL)) {
/* Can't create the snapshot. Continue conventional debugging. */
}
}
}
+311
View File
@@ -0,0 +1,311 @@
/*
* replay-events.c
*
* Copyright (c) 2010-2015 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "system/replay.h"
#include "replay-internal.h"
#include "qemu/aio.h"
#include "ui/input.h"
#include "hw/core/cpu.h"
typedef struct Event {
ReplayAsyncEventKind event_kind;
void *opaque;
void *opaque2;
uint64_t id;
QTAILQ_ENTRY(Event) events;
} Event;
static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list);
static bool events_enabled;
/* Functions */
static void replay_run_event(Event *event)
{
switch (event->event_kind) {
case REPLAY_ASYNC_EVENT_BH:
aio_bh_call(event->opaque);
break;
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
((QEMUBHFunc *)event->opaque)(event->opaque2);
break;
case REPLAY_ASYNC_EVENT_INPUT:
qemu_input_event_send_impl(NULL, (QemuInputEvent *)event->opaque);
g_free(event->opaque);
break;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
qemu_input_event_sync_impl();
break;
case REPLAY_ASYNC_EVENT_CHAR_READ:
replay_event_char_read_run(event->opaque);
break;
case REPLAY_ASYNC_EVENT_BLOCK:
aio_bh_call(event->opaque);
break;
case REPLAY_ASYNC_EVENT_NET:
replay_event_net_run(event->opaque);
break;
default:
error_report("Replay: invalid async event ID (%d) in the queue",
event->event_kind);
exit(1);
break;
}
}
void replay_enable_events(void)
{
if (replay_mode != REPLAY_MODE_NONE) {
events_enabled = true;
}
}
bool replay_has_events(void)
{
return !QTAILQ_EMPTY(&events_list);
}
void replay_flush_events(void)
{
if (replay_mode == REPLAY_MODE_NONE) {
return;
}
g_assert(replay_mutex_locked());
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
replay_run_event(event);
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
}
/*! Adds specified async event to the queue */
void replay_add_event(ReplayAsyncEventKind event_kind,
void *opaque,
void *opaque2, uint64_t id)
{
assert(event_kind < REPLAY_ASYNC_COUNT);
if (!replay_file || replay_mode == REPLAY_MODE_NONE
|| !events_enabled) {
Event e;
e.event_kind = event_kind;
e.opaque = opaque;
e.opaque2 = opaque2;
e.id = id;
replay_run_event(&e);
return;
}
Event *event = g_new0(Event, 1);
event->event_kind = event_kind;
event->opaque = opaque;
event->opaque2 = opaque2;
event->id = id;
g_assert(replay_mutex_locked());
QTAILQ_INSERT_TAIL(&events_list, event, events);
/* Kick the TCG thread out of tcg_cpu_exec(). */
cpu_exit(first_cpu);
}
void replay_bh_schedule_event(QEMUBH *bh)
{
if (events_enabled) {
uint64_t id = replay_get_current_icount();
replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
} else {
qemu_bh_schedule(bh);
}
}
void replay_bh_schedule_oneshot_event(AioContext *ctx,
QEMUBHFunc *cb, void *opaque)
{
if (events_enabled) {
uint64_t id = replay_get_current_icount();
replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id);
} else {
aio_bh_schedule_oneshot(ctx, cb, opaque);
}
}
void replay_add_input_event(QemuInputEvent *event)
{
replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
}
void replay_add_input_sync_event(void)
{
replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0);
}
void replay_block_event(QEMUBH *bh, uint64_t id)
{
if (events_enabled) {
replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id);
} else {
qemu_bh_schedule(bh);
}
}
static void replay_save_event(Event *event)
{
if (replay_mode != REPLAY_MODE_PLAY) {
/* put the event into the file */
g_assert(event->event_kind < REPLAY_ASYNC_COUNT);
replay_put_event(EVENT_ASYNC + event->event_kind);
/* save event-specific data */
switch (event->event_kind) {
case REPLAY_ASYNC_EVENT_BH:
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
replay_put_qword(event->id);
break;
case REPLAY_ASYNC_EVENT_INPUT:
replay_save_input_event(event->opaque);
break;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
break;
case REPLAY_ASYNC_EVENT_CHAR_READ:
replay_event_char_read_save(event->opaque);
break;
case REPLAY_ASYNC_EVENT_BLOCK:
replay_put_qword(event->id);
break;
case REPLAY_ASYNC_EVENT_NET:
replay_event_net_save(event->opaque);
break;
default:
error_report("Unknown ID %" PRId64 " of replay event", event->id);
exit(1);
}
}
}
/* Called with replay mutex locked */
void replay_save_events(void)
{
g_assert(replay_mutex_locked());
while (!QTAILQ_EMPTY(&events_list)) {
Event *event = QTAILQ_FIRST(&events_list);
replay_save_event(event);
replay_run_event(event);
QTAILQ_REMOVE(&events_list, event, events);
g_free(event);
}
}
static Event *replay_read_event(void)
{
Event *event;
ReplayAsyncEventKind event_kind = replay_state.data_kind - EVENT_ASYNC;
/* Events that has not to be in the queue */
switch (event_kind) {
case REPLAY_ASYNC_EVENT_BH:
case REPLAY_ASYNC_EVENT_BH_ONESHOT:
if (replay_state.read_event_id == -1) {
replay_state.read_event_id = replay_get_qword();
}
break;
case REPLAY_ASYNC_EVENT_INPUT:
event = g_new0(Event, 1);
event->event_kind = event_kind;
event->opaque = replay_read_input_event();
return event;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
event = g_new0(Event, 1);
event->event_kind = event_kind;
event->opaque = 0;
return event;
case REPLAY_ASYNC_EVENT_CHAR_READ:
event = g_new0(Event, 1);
event->event_kind = event_kind;
event->opaque = replay_event_char_read_load();
return event;
case REPLAY_ASYNC_EVENT_BLOCK:
if (replay_state.read_event_id == -1) {
replay_state.read_event_id = replay_get_qword();
}
break;
case REPLAY_ASYNC_EVENT_NET:
event = g_new0(Event, 1);
event->event_kind = event_kind;
event->opaque = replay_event_net_load();
return event;
default:
error_report("Unknown ID %d of replay event", event_kind);
exit(1);
break;
}
QTAILQ_FOREACH(event, &events_list, events) {
if (event->event_kind == event_kind
&& (replay_state.read_event_id == -1
|| replay_state.read_event_id == event->id)) {
break;
}
}
if (event) {
QTAILQ_REMOVE(&events_list, event, events);
}
return event;
}
/* Called with replay mutex locked */
void replay_read_events(void)
{
g_assert(replay_mutex_locked());
while (replay_state.data_kind >= EVENT_ASYNC
&& replay_state.data_kind <= EVENT_ASYNC_LAST) {
Event *event = replay_read_event();
if (!event) {
break;
}
replay_finish_event();
replay_state.read_event_id = -1;
replay_run_event(event);
g_free(event);
}
}
void replay_init_events(void)
{
replay_state.read_event_id = -1;
}
void replay_finish_events(void)
{
events_enabled = false;
replay_flush_events();
}
bool replay_events_enabled(void)
{
return events_enabled;
}
uint64_t blkreplay_next_id(void)
{
if (replay_events_enabled()) {
return replay_state.block_request_id++;
}
return 0;
}
+113
View File
@@ -0,0 +1,113 @@
/*
* replay-input.c
*
* Copyright (c) 2010-2015 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "system/replay.h"
#include "replay-internal.h"
#include "qemu/notify.h"
#include "ui/input.h"
#include "qapi/clone-visitor.h"
#include "qapi/qapi-visit-ui.h"
void replay_save_input_event(QemuInputEvent *evt)
{
replay_put_dword(evt->type);
switch (evt->type) {
case INPUT_EVENT_KIND_KEY:
replay_put_dword(evt->key.key);
replay_put_byte(evt->key.down);
break;
case INPUT_EVENT_KIND_BTN:
replay_put_dword(evt->btn.button);
replay_put_byte(evt->btn.down);
break;
case INPUT_EVENT_KIND_REL:
replay_put_dword(evt->rel.axis);
replay_put_qword(evt->rel.value);
break;
case INPUT_EVENT_KIND_ABS:
replay_put_dword(evt->abs.axis);
replay_put_qword(evt->abs.value);
break;
case INPUT_EVENT_KIND_MTT:
replay_put_dword(evt->mtt.type);
replay_put_qword(evt->mtt.slot);
replay_put_qword(evt->mtt.tracking_id);
replay_put_dword(evt->mtt.axis);
replay_put_qword(evt->mtt.value);
break;
case INPUT_EVENT_KIND__MAX:
/* keep gcc happy */
break;
}
}
QemuInputEvent *replay_read_input_event(void)
{
QemuInputEvent *evt = g_new(QemuInputEvent, 1);
evt->type = replay_get_dword();
switch (evt->type) {
case INPUT_EVENT_KIND_KEY:
evt->key.key = replay_get_dword();
evt->key.down = replay_get_byte();
break;
case INPUT_EVENT_KIND_BTN:
evt->btn.button = (InputButton)replay_get_dword();
evt->btn.down = replay_get_byte();
break;
case INPUT_EVENT_KIND_REL:
evt->rel.axis = (InputAxis)replay_get_dword();
evt->rel.value = replay_get_qword();
break;
case INPUT_EVENT_KIND_ABS:
evt->abs.axis = (InputAxis)replay_get_dword();
evt->abs.value = replay_get_qword();
break;
case INPUT_EVENT_KIND_MTT:
evt->mtt.type = (InputMultiTouchType)replay_get_dword();
evt->mtt.slot = replay_get_qword();
evt->mtt.tracking_id = replay_get_qword();
evt->mtt.axis = (InputAxis)replay_get_dword();
evt->mtt.value = replay_get_qword();
break;
case INPUT_EVENT_KIND__MAX:
/* keep gcc happy */
break;
}
return evt;
}
void replay_input_event(QemuConsole *src, QemuInputEvent *evt)
{
if (replay_mode == REPLAY_MODE_PLAY) {
/* Nothing */
} else if (replay_mode == REPLAY_MODE_RECORD) {
QemuInputEvent *clone = g_new(QemuInputEvent, 1);
*clone = *evt;
replay_add_input_event(clone);
} else {
qemu_input_event_send_impl(src, evt);
}
}
void replay_input_sync_event(void)
{
if (replay_mode == REPLAY_MODE_PLAY) {
/* Nothing */
} else if (replay_mode == REPLAY_MODE_RECORD) {
replay_add_input_sync_event();
} else {
qemu_input_event_sync_impl();
}
}
+322
View File
@@ -0,0 +1,322 @@
/*
* replay-internal.c
*
* Copyright (c) 2010-2015 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "system/replay.h"
#include "system/runstate.h"
#include "replay-internal.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "trace.h"
/* Mutex to protect reading and writing events to the log.
data_kind and has_unread_data are also protected
by this mutex.
It also protects replay events queue which stores events to be
written or read to the log. */
static QemuMutex lock;
/* Condition and queue for fair ordering of mutex lock requests. */
static QemuCond mutex_cond;
static unsigned long mutex_head, mutex_tail;
/* File for replay writing */
static bool write_error;
FILE *replay_file;
static void replay_write_error(void)
{
if (!write_error) {
error_report("replay write error");
write_error = true;
}
}
static void replay_read_error(void)
{
error_report("error reading the replay data");
exit(1);
}
static void replay_putc(uint8_t byte)
{
if (replay_file) {
if (putc(byte, replay_file) == EOF) {
replay_write_error();
}
}
}
void replay_put_byte(uint8_t byte)
{
trace_replay_put_byte(byte);
replay_putc(byte);
}
void replay_put_event(uint8_t event)
{
trace_replay_put_event(event);
assert(event < EVENT_COUNT);
replay_putc(event);
}
void replay_put_word(uint16_t word)
{
trace_replay_put_word(word);
replay_putc(word >> 8);
replay_putc(word);
}
void replay_put_dword(uint32_t dword)
{
int i;
trace_replay_put_dword(dword);
for (i = 24; i >= 0; i -= 8) {
replay_putc(dword >> i);
}
}
void replay_put_qword(int64_t qword)
{
int i;
trace_replay_put_qword(qword);
for (i = 56; i >= 0; i -= 8) {
replay_putc(qword >> i);
}
}
void replay_put_array(const uint8_t *buf, size_t size)
{
if (replay_file) {
replay_put_dword(size);
if (fwrite(buf, 1, size, replay_file) != size) {
replay_write_error();
}
}
}
static uint8_t replay_getc(void)
{
uint8_t byte = 0;
if (replay_file) {
int r = getc(replay_file);
if (r == EOF) {
replay_read_error();
}
byte = r;
}
return byte;
}
uint8_t replay_get_byte(void)
{
uint8_t byte = replay_getc();
trace_replay_get_byte(byte);
return byte;
}
uint16_t replay_get_word(void)
{
uint16_t word = 0;
if (replay_file) {
word = replay_getc();
word = (word << 8) + replay_getc();
}
trace_replay_get_word(word);
return word;
}
uint32_t replay_get_dword(void)
{
uint32_t dword = 0;
int i;
if (replay_file) {
for (i = 24; i >= 0; i -= 8) {
dword |= replay_getc() << i;
}
}
trace_replay_get_dword(dword);
return dword;
}
int64_t replay_get_qword(void)
{
uint64_t qword = 0;
int i;
if (replay_file) {
for (i = 56; i >= 0; i -= 8) {
qword |= (uint64_t)replay_getc() << i;
}
}
trace_replay_get_qword(qword);
return qword;
}
void replay_get_array(uint8_t *buf, size_t *size)
{
if (replay_file) {
*size = replay_get_dword();
if (fread(buf, 1, *size, replay_file) != *size) {
replay_read_error();
}
}
}
void replay_get_array_alloc(uint8_t **buf, size_t *size)
{
if (replay_file) {
*size = replay_get_dword();
*buf = g_malloc(*size);
if (fread(*buf, 1, *size, replay_file) != *size) {
replay_read_error();
}
}
}
void replay_check_error(void)
{
if (replay_file) {
if (feof(replay_file)) {
error_report("replay file is over");
qemu_system_vmstop_request_prepare();
qemu_system_vmstop_request(RUN_STATE_PAUSED);
} else if (ferror(replay_file)) {
error_report("replay file is over or something goes wrong");
qemu_system_vmstop_request_prepare();
qemu_system_vmstop_request(RUN_STATE_INTERNAL_ERROR);
}
}
}
void replay_fetch_data_kind(void)
{
trace_replay_fetch_data_kind();
if (replay_file) {
if (!replay_state.has_unread_data) {
replay_state.data_kind = replay_getc();
replay_state.current_event++;
trace_replay_get_event(replay_state.current_event, replay_state.data_kind);
if (replay_state.data_kind == EVENT_INSTRUCTION) {
replay_state.instruction_count = replay_get_dword();
}
replay_check_error();
replay_state.has_unread_data = true;
if (replay_state.data_kind >= EVENT_COUNT) {
error_report("Replay: unknown event kind %d",
replay_state.data_kind);
exit(1);
}
}
}
}
void replay_finish_event(void)
{
replay_state.has_unread_data = false;
replay_fetch_data_kind();
}
static __thread bool replay_locked;
void replay_mutex_init(void)
{
qemu_mutex_init(&lock);
qemu_cond_init(&mutex_cond);
/* Hold the mutex while we start-up */
replay_locked = true;
++mutex_tail;
}
bool replay_mutex_locked(void)
{
return replay_locked;
}
/* Ordering constraints, replay_lock must be taken before BQL */
void replay_mutex_lock(void)
{
if (replay_mode != REPLAY_MODE_NONE) {
unsigned long id;
g_assert(!bql_locked());
g_assert(!replay_mutex_locked());
qemu_mutex_lock(&lock);
id = mutex_tail++;
while (id != mutex_head) {
qemu_cond_wait(&mutex_cond, &lock);
}
replay_locked = true;
qemu_mutex_unlock(&lock);
}
}
void replay_mutex_unlock(void)
{
if (replay_mode != REPLAY_MODE_NONE) {
g_assert(replay_mutex_locked());
qemu_mutex_lock(&lock);
++mutex_head;
replay_locked = false;
qemu_cond_broadcast(&mutex_cond);
qemu_mutex_unlock(&lock);
}
}
void replay_advance_current_icount(uint64_t current_icount)
{
int diff = (int)(current_icount - replay_state.current_icount);
/* Time can only go forward */
trace_replay_advance_current_icount(replay_state.current_icount, diff);
assert(diff >= 0);
if (replay_mode == REPLAY_MODE_RECORD) {
if (diff > 0) {
replay_put_event(EVENT_INSTRUCTION);
replay_put_dword(diff);
replay_state.current_icount += diff;
}
} else if (replay_mode == REPLAY_MODE_PLAY) {
if (diff > 0) {
replay_state.instruction_count -= diff;
replay_state.current_icount += diff;
if (replay_state.instruction_count == 0) {
assert(replay_state.data_kind == EVENT_INSTRUCTION);
replay_finish_event();
/* Wake up iothread. This is required because
timers will not expire until clock counters
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));
}
}
}
/*! Saves cached instructions. */
void replay_save_instructions(void)
{
if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_advance_current_icount(replay_get_current_icount());
}
}
+217
View File
@@ -0,0 +1,217 @@
#ifndef REPLAY_INTERNAL_H
#define REPLAY_INTERNAL_H
/*
* replay-internal.h
*
* Copyright (c) 2010-2015 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
/* Asynchronous events IDs */
typedef enum ReplayAsyncEventKind {
REPLAY_ASYNC_EVENT_BH,
REPLAY_ASYNC_EVENT_BH_ONESHOT,
REPLAY_ASYNC_EVENT_INPUT,
REPLAY_ASYNC_EVENT_INPUT_SYNC,
REPLAY_ASYNC_EVENT_CHAR_READ,
REPLAY_ASYNC_EVENT_BLOCK,
REPLAY_ASYNC_EVENT_NET,
REPLAY_ASYNC_COUNT
} ReplayAsyncEventKind;
/*
* Any changes to order/number of events will need to bump
* REPLAY_VERSION to prevent confusion with old logs. Also don't
* forget to update replay_event_name() to make your debugging life
* easier.
*/
enum ReplayEvents {
/* for instruction event */
EVENT_INSTRUCTION,
/* for software interrupt */
EVENT_INTERRUPT,
/* for emulated exceptions */
EVENT_EXCEPTION,
/* for async events */
EVENT_ASYNC,
EVENT_ASYNC_LAST = EVENT_ASYNC + REPLAY_ASYNC_COUNT - 1,
/* for shutdown requests, range allows recovery of ShutdownCause */
EVENT_SHUTDOWN,
EVENT_SHUTDOWN_LAST = EVENT_SHUTDOWN + SHUTDOWN_CAUSE__MAX,
/* for character device write event */
EVENT_CHAR_WRITE,
/* for character device read all event */
EVENT_CHAR_READ_ALL,
EVENT_CHAR_READ_ALL_ERROR,
/* for audio out event */
EVENT_AUDIO_OUT,
/* for audio in event */
EVENT_AUDIO_IN,
/* for random number generator */
EVENT_RANDOM,
/* for clock read/writes */
/* some of greater codes are reserved for clocks */
EVENT_CLOCK,
EVENT_CLOCK_LAST = EVENT_CLOCK + REPLAY_CLOCK_COUNT - 1,
/* for checkpoint event */
/* some of greater codes are reserved for checkpoints */
EVENT_CHECKPOINT,
EVENT_CHECKPOINT_LAST = EVENT_CHECKPOINT + CHECKPOINT_COUNT - 1,
/* end of log event */
EVENT_END,
EVENT_COUNT
};
/**
* typedef ReplayState - global tracking Replay state
*
* This structure tracks where we are in the current ReplayState
* including the logged events from the recorded replay stream. Some
* of the data is also stored/restored from VMStateDescription when VM
* save/restore events take place.
*
* @cached_clock: Cached clocks values
* @current_icount: number of processed instructions
* @instruction_count: number of instructions until next event
* @current_event: current event index
* @data_kind: current event
* @has_unread_data: true if event not yet processed
* @file_offset: offset into replay log at replay snapshot
* @block_request_id: current serialised block request id
* @read_event_id: current async read event id
* @n_audio_samples: expected audio samples
*/
typedef struct ReplayState {
int64_t cached_clock[REPLAY_CLOCK_COUNT];
uint64_t current_icount;
int instruction_count;
unsigned int current_event;
unsigned int data_kind;
bool has_unread_data;
uint64_t file_offset;
uint64_t block_request_id;
uint64_t read_event_id;
size_t n_audio_samples;
} ReplayState;
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);
void replay_put_word(uint16_t word);
void replay_put_dword(uint32_t dword);
void replay_put_qword(int64_t qword);
void replay_put_array(const uint8_t *buf, size_t size);
uint8_t replay_get_byte(void);
uint16_t replay_get_word(void);
uint32_t replay_get_dword(void);
int64_t replay_get_qword(void);
void replay_get_array(uint8_t *buf, size_t *size);
void replay_get_array_alloc(uint8_t **buf, size_t *size);
/* Mutex functions for protecting replay log file and ensuring
* synchronisation between vCPU and main-loop threads. */
void replay_mutex_init(void);
bool replay_mutex_locked(void);
/*! Checks error status of the file. */
void replay_check_error(void);
/*! Finishes processing of the replayed event and fetches
the next event from the log. */
void replay_finish_event(void);
/*! Reads data type from the file and stores it in the
data_kind variable. */
void replay_fetch_data_kind(void);
/*! Advance replay_state.current_icount to the specified value. */
void replay_advance_current_icount(uint64_t current_icount);
/*! Saves queued events (like instructions and sound). */
void replay_save_instructions(void);
/*! Skips async events until some sync event will be found.
\return true, if event was found */
bool replay_next_event_is(int event);
/*! Reads next clock value from the file.
If clock kind read from the file is different from the parameter,
the value is not used. */
void replay_read_next_clock(ReplayClockKind kind);
/* Asynchronous events queue */
/*! Initializes events' processing internals */
void replay_init_events(void);
/*! Clears internal data structures for events handling */
void replay_finish_events(void);
/*! Returns true if there are any unsaved events in the queue */
bool replay_has_events(void);
/*! Saves events from queue into the file */
void replay_save_events(void);
/*! Read events from the file into the input queue */
void replay_read_events(void);
/*! Adds specified async event to the queue */
void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque,
void *opaque2, uint64_t id);
/* Input events */
/*! Saves input event to the log */
void replay_save_input_event(QemuInputEvent *evt);
/*! Reads input event from the log */
QemuInputEvent *replay_read_input_event(void);
/*! Adds input event to the queue */
void replay_add_input_event(QemuInputEvent *event);
/*! Adds input sync event to the queue */
void replay_add_input_sync_event(void);
/* Character devices */
/*! Called to run char device read event. */
void replay_event_char_read_run(void *opaque);
/*! Writes char read event to the file. */
void replay_event_char_read_save(void *opaque);
/*! Reads char event read from the file. */
void *replay_event_char_read_load(void);
/* Network devices */
/*! Called to run network event. */
void replay_event_net_run(void *opaque);
/*! Writes network event to the file. */
void replay_event_net_save(void *opaque);
/*! Reads network from the file. */
void *replay_event_net_load(void);
/* Diagnostics */
/**
* replay_sync_error(): report sync error and exit
*
* When we reach an error condition we want to report it centrally so
* we can also dump some useful information into the logs.
*/
G_NORETURN void replay_sync_error(const char *error);
/* VMState-related functions */
/* Registers replay VMState.
Should be called before virtual devices initialization
to make cached timers available for post_load functions. */
void replay_vmstate_register(void);
#endif
+101
View File
@@ -0,0 +1,101 @@
/*
* replay-net.c
*
* Copyright (c) 2010-2016 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "system/replay.h"
#include "replay-internal.h"
#include "net/net.h"
#include "net/filter.h"
#include "qemu/iov.h"
struct ReplayNetState {
NetFilterState *nfs;
int id;
};
typedef struct NetEvent {
uint8_t id;
uint32_t flags;
uint8_t *data;
size_t size;
} NetEvent;
static NetFilterState **network_filters;
static int network_filters_count;
ReplayNetState *replay_register_net(NetFilterState *nfs)
{
ReplayNetState *rns = g_new0(ReplayNetState, 1);
rns->nfs = nfs;
rns->id = network_filters_count++;
network_filters = g_realloc(network_filters,
network_filters_count
* sizeof(*network_filters));
network_filters[network_filters_count - 1] = nfs;
return rns;
}
void replay_unregister_net(ReplayNetState *rns)
{
network_filters[rns->id] = NULL;
g_free(rns);
}
void replay_net_packet_event(ReplayNetState *rns, unsigned flags,
const struct iovec *iov, int iovcnt)
{
NetEvent *event = g_new(NetEvent, 1);
event->flags = flags;
event->data = g_malloc(iov_size(iov, iovcnt));
event->size = iov_size(iov, iovcnt);
event->id = rns->id;
iov_to_buf(iov, iovcnt, 0, event->data, event->size);
replay_add_event(REPLAY_ASYNC_EVENT_NET, event, NULL, 0);
}
void replay_event_net_run(void *opaque)
{
NetEvent *event = opaque;
struct iovec iov = {
.iov_base = (void *)event->data,
.iov_len = event->size
};
assert(event->id < network_filters_count);
qemu_netfilter_pass_to_next(network_filters[event->id]->netdev,
event->flags, &iov, 1, network_filters[event->id]);
g_free(event->data);
g_free(event);
}
void replay_event_net_save(void *opaque)
{
NetEvent *event = opaque;
replay_put_byte(event->id);
replay_put_dword(event->flags);
replay_put_array(event->data, event->size);
}
void *replay_event_net_load(void)
{
NetEvent *event = g_new(NetEvent, 1);
event->id = replay_get_byte();
event->flags = replay_get_dword();
replay_get_array_alloc(&event->data, &event->size);
return event;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* replay-random.c
*
* Copyright (c) 2010-2020 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "system/replay.h"
#include "replay-internal.h"
void replay_save_random(int ret, void *buf, size_t len)
{
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_put_event(EVENT_RANDOM);
replay_put_dword(ret);
replay_put_array(buf, len);
}
int replay_read_random(void *buf, size_t len)
{
int ret = 0;
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
if (replay_next_event_is(EVENT_RANDOM)) {
size_t buf_size = 0;
ret = replay_get_dword();
replay_get_array(buf, &buf_size);
replay_finish_event();
g_assert(buf_size == len);
} else {
error_report("Missing random event in the replay log");
exit(1);
}
return ret;
}
+99
View File
@@ -0,0 +1,99 @@
/*
* replay-snapshot.c
*
* Copyright (c) 2010-2016 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "system/replay.h"
#include "replay-internal.h"
#include "monitor/monitor.h"
#include "qobject/qstring.h"
#include "qemu/error-report.h"
#include "migration/vmstate.h"
#include "migration/snapshot.h"
static int replay_pre_save(void *opaque)
{
ReplayState *state = opaque;
state->file_offset = ftell(replay_file);
return 0;
}
static int replay_post_load(void *opaque, int version_id)
{
ReplayState *state = opaque;
if (replay_mode == REPLAY_MODE_PLAY) {
fseek(replay_file, state->file_offset, SEEK_SET);
/* If this was a vmstate, saved in recording mode,
we need to initialize replay data fields. */
replay_fetch_data_kind();
} else if (replay_mode == REPLAY_MODE_RECORD) {
/* This is only useful for loading the initial state.
Therefore reset all the counters. */
state->instruction_count = 0;
state->block_request_id = 0;
}
return 0;
}
static const VMStateDescription vmstate_replay = {
.name = "replay",
.version_id = 3,
.minimum_version_id = 3,
.pre_save = replay_pre_save,
.post_load = replay_post_load,
.fields = (const VMStateField[]) {
VMSTATE_INT64_ARRAY(cached_clock, ReplayState, REPLAY_CLOCK_COUNT),
VMSTATE_UINT64(current_icount, ReplayState),
VMSTATE_INT32(instruction_count, ReplayState),
VMSTATE_UINT32(current_event, ReplayState),
VMSTATE_UINT32(data_kind, ReplayState),
VMSTATE_BOOL(has_unread_data, ReplayState),
VMSTATE_UINT64(file_offset, ReplayState),
VMSTATE_UINT64(block_request_id, ReplayState),
VMSTATE_UINT64(read_event_id, ReplayState),
VMSTATE_END_OF_LIST()
},
};
void replay_vmstate_register(void)
{
vmstate_register(NULL, 0, &vmstate_replay, &replay_state);
}
void replay_vmstate_init(void)
{
Error *err = NULL;
if (replay_snapshot) {
if (replay_mode == REPLAY_MODE_RECORD) {
if (!save_snapshot(replay_snapshot,
true, NULL, false, NULL, &err)) {
error_report_err(err);
error_report("Could not create snapshot for icount record");
exit(1);
}
} else if (replay_mode == REPLAY_MODE_PLAY) {
if (!load_snapshot(replay_snapshot, NULL, false, NULL, &err)) {
error_report_err(err);
error_report("Could not load snapshot for icount replay");
exit(1);
}
}
}
}
bool replay_can_snapshot(void)
{
return replay_mode == REPLAY_MODE_NONE
|| !replay_has_events();
}
+59
View File
@@ -0,0 +1,59 @@
/*
* replay-time.c
*
* Copyright (c) 2010-2015 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "system/replay.h"
#include "replay-internal.h"
#include "qemu/error-report.h"
int64_t replay_save_clock(ReplayClockKind kind, int64_t clock,
int64_t raw_icount)
{
g_assert(replay_file);
g_assert(replay_mutex_locked());
/*
* Due to the caller's locking requirements we get the icount from it
* instead of using replay_save_instructions().
*/
replay_advance_current_icount(raw_icount);
replay_put_event(EVENT_CLOCK + kind);
replay_put_qword(clock);
return clock;
}
void replay_read_next_clock(ReplayClockKind kind)
{
unsigned int read_kind = replay_state.data_kind - EVENT_CLOCK;
assert(read_kind == kind);
int64_t clock = replay_get_qword();
replay_check_error();
replay_finish_event();
replay_state.cached_clock[read_kind] = clock;
}
/*! Reads next clock event from the input. */
int64_t replay_read_clock(ReplayClockKind kind, int64_t raw_icount)
{
g_assert(replay_file && replay_mutex_locked());
replay_advance_current_icount(raw_icount);
if (replay_next_event_is(EVENT_CLOCK + kind)) {
replay_read_next_clock(kind);
}
return replay_state.cached_clock[kind];
}
+505
View File
@@ -0,0 +1,505 @@
/*
* replay.c
*
* Copyright (c) 2010-2015 Institute for System Programming
* of the Russian Academy of Sciences.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "exec/icount.h"
#include "system/replay.h"
#include "system/runstate.h"
#include "replay-internal.h"
#include "qemu/main-loop.h"
#include "qemu/option.h"
#include "system/cpus.h"
#include "qemu/error-report.h"
/* Current version of the replay mechanism.
Increase it when file format changes. */
#define REPLAY_VERSION 0xe0200e
/* Size of replay log header */
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
ReplayMode replay_mode = REPLAY_MODE_NONE;
char *replay_snapshot;
/* Name of replay file */
static char *replay_filename;
ReplayState replay_state;
static GSList *replay_blockers;
/* Replay breakpoints */
uint64_t replay_break_icount = -1ULL;
QEMUTimer *replay_break_timer;
/* Pretty print event names */
static const char *replay_async_event_name(ReplayAsyncEventKind event)
{
switch (event) {
#define ASYNC_EVENT(_x) case REPLAY_ASYNC_EVENT_ ## _x: return "ASYNC_EVENT_"#_x
ASYNC_EVENT(BH);
ASYNC_EVENT(BH_ONESHOT);
ASYNC_EVENT(INPUT);
ASYNC_EVENT(INPUT_SYNC);
ASYNC_EVENT(CHAR_READ);
ASYNC_EVENT(BLOCK);
ASYNC_EVENT(NET);
#undef ASYNC_EVENT
default:
g_assert_not_reached();
}
}
static const char *replay_clock_event_name(ReplayClockKind clock)
{
switch (clock) {
#define CLOCK_EVENT(_x) case REPLAY_CLOCK_ ## _x: return "CLOCK_" #_x
CLOCK_EVENT(HOST);
CLOCK_EVENT(VIRTUAL_RT);
#undef CLOCK_EVENT
default:
g_assert_not_reached();
}
}
/* Pretty print shutdown event names */
static const char *replay_shutdown_event_name(ShutdownCause cause)
{
switch (cause) {
#define SHUTDOWN_EVENT(_x) case SHUTDOWN_CAUSE_ ## _x: return "SHUTDOWN_CAUSE_" #_x
SHUTDOWN_EVENT(NONE);
SHUTDOWN_EVENT(HOST_ERROR);
SHUTDOWN_EVENT(HOST_QMP_QUIT);
SHUTDOWN_EVENT(HOST_QMP_SYSTEM_RESET);
SHUTDOWN_EVENT(HOST_SIGNAL);
SHUTDOWN_EVENT(HOST_UI);
SHUTDOWN_EVENT(GUEST_SHUTDOWN);
SHUTDOWN_EVENT(GUEST_RESET);
SHUTDOWN_EVENT(GUEST_PANIC);
SHUTDOWN_EVENT(SUBSYSTEM_RESET);
SHUTDOWN_EVENT(SNAPSHOT_LOAD);
#undef SHUTDOWN_EVENT
default:
g_assert_not_reached();
}
}
static const char *replay_checkpoint_event_name(enum ReplayCheckpoint checkpoint)
{
switch (checkpoint) {
#define CHECKPOINT_EVENT(_x) case CHECKPOINT_ ## _x: return "CHECKPOINT_" #_x
CHECKPOINT_EVENT(CLOCK_WARP_START);
CHECKPOINT_EVENT(CLOCK_WARP_ACCOUNT);
CHECKPOINT_EVENT(RESET_REQUESTED);
CHECKPOINT_EVENT(SUSPEND_REQUESTED);
CHECKPOINT_EVENT(CLOCK_VIRTUAL);
CHECKPOINT_EVENT(CLOCK_HOST);
CHECKPOINT_EVENT(CLOCK_VIRTUAL_RT);
CHECKPOINT_EVENT(INIT);
CHECKPOINT_EVENT(RESET);
#undef CHECKPOINT_EVENT
default:
g_assert_not_reached();
}
}
static const char *replay_event_name(enum ReplayEvents event)
{
/* First deal with the simple ones */
switch (event) {
#define EVENT(_x) case EVENT_ ## _x: return "EVENT_"#_x
EVENT(INSTRUCTION);
EVENT(INTERRUPT);
EVENT(EXCEPTION);
EVENT(CHAR_WRITE);
EVENT(CHAR_READ_ALL);
EVENT(AUDIO_OUT);
EVENT(AUDIO_IN);
EVENT(RANDOM);
#undef EVENT
default:
if (event >= EVENT_ASYNC && event <= EVENT_ASYNC_LAST) {
return replay_async_event_name(event - EVENT_ASYNC);
} else if (event >= EVENT_SHUTDOWN && event <= EVENT_SHUTDOWN_LAST) {
return replay_shutdown_event_name(event - EVENT_SHUTDOWN);
} else if (event >= EVENT_CLOCK && event <= EVENT_CLOCK_LAST) {
return replay_clock_event_name(event - EVENT_CLOCK);
} else if (event >= EVENT_CHECKPOINT && event <= EVENT_CHECKPOINT_LAST) {
return replay_checkpoint_event_name(event - EVENT_CHECKPOINT);
}
}
g_assert_not_reached();
}
bool replay_next_event_is(int event)
{
bool res = false;
/* nothing to skip - not all instructions used */
if (replay_state.instruction_count != 0) {
assert(replay_state.data_kind == EVENT_INSTRUCTION);
return event == EVENT_INSTRUCTION;
}
while (true) {
unsigned int data_kind = replay_state.data_kind;
if (event == data_kind) {
res = true;
}
switch (data_kind) {
case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
replay_finish_event();
qemu_system_shutdown_request(data_kind - EVENT_SHUTDOWN);
break;
default:
/* clock, time_t, checkpoint and other events */
return res;
}
}
return res;
}
uint64_t replay_get_current_icount(void)
{
return icount_get_raw();
}
int replay_get_instructions(void)
{
int res = 0;
g_assert(replay_mutex_locked());
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;
}
}
}
return res;
}
void replay_account_executed_instructions(void)
{
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
if (replay_state.instruction_count > 0) {
replay_advance_current_icount(replay_get_current_icount());
}
}
}
bool replay_exception(void)
{
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_put_event(EVENT_EXCEPTION);
return true;
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
bool res = replay_has_exception();
if (res) {
replay_finish_event();
}
return res;
}
return true;
}
bool replay_has_exception(void)
{
bool res = false;
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
res = replay_next_event_is(EVENT_EXCEPTION);
}
return res;
}
bool replay_interrupt(void)
{
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_instructions();
replay_put_event(EVENT_INTERRUPT);
return true;
} else if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
bool res = replay_has_interrupt();
if (res) {
replay_finish_event();
}
return res;
}
return true;
}
bool replay_has_interrupt(void)
{
bool res = false;
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
res = replay_next_event_is(EVENT_INTERRUPT);
}
return res;
}
void replay_shutdown_request(ShutdownCause cause)
{
replay_save_instructions();
if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_put_event(EVENT_SHUTDOWN + cause);
}
}
bool replay_checkpoint(ReplayCheckpoint checkpoint)
{
assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
replay_save_instructions();
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
replay_finish_event();
} else {
return false;
}
} else if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_put_event(EVENT_CHECKPOINT + checkpoint);
}
return true;
}
void replay_async_events(void)
{
static bool processing = false;
/*
* If we are already processing the events, recursion may occur
* in case of incorrect implementation when HW event modifies timers.
* Timer modification may invoke the icount warp, event processing,
* and cause the recursion.
*/
g_assert(!processing);
processing = true;
replay_save_instructions();
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_read_events();
} else if (replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
replay_save_events();
}
processing = false;
}
bool replay_has_event(void)
{
bool res = false;
if (replay_mode == REPLAY_MODE_PLAY) {
g_assert(replay_mutex_locked());
replay_account_executed_instructions();
res = EVENT_CHECKPOINT <= replay_state.data_kind
&& replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
res = res || (EVENT_ASYNC <= replay_state.data_kind
&& replay_state.data_kind <= EVENT_ASYNC_LAST);
}
return res;
}
G_NORETURN void replay_sync_error(const char *error)
{
error_report("%s (insn total %"PRId64"/%d left, event %d is %s)", error,
replay_state.current_icount, replay_state.instruction_count,
replay_state.current_event,
replay_event_name(replay_state.data_kind));
abort();
}
static void replay_enable(const char *fname, int mode)
{
const char *fmode = NULL;
assert(!replay_file);
switch (mode) {
case REPLAY_MODE_RECORD:
fmode = "wb";
break;
case REPLAY_MODE_PLAY:
fmode = "rb";
break;
default:
fprintf(stderr, "Replay: internal error: invalid replay mode\n");
exit(1);
}
atexit(replay_finish);
replay_file = fopen(fname, fmode);
if (replay_file == NULL) {
fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
exit(1);
}
replay_filename = g_strdup(fname);
replay_mode = mode;
replay_mutex_init();
replay_state.data_kind = -1;
replay_state.instruction_count = 0;
replay_state.current_icount = 0;
replay_state.current_event = 0;
replay_state.has_unread_data = 0;
/* skip file header for RECORD and check it for PLAY */
if (replay_mode == REPLAY_MODE_RECORD) {
fseek(replay_file, HEADER_SIZE, SEEK_SET);
} else if (replay_mode == REPLAY_MODE_PLAY) {
unsigned int version = replay_get_dword();
if (version != REPLAY_VERSION) {
fprintf(stderr, "Replay: invalid input log file version\n");
exit(1);
}
/* go to the beginning */
fseek(replay_file, HEADER_SIZE, SEEK_SET);
replay_fetch_data_kind();
}
runstate_replay_enable();
replay_init_events();
}
void replay_configure(QemuOpts *opts)
{
const char *fname;
const char *rr;
ReplayMode mode = REPLAY_MODE_NONE;
Location loc;
if (!opts) {
return;
}
loc_push_none(&loc);
qemu_opts_loc_restore(opts);
rr = qemu_opt_get(opts, "rr");
if (!rr) {
/* Just enabling icount */
goto out;
} else if (!strcmp(rr, "record")) {
mode = REPLAY_MODE_RECORD;
} else if (!strcmp(rr, "replay")) {
mode = REPLAY_MODE_PLAY;
} else {
error_report("Invalid icount rr option: %s", rr);
exit(1);
}
fname = qemu_opt_get(opts, "rrfile");
if (!fname) {
error_report("File name not specified for replay");
exit(1);
}
replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
replay_vmstate_register();
replay_enable(fname, mode);
out:
loc_pop(&loc);
}
void replay_start(void)
{
if (replay_mode == REPLAY_MODE_NONE) {
return;
}
if (replay_blockers) {
error_reportf_err(replay_blockers->data, "Record/replay: ");
exit(1);
}
if (!icount_enabled()) {
error_report("Please enable icount to use record/replay");
exit(1);
}
/* Timer for snapshotting will be set up here. */
replay_enable_events();
}
void replay_finish(void)
{
if (replay_mode == REPLAY_MODE_NONE) {
return;
}
replay_save_instructions();
/* finalize the file */
if (replay_file) {
if (replay_mode == REPLAY_MODE_RECORD) {
/*
* Can't do it in the signal handler, therefore
* add shutdown event here for the case of Ctrl-C.
*/
replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
/* write end event */
replay_put_event(EVENT_END);
/* write header */
fseek(replay_file, 0, SEEK_SET);
replay_put_dword(REPLAY_VERSION);
}
fclose(replay_file);
replay_file = NULL;
}
g_free(replay_filename);
replay_filename = NULL;
g_free(replay_snapshot);
replay_snapshot = NULL;
replay_finish_events();
replay_mode = REPLAY_MODE_NONE;
}
void replay_add_blocker(const char *feature)
{
Error *reason = NULL;
error_setg(&reason, "Record/replay is not supported with %s",
feature);
replay_blockers = g_slist_prepend(replay_blockers, reason);
}
const char *replay_get_filename(void)
{
return replay_filename;
}
+102
View File
@@ -0,0 +1,102 @@
#include "qemu/osdep.h"
#include "system/replay.h"
#include "ui/input.h"
void replay_input_event(QemuConsole *src, QemuInputEvent *evt)
{
qemu_input_event_send_impl(src, evt);
}
void replay_input_sync_event(void)
{
qemu_input_event_sync_impl();
}
void replay_add_blocker(const char *feature)
{
}
void replay_audio_in_start(size_t *nsamples)
{
}
void replay_audio_in_sample_lr(uint64_t *left, uint64_t *right)
{
}
void replay_audio_in_finish(void)
{
}
void replay_audio_out(size_t *played)
{
}
void replay_breakpoint(void)
{
}
bool replay_can_snapshot(void)
{
return true;
}
void replay_configure(struct QemuOpts *opts)
{
}
void replay_flush_events(void)
{
}
void replay_gdb_attached(void)
{
}
bool replay_running_debug(void)
{
return false;
}
void replay_shutdown_request(ShutdownCause cause)
{
}
void replay_start(void)
{
}
void replay_vmstate_init(void)
{
}
#include "monitor/monitor.h"
#include "monitor/hmp.h"
#include "qapi/qapi-commands-replay.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
void hmp_info_replay(Monitor *mon, const QDict *qdict)
{
error_report("replay support not available");
}
void hmp_replay_break(Monitor *mon, const QDict *qdict)
{
error_report("replay support not available");
}
void hmp_replay_delete_break(Monitor *mon, const QDict *qdict)
{
error_report("replay support not available");
}
void hmp_replay_seek(Monitor *mon, const QDict *qdict)
{
error_report("replay support not available");
}
ReplayInfo *qmp_query_replay(Error **errp)
{
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"replay support not available");
return NULL;
}
void qmp_replay_break(int64_t icount, Error **errp)
{
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"replay support not available");
}
void qmp_replay_delete_break(Error **errp)
{
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"replay support not available");
}
void qmp_replay_seek(int64_t icount, Error **errp)
{
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"replay support not available");
}
+12
View File
@@ -0,0 +1,12 @@
replay_put_byte(uint8_t event) "%02x"
replay_put_event(uint8_t event) "%02x"
replay_put_word(uint16_t event) "%04x"
replay_put_dword(uint32_t event) "%08x"
replay_put_qword(uint64_t event) "%016" PRIx64
replay_get_byte(uint8_t byte) "%02x"
replay_get_word(uint16_t word) "%04x"
replay_get_dword(uint32_t dword) "%08x"
replay_get_qword(uint64_t qword) "%016" PRIx64
replay_fetch_data_kind(void) ""
replay_get_event(uint32_t current, uint8_t data) "#%u data=%02x"
replay_advance_current_icount(uint64_t current_icount, int diff) "current=%" PRIu64 " diff=%d"
+1
View File
@@ -0,0 +1 @@
#include "trace/trace-replay.h"