diff options
Diffstat (limited to 'qemu/replay/replay-events.c')
-rw-r--r-- | qemu/replay/replay-events.c | 311 |
1 files changed, 311 insertions, 0 deletions
diff --git a/qemu/replay/replay-events.c b/qemu/replay/replay-events.c new file mode 100644 index 000000000..3807245ae --- /dev/null +++ b/qemu/replay/replay-events.c @@ -0,0 +1,311 @@ +/* + * 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/osdep.h" +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "sysemu/replay.h" +#include "replay-internal.h" +#include "block/aio.h" +#include "ui/input.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) { + case REPLAY_ASYNC_EVENT_BH: + aio_bh_call(event->opaque); + break; + case REPLAY_ASYNC_EVENT_INPUT: + qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque); + qapi_free_InputEvent((InputEvent *)event->opaque); + break; + case REPLAY_ASYNC_EVENT_INPUT_SYNC: + qemu_input_event_sync_impl(); + break; + case REPLAY_ASYNC_EVENT_CHAR_READ: + replay_event_char_read_run(event->opaque); + break; + case REPLAY_ASYNC_EVENT_BLOCK: + aio_bh_call(event->opaque); + break; + 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 */ +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(); +} + +void replay_bh_schedule_event(QEMUBH *bh) +{ + if (replay_mode != REPLAY_MODE_NONE && events_enabled) { + uint64_t id = replay_get_current_step(); + replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id); + } else { + qemu_bh_schedule(bh); + } +} + +void replay_add_input_event(struct InputEvent *event) +{ + replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0); +} + +void replay_add_input_sync_event(void) +{ + replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0); +} + +void replay_block_event(QEMUBH *bh, uint64_t id) +{ + if (replay_mode != REPLAY_MODE_NONE && events_enabled) { + replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id); + } else { + qemu_bh_schedule(bh); + } +} + +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) { + case REPLAY_ASYNC_EVENT_BH: + replay_put_qword(event->id); + break; + case REPLAY_ASYNC_EVENT_INPUT: + replay_save_input_event(event->opaque); + break; + case REPLAY_ASYNC_EVENT_INPUT_SYNC: + break; + case REPLAY_ASYNC_EVENT_CHAR_READ: + replay_event_char_read_save(event->opaque); + break; + case REPLAY_ASYNC_EVENT_BLOCK: + replay_put_qword(event->id); + break; + default: + error_report("Unknown ID %" PRId64 " of replay event", event->id); + exit(1); + } + } +} + +/* 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) { + case REPLAY_ASYNC_EVENT_BH: + if (read_id == -1) { + read_id = replay_get_qword(); + } + break; + case REPLAY_ASYNC_EVENT_INPUT: + event = g_malloc0(sizeof(Event)); + event->event_kind = read_event_kind; + event->opaque = replay_read_input_event(); + return event; + case REPLAY_ASYNC_EVENT_INPUT_SYNC: + event = g_malloc0(sizeof(Event)); + event->event_kind = read_event_kind; + event->opaque = 0; + return event; + case REPLAY_ASYNC_EVENT_CHAR_READ: + event = g_malloc0(sizeof(Event)); + event->event_kind = read_event_kind; + event->opaque = replay_event_char_read_load(); + return event; + case REPLAY_ASYNC_EVENT_BLOCK: + if (read_id == -1) { + read_id = replay_get_qword(); + } + break; + 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; +} |