]> git.proxmox.com Git - mirror_qemu.git/blobdiff - replay/replay-internal.c
Drop superfluous conditionals around g_free()
[mirror_qemu.git] / replay / replay-internal.c
index 5835e8def3b48c353c17128ab5d2e445e30b34d9..77d0c82327ed5aeeee63f446bddc147d592af7eb 100644 (file)
  */
 
 #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();
+        }
     }
 }
 
@@ -65,7 +82,9 @@ void replay_put_array(const uint8_t *buf, size_t size)
 {
     if (replay_file) {
         replay_put_dword(size);
-        fwrite(buf, 1, size, replay_file);
+        if (fwrite(buf, 1, size, replay_file) != size) {
+            replay_write_error();
+        }
     }
 }
 
@@ -73,7 +92,11 @@ uint8_t replay_get_byte(void)
 {
     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;
 }
@@ -116,7 +139,7 @@ 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) {
-            error_report("replay read error");
+            replay_read_error();
         }
     }
 }
@@ -127,7 +150,7 @@ void replay_get_array_alloc(uint8_t **buf, size_t *size)
         *size = replay_get_dword();
         *buf = g_malloc(*size);
         if (fread(*buf, 1, *size, replay_file) != *size) {
-            error_report("replay read error");
+            replay_read_error();
         }
     }
 }
@@ -150,15 +173,16 @@ void replay_check_error(void)
 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);
             }
         }
@@ -167,41 +191,95 @@ void replay_fetch_data_kind(void)
 
 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());
     }
 }