*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "sysemu/replay.h"
+#include "sysemu/runstate.h"
#include "replay-internal.h"
#include "qemu/error-report.h"
-#include "sysemu/sysemu.h"
-
-unsigned int replay_data_kind = -1;
-static unsigned int replay_has_unread_data;
+#include "qemu/main-loop.h"
/* Mutex to protect reading and writing events to the log.
- replay_data_kind and replay_has_unread_data are also protected
+ 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);
+}
+
void replay_put_byte(uint8_t byte)
{
if (replay_file) {
- putc(byte, replay_file);
+ if (putc(byte, replay_file) == EOF) {
+ replay_write_error();
+ }
}
}
{
if (replay_file) {
replay_put_dword(size);
- fwrite(buf, 1, size, replay_file);
+ if (fwrite(buf, 1, size, replay_file) != size) {
+ replay_write_error();
+ }
}
}
{
uint8_t byte = 0;
if (replay_file) {
- byte = getc(replay_file);
+ int r = getc(replay_file);
+ if (r == EOF) {
+ replay_read_error();
+ }
+ byte = r;
}
return byte;
}
if (replay_file) {
*size = replay_get_dword();
if (fread(buf, 1, *size, replay_file) != *size) {
- error_report("replay read error");
+ replay_read_error();
}
}
}
*size = replay_get_dword();
*buf = g_malloc(*size);
if (fread(*buf, 1, *size, replay_file) != *size) {
- error_report("replay read error");
+ replay_read_error();
}
}
}
void replay_fetch_data_kind(void)
{
if (replay_file) {
- if (!replay_has_unread_data) {
- replay_data_kind = replay_get_byte();
- if (replay_data_kind == EVENT_INSTRUCTION) {
- replay_state.instructions_count = replay_get_dword();
+ if (!replay_state.has_unread_data) {
+ replay_state.data_kind = replay_get_byte();
+ if (replay_state.data_kind == EVENT_INSTRUCTION) {
+ replay_state.instruction_count = replay_get_dword();
}
replay_check_error();
- replay_has_unread_data = 1;
- if (replay_data_kind >= EVENT_COUNT) {
- error_report("Replay: unknown event kind %d", replay_data_kind);
+ replay_state.has_unread_data = 1;
+ 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_has_unread_data = 0;
+ replay_state.has_unread_data = 0;
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;
}
-void replay_mutex_destroy(void)
+bool replay_mutex_locked(void)
{
- qemu_mutex_destroy(&lock);
+ return replay_locked;
}
+/* Ordering constraints, replay_lock must be taken before BQL */
void replay_mutex_lock(void)
{
- qemu_mutex_lock(&lock);
+ if (replay_mode != REPLAY_MODE_NONE) {
+ unsigned long id;
+ g_assert(!qemu_mutex_iothread_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)
{
- qemu_mutex_unlock(&lock);
+ 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);
+ }
}
-/*! Saves cached instructions. */
-void replay_save_instructions(void)
+void replay_advance_current_icount(uint64_t current_icount)
{
- if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
- replay_mutex_lock();
- int diff = (int)(replay_get_current_step() - replay_state.current_step);
+ int diff = (int)(current_icount - replay_state.current_icount);
+
+ /* Time can only go forward */
+ assert(diff >= 0);
+
+ if (replay_mode == REPLAY_MODE_RECORD) {
if (diff > 0) {
replay_put_event(EVENT_INSTRUCTION);
replay_put_dword(diff);
- replay_state.current_step += 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));
}
- replay_mutex_unlock();
+ }
+}
+
+/*! 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());
}
}