qemu/replay/replay-events.c
<<
>>
Prefs
   1/*
   2 * replay-events.c
   3 *
   4 * Copyright (c) 2010-2015 Institute for System Programming
   5 *                         of the Russian Academy of Sciences.
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2 or later.
   8 * See the COPYING file in the top-level directory.
   9 *
  10 */
  11
  12#include "qemu/osdep.h"
  13#include "qemu-common.h"
  14#include "qemu/error-report.h"
  15#include "sysemu/replay.h"
  16#include "replay-internal.h"
  17#include "block/aio.h"
  18#include "ui/input.h"
  19
  20typedef struct Event {
  21    ReplayAsyncEventKind event_kind;
  22    void *opaque;
  23    void *opaque2;
  24    uint64_t id;
  25
  26    QTAILQ_ENTRY(Event) events;
  27} Event;
  28
  29static QTAILQ_HEAD(, Event) events_list = QTAILQ_HEAD_INITIALIZER(events_list);
  30static unsigned int read_event_kind = -1;
  31static uint64_t read_id = -1;
  32static int read_checkpoint = -1;
  33
  34static bool events_enabled;
  35
  36/* Functions */
  37
  38static void replay_run_event(Event *event)
  39{
  40    switch (event->event_kind) {
  41    case REPLAY_ASYNC_EVENT_BH:
  42        aio_bh_call(event->opaque);
  43        break;
  44    case REPLAY_ASYNC_EVENT_INPUT:
  45        qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque);
  46        qapi_free_InputEvent((InputEvent *)event->opaque);
  47        break;
  48    case REPLAY_ASYNC_EVENT_INPUT_SYNC:
  49        qemu_input_event_sync_impl();
  50        break;
  51    case REPLAY_ASYNC_EVENT_CHAR_READ:
  52        replay_event_char_read_run(event->opaque);
  53        break;
  54    case REPLAY_ASYNC_EVENT_BLOCK:
  55        aio_bh_call(event->opaque);
  56        break;
  57    default:
  58        error_report("Replay: invalid async event ID (%d) in the queue",
  59                    event->event_kind);
  60        exit(1);
  61        break;
  62    }
  63}
  64
  65void replay_enable_events(void)
  66{
  67    events_enabled = true;
  68}
  69
  70bool replay_has_events(void)
  71{
  72    return !QTAILQ_EMPTY(&events_list);
  73}
  74
  75void replay_flush_events(void)
  76{
  77    replay_mutex_lock();
  78    while (!QTAILQ_EMPTY(&events_list)) {
  79        Event *event = QTAILQ_FIRST(&events_list);
  80        replay_mutex_unlock();
  81        replay_run_event(event);
  82        replay_mutex_lock();
  83        QTAILQ_REMOVE(&events_list, event, events);
  84        g_free(event);
  85    }
  86    replay_mutex_unlock();
  87}
  88
  89void replay_disable_events(void)
  90{
  91    if (replay_mode != REPLAY_MODE_NONE) {
  92        events_enabled = false;
  93        /* Flush events queue before waiting of completion */
  94        replay_flush_events();
  95    }
  96}
  97
  98void replay_clear_events(void)
  99{
 100    replay_mutex_lock();
 101    while (!QTAILQ_EMPTY(&events_list)) {
 102        Event *event = QTAILQ_FIRST(&events_list);
 103        QTAILQ_REMOVE(&events_list, event, events);
 104
 105        g_free(event);
 106    }
 107    replay_mutex_unlock();
 108}
 109
 110/*! Adds specified async event to the queue */
 111void replay_add_event(ReplayAsyncEventKind event_kind,
 112                      void *opaque,
 113                      void *opaque2, uint64_t id)
 114{
 115    assert(event_kind < REPLAY_ASYNC_COUNT);
 116
 117    if (!replay_file || replay_mode == REPLAY_MODE_NONE
 118        || !events_enabled) {
 119        Event e;
 120        e.event_kind = event_kind;
 121        e.opaque = opaque;
 122        e.opaque2 = opaque2;
 123        e.id = id;
 124        replay_run_event(&e);
 125        return;
 126    }
 127
 128    Event *event = g_malloc0(sizeof(Event));
 129    event->event_kind = event_kind;
 130    event->opaque = opaque;
 131    event->opaque2 = opaque2;
 132    event->id = id;
 133
 134    replay_mutex_lock();
 135    QTAILQ_INSERT_TAIL(&events_list, event, events);
 136    replay_mutex_unlock();
 137}
 138
 139void replay_bh_schedule_event(QEMUBH *bh)
 140{
 141    if (replay_mode != REPLAY_MODE_NONE && events_enabled) {
 142        uint64_t id = replay_get_current_step();
 143        replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
 144    } else {
 145        qemu_bh_schedule(bh);
 146    }
 147}
 148
 149void replay_add_input_event(struct InputEvent *event)
 150{
 151    replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
 152}
 153
 154void replay_add_input_sync_event(void)
 155{
 156    replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0);
 157}
 158
 159void replay_block_event(QEMUBH *bh, uint64_t id)
 160{
 161    if (replay_mode != REPLAY_MODE_NONE && events_enabled) {
 162        replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id);
 163    } else {
 164        qemu_bh_schedule(bh);
 165    }
 166}
 167
 168static void replay_save_event(Event *event, int checkpoint)
 169{
 170    if (replay_mode != REPLAY_MODE_PLAY) {
 171        /* put the event into the file */
 172        replay_put_event(EVENT_ASYNC);
 173        replay_put_byte(checkpoint);
 174        replay_put_byte(event->event_kind);
 175
 176        /* save event-specific data */
 177        switch (event->event_kind) {
 178        case REPLAY_ASYNC_EVENT_BH:
 179            replay_put_qword(event->id);
 180            break;
 181        case REPLAY_ASYNC_EVENT_INPUT:
 182            replay_save_input_event(event->opaque);
 183            break;
 184        case REPLAY_ASYNC_EVENT_INPUT_SYNC:
 185            break;
 186        case REPLAY_ASYNC_EVENT_CHAR_READ:
 187            replay_event_char_read_save(event->opaque);
 188            break;
 189        case REPLAY_ASYNC_EVENT_BLOCK:
 190            replay_put_qword(event->id);
 191            break;
 192        default:
 193            error_report("Unknown ID %" PRId64 " of replay event", event->id);
 194            exit(1);
 195        }
 196    }
 197}
 198
 199/* Called with replay mutex locked */
 200void replay_save_events(int checkpoint)
 201{
 202    while (!QTAILQ_EMPTY(&events_list)) {
 203        Event *event = QTAILQ_FIRST(&events_list);
 204        replay_save_event(event, checkpoint);
 205
 206        replay_mutex_unlock();
 207        replay_run_event(event);
 208        replay_mutex_lock();
 209        QTAILQ_REMOVE(&events_list, event, events);
 210        g_free(event);
 211    }
 212}
 213
 214static Event *replay_read_event(int checkpoint)
 215{
 216    Event *event;
 217    if (read_event_kind == -1) {
 218        read_checkpoint = replay_get_byte();
 219        read_event_kind = replay_get_byte();
 220        read_id = -1;
 221        replay_check_error();
 222    }
 223
 224    if (checkpoint != read_checkpoint) {
 225        return NULL;
 226    }
 227
 228    /* Events that has not to be in the queue */
 229    switch (read_event_kind) {
 230    case REPLAY_ASYNC_EVENT_BH:
 231        if (read_id == -1) {
 232            read_id = replay_get_qword();
 233        }
 234        break;
 235    case REPLAY_ASYNC_EVENT_INPUT:
 236        event = g_malloc0(sizeof(Event));
 237        event->event_kind = read_event_kind;
 238        event->opaque = replay_read_input_event();
 239        return event;
 240    case REPLAY_ASYNC_EVENT_INPUT_SYNC:
 241        event = g_malloc0(sizeof(Event));
 242        event->event_kind = read_event_kind;
 243        event->opaque = 0;
 244        return event;
 245    case REPLAY_ASYNC_EVENT_CHAR_READ:
 246        event = g_malloc0(sizeof(Event));
 247        event->event_kind = read_event_kind;
 248        event->opaque = replay_event_char_read_load();
 249        return event;
 250    case REPLAY_ASYNC_EVENT_BLOCK:
 251        if (read_id == -1) {
 252            read_id = replay_get_qword();
 253        }
 254        break;
 255    default:
 256        error_report("Unknown ID %d of replay event", read_event_kind);
 257        exit(1);
 258        break;
 259    }
 260
 261    QTAILQ_FOREACH(event, &events_list, events) {
 262        if (event->event_kind == read_event_kind
 263            && (read_id == -1 || read_id == event->id)) {
 264            break;
 265        }
 266    }
 267
 268    if (event) {
 269        QTAILQ_REMOVE(&events_list, event, events);
 270    } else {
 271        return NULL;
 272    }
 273
 274    /* Read event-specific data */
 275
 276    return event;
 277}
 278
 279/* Called with replay mutex locked */
 280void replay_read_events(int checkpoint)
 281{
 282    while (replay_data_kind == EVENT_ASYNC) {
 283        Event *event = replay_read_event(checkpoint);
 284        if (!event) {
 285            break;
 286        }
 287        replay_mutex_unlock();
 288        replay_run_event(event);
 289        replay_mutex_lock();
 290
 291        g_free(event);
 292        replay_finish_event();
 293        read_event_kind = -1;
 294    }
 295}
 296
 297void replay_init_events(void)
 298{
 299    read_event_kind = -1;
 300}
 301
 302void replay_finish_events(void)
 303{
 304    events_enabled = false;
 305    replay_clear_events();
 306}
 307
 308bool replay_events_enabled(void)
 309{
 310    return events_enabled;
 311}
 312