aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sysemu/replay.h6
-rw-r--r--replay/Makefile.objs1
-rw-r--r--replay/replay-events.c227
-rw-r--r--replay/replay-internal.h29
4 files changed, 263 insertions, 0 deletions
diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h
index 8915523eaf..c2a7651d7a 100644
--- a/include/sysemu/replay.h
+++ b/include/sysemu/replay.h
@@ -43,5 +43,11 @@ bool replay_interrupt(void);
Returns true, when interrupt request is pending */
bool replay_has_interrupt(void);
+/* Asynchronous events queue */
+
+/*! Disables storing events in the queue */
+void replay_disable_events(void);
+/*! Returns true when saving events is enabled */
+bool replay_events_enabled(void);
#endif
diff --git a/replay/Makefile.objs b/replay/Makefile.objs
index e67a932b73..6b439c2c23 100644
--- a/replay/Makefile.objs
+++ b/replay/Makefile.objs
@@ -1,2 +1,3 @@
common-obj-y += replay.o
common-obj-y += replay-internal.o
+common-obj-y += replay-events.o
diff --git a/replay/replay-events.c b/replay/replay-events.c
new file mode 100644
index 0000000000..8dcc96d51e
--- /dev/null
+++ b/replay/replay-events.c
@@ -0,0 +1,227 @@
+/*
+ * 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-common.h"
+#include "qemu/error-report.h"
+#include "sysemu/replay.h"
+#include "replay-internal.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 unsigned int read_event_kind = -1;
+static uint64_t read_id = -1;
+static int read_checkpoint = -1;
+
+static bool events_enabled;
+
+/* Functions */
+
+static void replay_run_event(Event *event)
+{
+ switch (event->event_kind) {
+ default:
+ error_report("Replay: invalid async event ID (%d) in the queue",
+ event->event_kind);
+ exit(1);
+ break;
+ }
+}
+
+void replay_enable_events(void)
+{
+ events_enabled = true;
+}
+
+bool replay_has_events(void)
+{
+ return !QTAILQ_EMPTY(&events_list);
+}
+
+void replay_flush_events(void)
+{
+ replay_mutex_lock();
+ while (!QTAILQ_EMPTY(&events_list)) {
+ Event *event = QTAILQ_FIRST(&events_list);
+ replay_mutex_unlock();
+ replay_run_event(event);
+ replay_mutex_lock();
+ QTAILQ_REMOVE(&events_list, event, events);
+ g_free(event);
+ }
+ replay_mutex_unlock();
+}
+
+void replay_disable_events(void)
+{
+ if (replay_mode != REPLAY_MODE_NONE) {
+ events_enabled = false;
+ /* Flush events queue before waiting of completion */
+ replay_flush_events();
+ }
+}
+
+void replay_clear_events(void)
+{
+ replay_mutex_lock();
+ while (!QTAILQ_EMPTY(&events_list)) {
+ Event *event = QTAILQ_FIRST(&events_list);
+ QTAILQ_REMOVE(&events_list, event, events);
+
+ g_free(event);
+ }
+ replay_mutex_unlock();
+}
+
+/*! Adds specified async event to the queue */
+#if 0
+static 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_malloc0(sizeof(Event));
+ event->event_kind = event_kind;
+ event->opaque = opaque;
+ event->opaque2 = opaque2;
+ event->id = id;
+
+ replay_mutex_lock();
+ QTAILQ_INSERT_TAIL(&events_list, event, events);
+ replay_mutex_unlock();
+}
+#endif
+
+static void replay_save_event(Event *event, int checkpoint)
+{
+ if (replay_mode != REPLAY_MODE_PLAY) {
+ /* put the event into the file */
+ replay_put_event(EVENT_ASYNC);
+ replay_put_byte(checkpoint);
+ replay_put_byte(event->event_kind);
+
+ /* save event-specific data */
+ switch (event->event_kind) {
+ default:
+ error_report("Unknown ID %d of replay event", read_event_kind);
+ exit(1);
+ break;
+ }
+ }
+}
+
+/* Called with replay mutex locked */
+void replay_save_events(int checkpoint)
+{
+ while (!QTAILQ_EMPTY(&events_list)) {
+ Event *event = QTAILQ_FIRST(&events_list);
+ replay_save_event(event, checkpoint);
+
+ replay_mutex_unlock();
+ replay_run_event(event);
+ replay_mutex_lock();
+ QTAILQ_REMOVE(&events_list, event, events);
+ g_free(event);
+ }
+}
+
+static Event *replay_read_event(int checkpoint)
+{
+ Event *event;
+ if (read_event_kind == -1) {
+ read_checkpoint = replay_get_byte();
+ read_event_kind = replay_get_byte();
+ read_id = -1;
+ replay_check_error();
+ }
+
+ if (checkpoint != read_checkpoint) {
+ return NULL;
+ }
+
+ /* Events that has not to be in the queue */
+ switch (read_event_kind) {
+ default:
+ error_report("Unknown ID %d of replay event", read_event_kind);
+ exit(1);
+ break;
+ }
+
+ QTAILQ_FOREACH(event, &events_list, events) {
+ if (event->event_kind == read_event_kind
+ && (read_id == -1 || read_id == event->id)) {
+ break;
+ }
+ }
+
+ if (event) {
+ QTAILQ_REMOVE(&events_list, event, events);
+ } else {
+ return NULL;
+ }
+
+ /* Read event-specific data */
+
+ return event;
+}
+
+/* Called with replay mutex locked */
+void replay_read_events(int checkpoint)
+{
+ while (replay_data_kind == EVENT_ASYNC) {
+ Event *event = replay_read_event(checkpoint);
+ if (!event) {
+ break;
+ }
+ replay_mutex_unlock();
+ replay_run_event(event);
+ replay_mutex_lock();
+
+ g_free(event);
+ replay_finish_event();
+ read_event_kind = -1;
+ }
+}
+
+void replay_init_events(void)
+{
+ read_event_kind = -1;
+}
+
+void replay_finish_events(void)
+{
+ events_enabled = false;
+ replay_clear_events();
+}
+
+bool replay_events_enabled(void)
+{
+ return events_enabled;
+}
diff --git a/replay/replay-internal.h b/replay/replay-internal.h
index 5ff1c14287..23807ca413 100644
--- a/replay/replay-internal.h
+++ b/replay/replay-internal.h
@@ -21,9 +21,19 @@ enum ReplayEvents {
EVENT_INTERRUPT,
/* for emulated exceptions */
EVENT_EXCEPTION,
+ /* for async events */
+ EVENT_ASYNC,
EVENT_COUNT
};
+/* Asynchronous events IDs */
+
+enum ReplayAsyncEventKind {
+ REPLAY_ASYNC_COUNT
+};
+
+typedef enum ReplayAsyncEventKind ReplayAsyncEventKind;
+
typedef struct ReplayState {
/*! Current step - number of processed instructions and timer events. */
uint64_t current_step;
@@ -75,4 +85,23 @@ void replay_save_instructions(void);
\return true, if event was found */
bool replay_next_event_is(int event);
+/* Asynchronous events queue */
+
+/*! Initializes events' processing internals */
+void replay_init_events(void);
+/*! Clears internal data structures for events handling */
+void replay_finish_events(void);
+/*! Enables storing events in the queue */
+void replay_enable_events(void);
+/*! Flushes events queue */
+void replay_flush_events(void);
+/*! Clears events list before loading new VM state */
+void replay_clear_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(int checkpoint);
+/*! Read events from the file into the input queue */
+void replay_read_events(int checkpoint);
+
#endif