#include "qemu/osdep.h"
#include "sysemu/replay.h"
+#include "sysemu/runstate.h"
#include "replay-internal.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
-#include "sysemu/sysemu.h"
/* Mutex to protect reading and writing events to the log.
data_kind and has_unread_data are also protected
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;
if (!replay_state.has_unread_data) {
replay_state.data_kind = replay_get_byte();
if (replay_state.data_kind == EVENT_INSTRUCTION) {
- replay_state.instructions_count = replay_get_dword();
+ replay_state.instruction_count = replay_get_dword();
}
replay_check_error();
replay_state.has_unread_data = 1;
void replay_mutex_init(void)
{
qemu_mutex_init(&lock);
+ qemu_cond_init(&mutex_cond);
/* Hold the mutex while we start-up */
- qemu_mutex_lock(&lock);
replay_locked = true;
+ ++mutex_tail;
}
bool replay_mutex_locked(void)
void replay_mutex_lock(void)
{
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);
}
}
{
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_step(uint64_t current_step)
+void replay_advance_current_icount(uint64_t current_icount)
{
- 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 (diff > 0) {
- replay_put_event(EVENT_INSTRUCTION);
- replay_put_dword(diff);
- replay_state.current_step += diff;
+ 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));
+ }
}
}
{
if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
g_assert(replay_mutex_locked());
- replay_advance_current_step(replay_get_current_step());
+ replay_advance_current_icount(replay_get_current_icount());
}
}