Record/replay fixes for replay_kernel tests
- add a 32 bit x86 replay test case - fix some typos - use modern snapshot setting for tests - update replay_dump for current ABI - remove stale replay variables - improve kdoc for ReplayState - introduce common error path for replay - always fully drain chardevs when in replay - catch unexpected waitio on playback - remove flaky tags from replay_kernel tests -----BEGIN PGP SIGNATURE----- iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmWcAJgACgkQ+9DbCVqe KkS/TQf+PuIPtuX71ENajfRBjz6450IbGqLUJ1HEaPGYGRj+fR6rg5g5u8qaBrT7 TUv9ef9L22NtyL+Gbs1OGpGDWKoqV6RQc+A/MHa8IKFpcS24nUo3k4psIC6NSGRH 6w3++fPC1Q5cDk9Lei3Qt8fXzcnUZz+NTiIK05aC0xh7D6uGfdADvKqHeLav7qi+ X2ztNdBsy/WJWCuWcMVzb/dGwDBtuyyxvqTD4EF+zn+gSYq9od2G8XdF+0o6ZVLM mXEHwNwB6UjOkLt2cYaay59SXcJFvwxKbEGTDnA7T+kgd3rknuBaWdVBIazoSPQh +522nPz5qq/3wO1l7+iQXuvd38fWyw== =nKRx -----END PGP SIGNATURE----- Merge tag 'pull-replay-fixes-080124-1' of https://gitlab.com/stsquad/qemu into staging Record/replay fixes for replay_kernel tests - add a 32 bit x86 replay test case - fix some typos - use modern snapshot setting for tests - update replay_dump for current ABI - remove stale replay variables - improve kdoc for ReplayState - introduce common error path for replay - always fully drain chardevs when in replay - catch unexpected waitio on playback - remove flaky tags from replay_kernel tests # -----BEGIN PGP SIGNATURE----- # # iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmWcAJgACgkQ+9DbCVqe # KkS/TQf+PuIPtuX71ENajfRBjz6450IbGqLUJ1HEaPGYGRj+fR6rg5g5u8qaBrT7 # TUv9ef9L22NtyL+Gbs1OGpGDWKoqV6RQc+A/MHa8IKFpcS24nUo3k4psIC6NSGRH # 6w3++fPC1Q5cDk9Lei3Qt8fXzcnUZz+NTiIK05aC0xh7D6uGfdADvKqHeLav7qi+ # X2ztNdBsy/WJWCuWcMVzb/dGwDBtuyyxvqTD4EF+zn+gSYq9od2G8XdF+0o6ZVLM # mXEHwNwB6UjOkLt2cYaay59SXcJFvwxKbEGTDnA7T+kgd3rknuBaWdVBIazoSPQh # +522nPz5qq/3wO1l7+iQXuvd38fWyw== # =nKRx # -----END PGP SIGNATURE----- # gpg: Signature made Mon 08 Jan 2024 14:03:04 GMT # gpg: using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44 # gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [full] # Primary key fingerprint: 6685 AE99 E751 67BC AFC8 DF35 FBD0 DB09 5A9E 2A44 * tag 'pull-replay-fixes-080124-1' of https://gitlab.com/stsquad/qemu: tests/avocado: remove skips from replay_kernel chardev: force write all when recording replay logs replay: stop us hanging in rr_wait_io_event replay/replay-char: use report_sync_error replay: introduce a central report point for sync errors replay: make has_unread_data a bool replay: add proper kdoc for ReplayState replay: remove host_clock_last scripts/replay_dump: track total number of instructions scripts/replay-dump: update to latest format tests/avocado: modernise the drive args for replay_linux tests/avocado: fix typo in replay_linux tests/avocado: add a simple i386 replay kernel test Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
c1df5b4f16
@ -109,7 +109,7 @@ static void rr_wait_io_event(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
while (all_cpu_threads_idle()) {
|
||||
while (all_cpu_threads_idle() && replay_can_wait()) {
|
||||
rr_stop_kick_timer();
|
||||
qemu_cond_wait_iothread(first_cpu->halt_cond);
|
||||
}
|
||||
|
@ -171,6 +171,18 @@ int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all)
|
||||
return res;
|
||||
}
|
||||
|
||||
if (replay_mode == REPLAY_MODE_RECORD) {
|
||||
/*
|
||||
* When recording we don't want temporary conditions to
|
||||
* perturb the result. By ensuring we write everything we can
|
||||
* while recording we avoid playback being out of sync if it
|
||||
* doesn't encounter the same temporary conditions (usually
|
||||
* triggered by external programs not reading the chardev fast
|
||||
* enough and pipes filling up).
|
||||
*/
|
||||
write_all = true;
|
||||
}
|
||||
|
||||
res = qemu_chr_write_buffer(s, buf, len, &offset, write_all);
|
||||
|
||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
||||
|
@ -70,6 +70,11 @@ int replay_get_instructions(void);
|
||||
/*! Updates instructions counter in replay mode. */
|
||||
void replay_account_executed_instructions(void);
|
||||
|
||||
/**
|
||||
* replay_can_wait: check if we should pause for wait-io
|
||||
*/
|
||||
bool replay_can_wait(void);
|
||||
|
||||
/* Processing clocks and other time sources */
|
||||
|
||||
/*! Save the specified clock */
|
||||
|
@ -113,8 +113,7 @@ void replay_char_write_event_load(int *res, int *offset)
|
||||
*offset = replay_get_dword();
|
||||
replay_finish_event();
|
||||
} else {
|
||||
error_report("Missing character write event in the replay log");
|
||||
exit(1);
|
||||
replay_sync_error("Missing character write event in the replay log");
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,8 +134,7 @@ int replay_char_read_all_load(uint8_t *buf)
|
||||
replay_finish_event();
|
||||
return res;
|
||||
} else {
|
||||
error_report("Missing character read all event in the replay log");
|
||||
exit(1);
|
||||
replay_sync_error("Missing character read all event in the replay log");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,11 +175,12 @@ void replay_fetch_data_kind(void)
|
||||
if (replay_file) {
|
||||
if (!replay_state.has_unread_data) {
|
||||
replay_state.data_kind = replay_get_byte();
|
||||
replay_state.current_event++;
|
||||
if (replay_state.data_kind == EVENT_INSTRUCTION) {
|
||||
replay_state.instruction_count = replay_get_dword();
|
||||
}
|
||||
replay_check_error();
|
||||
replay_state.has_unread_data = 1;
|
||||
replay_state.has_unread_data = true;
|
||||
if (replay_state.data_kind >= EVENT_COUNT) {
|
||||
error_report("Replay: unknown event kind %d",
|
||||
replay_state.data_kind);
|
||||
@ -191,7 +192,7 @@ void replay_fetch_data_kind(void)
|
||||
|
||||
void replay_finish_event(void)
|
||||
{
|
||||
replay_state.has_unread_data = 0;
|
||||
replay_state.has_unread_data = false;
|
||||
replay_fetch_data_kind();
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,12 @@ typedef enum ReplayAsyncEventKind {
|
||||
REPLAY_ASYNC_COUNT
|
||||
} ReplayAsyncEventKind;
|
||||
|
||||
/* Any changes to order/number of events will need to bump REPLAY_VERSION */
|
||||
/*
|
||||
* 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,
|
||||
@ -63,26 +68,33 @@ enum ReplayEvents {
|
||||
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
|
||||
*/
|
||||
typedef struct ReplayState {
|
||||
/*! Cached clock values. */
|
||||
int64_t cached_clock[REPLAY_CLOCK_COUNT];
|
||||
/*! Current icount - number of processed instructions. */
|
||||
uint64_t current_icount;
|
||||
/*! Number of instructions to be executed before other events happen. */
|
||||
int instruction_count;
|
||||
/*! Type of the currently executed event. */
|
||||
unsigned int current_event;
|
||||
unsigned int data_kind;
|
||||
/*! Flag which indicates that event is not processed yet. */
|
||||
unsigned int has_unread_data;
|
||||
/*! Temporary variable for saving current log offset. */
|
||||
bool has_unread_data;
|
||||
uint64_t file_offset;
|
||||
/*! Next block operation id.
|
||||
This counter is global, because requests from different
|
||||
block devices should not get overlapping ids. */
|
||||
uint64_t block_request_id;
|
||||
/*! Prior value of the host clock */
|
||||
uint64_t host_clock_last;
|
||||
/*! Asynchronous event id read from the log */
|
||||
uint64_t read_event_id;
|
||||
} ReplayState;
|
||||
extern ReplayState replay_state;
|
||||
@ -183,6 +195,16 @@ 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.
|
||||
|
@ -47,16 +47,17 @@ static int replay_post_load(void *opaque, int version_id)
|
||||
|
||||
static const VMStateDescription vmstate_replay = {
|
||||
.name = "replay",
|
||||
.version_id = 2,
|
||||
.minimum_version_id = 2,
|
||||
.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_UINT32(has_unread_data, ReplayState),
|
||||
VMSTATE_BOOL(has_unread_data, ReplayState),
|
||||
VMSTATE_UINT64(file_offset, ReplayState),
|
||||
VMSTATE_UINT64(block_request_id, ReplayState),
|
||||
VMSTATE_UINT64(read_event_id, ReplayState),
|
||||
|
132
replay/replay.c
132
replay/replay.c
@ -38,6 +38,107 @@ static GSList *replay_blockers;
|
||||
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;
|
||||
@ -226,6 +327,15 @@ bool replay_has_event(void)
|
||||
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;
|
||||
@ -258,6 +368,7 @@ static void replay_enable(const char *fname, int mode)
|
||||
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 */
|
||||
@ -338,6 +449,27 @@ void replay_start(void)
|
||||
replay_enable_events();
|
||||
}
|
||||
|
||||
/*
|
||||
* For none/record the answer is yes.
|
||||
*/
|
||||
bool replay_can_wait(void)
|
||||
{
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
/*
|
||||
* For playback we shouldn't ever be at a point we wait. If
|
||||
* the instruction count has reached zero and we have an
|
||||
* unconsumed event we should go around again and consume it.
|
||||
*/
|
||||
if (replay_state.instruction_count == 0 && replay_state.has_unread_data) {
|
||||
return false;
|
||||
} else {
|
||||
replay_sync_error("Playback shouldn't have to iowait");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void replay_finish(void)
|
||||
{
|
||||
if (replay_mode == REPLAY_MODE_NONE) {
|
||||
|
@ -21,6 +21,7 @@
|
||||
import argparse
|
||||
import struct
|
||||
from collections import namedtuple
|
||||
from os import path
|
||||
|
||||
# This mirrors some of the global replay state which some of the
|
||||
# stream loading refers to. Some decoders may read the next event so
|
||||
@ -82,6 +83,12 @@ def read_qword(fin):
|
||||
"Read a 64 bit word"
|
||||
return struct.unpack('>Q', fin.read(8))[0]
|
||||
|
||||
def read_array(fin):
|
||||
"Read a sized array"
|
||||
size = read_dword(fin)
|
||||
data = fin.read(size)
|
||||
return data
|
||||
|
||||
# Generic decoder structure
|
||||
Decoder = namedtuple("Decoder", "eid name fn")
|
||||
|
||||
@ -115,6 +122,11 @@ def decode_unimp(eid, name, _unused_dumpfile):
|
||||
print("%s not handled - will now stop" % (name))
|
||||
return False
|
||||
|
||||
def decode_plain(eid, name, _unused_dumpfile):
|
||||
"Plain events without additional data"
|
||||
print_event(eid, name, "no data")
|
||||
return True
|
||||
|
||||
# Checkpoint decoder
|
||||
def swallow_async_qword(eid, name, dumpfile):
|
||||
"Swallow a qword of data without looking at it"
|
||||
@ -145,10 +157,19 @@ def decode_async(eid, name, dumpfile):
|
||||
|
||||
return call_decode(async_decode_table, async_event_kind, dumpfile)
|
||||
|
||||
total_insns = 0
|
||||
|
||||
def decode_instruction(eid, name, dumpfile):
|
||||
global total_insns
|
||||
ins_diff = read_dword(dumpfile)
|
||||
print_event(eid, name, "0x%x" % (ins_diff))
|
||||
total_insns += ins_diff
|
||||
print_event(eid, name, "+ %d -> %d" % (ins_diff, total_insns))
|
||||
return True
|
||||
|
||||
def decode_char_write(eid, name, dumpfile):
|
||||
res = read_dword(dumpfile)
|
||||
offset = read_dword(dumpfile)
|
||||
print_event(eid, name, "%d -> %d" % (offset, res))
|
||||
return True
|
||||
|
||||
def decode_audio_out(eid, name, dumpfile):
|
||||
@ -189,14 +210,19 @@ def decode_clock(eid, name, dumpfile):
|
||||
print_event(eid, name, "0x%x" % (clock_data))
|
||||
return True
|
||||
|
||||
def decode_random(eid, name, dumpfile):
|
||||
ret = read_dword(dumpfile)
|
||||
data = read_array(dumpfile)
|
||||
print_event(eid, "%d bytes of random data" % len(data))
|
||||
return True
|
||||
|
||||
# pre-MTTCG merge
|
||||
v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
|
||||
Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
|
||||
Decoder(2, "EVENT_EXCEPTION", decode_unimp),
|
||||
Decoder(2, "EVENT_EXCEPTION", decode_plain),
|
||||
Decoder(3, "EVENT_ASYNC", decode_async),
|
||||
Decoder(4, "EVENT_SHUTDOWN", decode_unimp),
|
||||
Decoder(5, "EVENT_CHAR_WRITE", decode_unimp),
|
||||
Decoder(5, "EVENT_CHAR_WRITE", decode_char_write),
|
||||
Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp),
|
||||
Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
|
||||
Decoder(8, "EVENT_CLOCK_HOST", decode_clock),
|
||||
@ -215,10 +241,10 @@ v5_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
|
||||
# post-MTTCG merge, AUDIO support added
|
||||
v6_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
|
||||
Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
|
||||
Decoder(2, "EVENT_EXCEPTION", decode_unimp),
|
||||
Decoder(2, "EVENT_EXCEPTION", decode_plain),
|
||||
Decoder(3, "EVENT_ASYNC", decode_async),
|
||||
Decoder(4, "EVENT_SHUTDOWN", decode_unimp),
|
||||
Decoder(5, "EVENT_CHAR_WRITE", decode_unimp),
|
||||
Decoder(5, "EVENT_CHAR_WRITE", decode_char_write),
|
||||
Decoder(6, "EVENT_CHAR_READ_ALL", decode_unimp),
|
||||
Decoder(7, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
|
||||
Decoder(8, "EVENT_AUDIO_OUT", decode_audio_out),
|
||||
@ -250,7 +276,7 @@ v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
|
||||
Decoder(10, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp),
|
||||
Decoder(11, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp),
|
||||
Decoder(12, "EVENT_SHUTDOWN___MAX", decode_unimp),
|
||||
Decoder(13, "EVENT_CHAR_WRITE", decode_unimp),
|
||||
Decoder(13, "EVENT_CHAR_WRITE", decode_char_write),
|
||||
Decoder(14, "EVENT_CHAR_READ_ALL", decode_unimp),
|
||||
Decoder(15, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
|
||||
Decoder(16, "EVENT_AUDIO_OUT", decode_audio_out),
|
||||
@ -268,6 +294,48 @@ v7_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
|
||||
Decoder(28, "EVENT_CP_RESET", decode_checkpoint),
|
||||
]
|
||||
|
||||
v12_event_table = [Decoder(0, "EVENT_INSTRUCTION", decode_instruction),
|
||||
Decoder(1, "EVENT_INTERRUPT", decode_interrupt),
|
||||
Decoder(2, "EVENT_EXCEPTION", decode_plain),
|
||||
Decoder(3, "EVENT_ASYNC", decode_async),
|
||||
Decoder(4, "EVENT_ASYNC", decode_async),
|
||||
Decoder(5, "EVENT_ASYNC", decode_async),
|
||||
Decoder(6, "EVENT_ASYNC", decode_async),
|
||||
Decoder(6, "EVENT_ASYNC", decode_async),
|
||||
Decoder(8, "EVENT_ASYNC", decode_async),
|
||||
Decoder(9, "EVENT_ASYNC", decode_async),
|
||||
Decoder(10, "EVENT_ASYNC", decode_async),
|
||||
Decoder(11, "EVENT_SHUTDOWN", decode_unimp),
|
||||
Decoder(12, "EVENT_SHUTDOWN_HOST_ERR", decode_unimp),
|
||||
Decoder(13, "EVENT_SHUTDOWN_HOST_QMP_QUIT", decode_unimp),
|
||||
Decoder(14, "EVENT_SHUTDOWN_HOST_QMP_RESET", decode_unimp),
|
||||
Decoder(14, "EVENT_SHUTDOWN_HOST_SIGNAL", decode_unimp),
|
||||
Decoder(15, "EVENT_SHUTDOWN_HOST_UI", decode_unimp),
|
||||
Decoder(16, "EVENT_SHUTDOWN_GUEST_SHUTDOWN", decode_unimp),
|
||||
Decoder(17, "EVENT_SHUTDOWN_GUEST_RESET", decode_unimp),
|
||||
Decoder(18, "EVENT_SHUTDOWN_GUEST_PANIC", decode_unimp),
|
||||
Decoder(19, "EVENT_SHUTDOWN_GUEST_SUBSYSTEM_RESET", decode_unimp),
|
||||
Decoder(20, "EVENT_SHUTDOWN_GUEST_SNAPSHOT_LOAD", decode_unimp),
|
||||
Decoder(21, "EVENT_SHUTDOWN___MAX", decode_unimp),
|
||||
Decoder(22, "EVENT_CHAR_WRITE", decode_char_write),
|
||||
Decoder(23, "EVENT_CHAR_READ_ALL", decode_unimp),
|
||||
Decoder(24, "EVENT_CHAR_READ_ALL_ERROR", decode_unimp),
|
||||
Decoder(25, "EVENT_AUDIO_IN", decode_unimp),
|
||||
Decoder(26, "EVENT_AUDIO_OUT", decode_audio_out),
|
||||
Decoder(27, "EVENT_RANDOM", decode_random),
|
||||
Decoder(28, "EVENT_CLOCK_HOST", decode_clock),
|
||||
Decoder(29, "EVENT_CLOCK_VIRTUAL_RT", decode_clock),
|
||||
Decoder(30, "EVENT_CP_CLOCK_WARP_START", decode_checkpoint),
|
||||
Decoder(31, "EVENT_CP_CLOCK_WARP_ACCOUNT", decode_checkpoint),
|
||||
Decoder(32, "EVENT_CP_RESET_REQUESTED", decode_checkpoint),
|
||||
Decoder(33, "EVENT_CP_SUSPEND_REQUESTED", decode_checkpoint),
|
||||
Decoder(34, "EVENT_CP_CLOCK_VIRTUAL", decode_checkpoint),
|
||||
Decoder(35, "EVENT_CP_CLOCK_HOST", decode_checkpoint),
|
||||
Decoder(36, "EVENT_CP_CLOCK_VIRTUAL_RT", decode_checkpoint),
|
||||
Decoder(37, "EVENT_CP_INIT", decode_checkpoint_init),
|
||||
Decoder(38, "EVENT_CP_RESET", decode_checkpoint),
|
||||
]
|
||||
|
||||
def parse_arguments():
|
||||
"Grab arguments for script"
|
||||
parser = argparse.ArgumentParser()
|
||||
@ -278,14 +346,18 @@ def parse_arguments():
|
||||
def decode_file(filename):
|
||||
"Decode a record/replay dump"
|
||||
dumpfile = open(filename, "rb")
|
||||
|
||||
dumpsize = path.getsize(filename)
|
||||
# read and throwaway the header
|
||||
version = read_dword(dumpfile)
|
||||
junk = read_qword(dumpfile)
|
||||
|
||||
# see REPLAY_VERSION
|
||||
print("HEADER: version 0x%x" % (version))
|
||||
|
||||
if version == 0xe02007:
|
||||
if version == 0xe0200c:
|
||||
event_decode_table = v12_event_table
|
||||
replay_state.checkpoint_start = 30
|
||||
elif version == 0xe02007:
|
||||
event_decode_table = v7_event_table
|
||||
replay_state.checkpoint_start = 12
|
||||
elif version == 0xe02006:
|
||||
@ -299,8 +371,13 @@ def decode_file(filename):
|
||||
decode_ok = True
|
||||
while decode_ok:
|
||||
event = read_event(dumpfile)
|
||||
decode_ok = call_decode(event_decode_table, event, dumpfile)
|
||||
decode_ok = call_decode(event_decode_table, event,
|
||||
dumpfile)
|
||||
except Exception as inst:
|
||||
print(f"error {inst}")
|
||||
|
||||
finally:
|
||||
print(f"Reached {dumpfile.tell()} of {dumpsize} bytes")
|
||||
dumpfile.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -82,13 +82,26 @@ class ReplayKernelBase(LinuxKernelTest):
|
||||
|
||||
class ReplayKernelNormal(ReplayKernelBase):
|
||||
|
||||
# See https://gitlab.com/qemu-project/qemu/-/issues/2010
|
||||
@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test sometimes gets stuck')
|
||||
def test_i386_pc(self):
|
||||
"""
|
||||
:avocado: tags=arch:i386
|
||||
:avocado: tags=machine:pc
|
||||
"""
|
||||
kernel_url = ('https://storage.tuxboot.com/20230331/i386/bzImage')
|
||||
kernel_hash = 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956'
|
||||
kernel_path = self.fetch_asset(kernel_url,
|
||||
asset_hash=kernel_hash,
|
||||
algorithm = "sha256")
|
||||
|
||||
kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0'
|
||||
console_pattern = 'VFS: Cannot open root device'
|
||||
|
||||
self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
|
||||
|
||||
def test_x86_64_pc(self):
|
||||
"""
|
||||
:avocado: tags=arch:x86_64
|
||||
:avocado: tags=machine:pc
|
||||
:avocado: tags=flaky
|
||||
"""
|
||||
kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora'
|
||||
'/linux/releases/29/Everything/x86_64/os/images/pxeboot'
|
||||
@ -119,8 +132,6 @@ class ReplayKernelNormal(ReplayKernelBase):
|
||||
|
||||
self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5)
|
||||
|
||||
# See https://gitlab.com/qemu-project/qemu/-/issues/2013
|
||||
@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
|
||||
def test_mips64el_malta(self):
|
||||
"""
|
||||
This test requires the ar tool to extract "data.tar.gz" from
|
||||
@ -136,7 +147,6 @@ class ReplayKernelNormal(ReplayKernelBase):
|
||||
|
||||
:avocado: tags=arch:mips64el
|
||||
:avocado: tags=machine:malta
|
||||
:avocado: tags=flaky
|
||||
"""
|
||||
deb_url = ('http://snapshot.debian.org/archive/debian/'
|
||||
'20130217T032700Z/pool/main/l/linux-2.6/'
|
||||
@ -184,13 +194,10 @@ class ReplayKernelNormal(ReplayKernelBase):
|
||||
|
||||
self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1)
|
||||
|
||||
@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
|
||||
|
||||
def test_arm_cubieboard_initrd(self):
|
||||
"""
|
||||
:avocado: tags=arch:arm
|
||||
:avocado: tags=machine:cubieboard
|
||||
:avocado: tags=flaky
|
||||
"""
|
||||
deb_url = ('https://apt.armbian.com/pool/main/l/'
|
||||
'linux-5.10.16-sunxi/linux-image-current-sunxi_21.02.2_armhf.deb')
|
||||
@ -338,7 +345,6 @@ class ReplayKernelNormal(ReplayKernelBase):
|
||||
file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
|
||||
self.do_test_advcal_2018(file_path, 'sanity-clause.elf')
|
||||
|
||||
@skip("Test currently broken") # Console stuck as of 5.2-rc1
|
||||
def test_microblaze_s3adsp1800(self):
|
||||
"""
|
||||
:avocado: tags=arch:microblaze
|
||||
@ -373,7 +379,6 @@ class ReplayKernelNormal(ReplayKernelBase):
|
||||
file_path = self.fetch_asset(tar_url, asset_hash=tar_hash)
|
||||
self.do_test_advcal_2018(file_path, 'vmlinux')
|
||||
|
||||
@skip("nios2 emulation is buggy under record/replay")
|
||||
def test_nios2_10m50(self):
|
||||
"""
|
||||
:avocado: tags=arch:nios2
|
||||
|
@ -48,12 +48,15 @@ class ReplayLinux(LinuxTest):
|
||||
bus_string = ''
|
||||
if self.bus:
|
||||
bus_string = ',bus=%s.%d' % (self.bus, id,)
|
||||
vm.add_args('-drive', 'file=%s,snapshot,id=disk%s,if=none' % (path, id))
|
||||
vm.add_args('-drive', 'file=%s,snapshot=on,id=disk%s,if=none' % (path, id))
|
||||
vm.add_args('-drive',
|
||||
'driver=blkreplay,id=disk%s-rr,if=none,image=disk%s' % (id, id))
|
||||
vm.add_args('-device',
|
||||
'%s,drive=disk%s-rr%s' % (device, id, bus_string))
|
||||
|
||||
def vm_add_cdrom(self, vm, path, id, device):
|
||||
vm.add_args('-drive', 'file=%s,id=disk%s,if=none,media=cdrom' % (path, id))
|
||||
|
||||
def launch_and_wait(self, record, args, shift):
|
||||
self.require_netdev('user')
|
||||
vm = self.get_vm()
|
||||
@ -65,7 +68,7 @@ class ReplayLinux(LinuxTest):
|
||||
if args:
|
||||
vm.add_args(*args)
|
||||
self.vm_add_disk(vm, self.boot_path, 0, self.hdd)
|
||||
self.vm_add_disk(vm, self.cloudinit_path, 1, self.cd)
|
||||
self.vm_add_cdrom(vm, self.cloudinit_path, 1, self.cd)
|
||||
logger = logging.getLogger('replay')
|
||||
if record:
|
||||
logger.info('recording the execution...')
|
||||
@ -94,7 +97,7 @@ class ReplayLinux(LinuxTest):
|
||||
else:
|
||||
vm.event_wait('SHUTDOWN', self.timeout)
|
||||
vm.wait()
|
||||
logger.info('successfully fihished the replay')
|
||||
logger.info('successfully finished the replay')
|
||||
elapsed = time.time() - start_time
|
||||
logger.info('elapsed time %.2f sec' % elapsed)
|
||||
return elapsed
|
||||
|
Loading…
Reference in New Issue
Block a user