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/error-report.h"
  14#include "sysemu/replay.h"
  15#include "replay-internal.h"
  16#include "block/aio.h"
  17#include "ui/input.h"
  18#include "hw/core/cpu.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 bool events_enabled;
  31
  32/* Functions */
  33
  34static void replay_run_event(Event *event)
  35{
  36    switch (event->event_kind) {
  37    case REPLAY_ASYNC_EVENT_BH:
  38        aio_bh_call(event->opaque);
  39        break;
  40    case REPLAY_ASYNC_EVENT_BH_ONESHOT:
  41        ((QEMUBHFunc *)event->opaque)(event->opaque2);
  42        break;
  43    case REPLAY_ASYNC_EVENT_INPUT:
  44        qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque);
  45        qapi_free_InputEvent((InputEvent *)event->opaque);
  46        break;
  47    case REPLAY_ASYNC_EVENT_INPUT_SYNC:
  48        qemu_input_event_sync_impl();
  49        break;
  50    case REPLAY_ASYNC_EVENT_CHAR_READ:
  51        replay_event_char_read_run(event->opaque);
  52        break;
  53    case REPLAY_ASYNC_EVENT_BLOCK:
  54        aio_bh_call(event->opaque);
  55        break;
  56    case REPLAY_ASYNC_EVENT_NET:
  57        replay_event_net_run(event->opaque);
  58        break;
  59    default:
  60        error_report("Replay: invalid async event ID (%d) in the queue",
  61                    event->event_kind);
  62        exit(1);
  63        break;
  64    }
  65}
  66
  67void replay_enable_events(void)
  68{
  69    if (replay_mode != REPLAY_MODE_NONE) {
  70        events_enabled = true;
  71    }
  72}
  73
  74bool replay_has_events(void)
  75{
  76    return !QTAILQ_EMPTY(&events_list);
  77}
  78
  79void replay_flush_events(void)
  80{
  81    if (replay_mode == REPLAY_MODE_NONE) {
  82        return;
  83    }
  84
  85    g_assert(replay_mutex_locked());
  86
  87    while (!QTAILQ_EMPTY(&events_list)) {
  88        Event *event = QTAILQ_FIRST(&events_list);
  89        replay_run_event(event);
  90        QTAILQ_REMOVE(&events_list, event, events);
  91        g_free(event);
  92    }
  93}
  94
  95void replay_disable_events(void)
  96{
  97    if (replay_mode != REPLAY_MODE_NONE) {
  98        events_enabled = false;
  99        /* Flush events queue before waiting of completion */
 100        replay_flush_events();
 101    }
 102}
 103
 104/*! Adds specified async event to the queue */
 105void replay_add_event(ReplayAsyncEventKind event_kind,
 106                      void *opaque,
 107                      void *opaque2, uint64_t id)
 108{
 109    assert(event_kind < REPLAY_ASYNC_COUNT);
 110
 111    if (!replay_file || replay_mode == REPLAY_MODE_NONE
 112        || !events_enabled) {
 113        Event e;
 114        e.event_kind = event_kind;
 115        e.opaque = opaque;
 116        e.opaque2 = opaque2;
 117        e.id = id;
 118        replay_run_event(&e);
 119        return;
 120    }
 121
 122    Event *event = g_malloc0(sizeof(Event));
 123    event->event_kind = event_kind;
 124    event->opaque = opaque;
 125    event->opaque2 = opaque2;
 126    event->id = id;
 127
 128    g_assert(replay_mutex_locked());
 129    QTAILQ_INSERT_TAIL(&events_list, event, events);
 130    qemu_cpu_kick(first_cpu);
 131}
 132
 133void replay_bh_schedule_event(QEMUBH *bh)
 134{
 135    if (events_enabled) {
 136        uint64_t id = replay_get_current_icount();
 137        replay_add_event(REPLAY_ASYNC_EVENT_BH, bh, NULL, id);
 138    } else {
 139        qemu_bh_schedule(bh);
 140    }
 141}
 142
 143void replay_bh_schedule_oneshot_event(AioContext *ctx,
 144    QEMUBHFunc *cb, void *opaque)
 145{
 146    if (events_enabled) {
 147        uint64_t id = replay_get_current_icount();
 148        replay_add_event(REPLAY_ASYNC_EVENT_BH_ONESHOT, cb, opaque, id);
 149    } else {
 150        aio_bh_schedule_oneshot(ctx, cb, opaque);
 151    }
 152}
 153
 154void replay_add_input_event(struct InputEvent *event)
 155{
 156    replay_add_event(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0);
 157}
 158
 159void replay_add_input_sync_event(void)
 160{
 161    replay_add_event(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0);
 162}
 163
 164void replay_block_event(QEMUBH *bh, uint64_t id)
 165{
 166    if (events_enabled) {
 167        replay_add_event(REPLAY_ASYNC_EVENT_BLOCK, bh, NULL, id);
 168    } else {
 169        qemu_bh_schedule(bh);
 170    }
 171}
 172
 173static void replay_save_event(Event *event, int checkpoint)
 174{
 175    if (replay_mode != REPLAY_MODE_PLAY) {
 176        /* put the event into the file */
 177        replay_put_event(EVENT_ASYNC);
 178        replay_put_byte(checkpoint);
 179        replay_put_byte(event->event_kind);
 180
 181        /* save event-specific data */
 182        switch (event->event_kind) {
 183        case REPLAY_ASYNC_EVENT_BH:
 184        case REPLAY_ASYNC_EVENT_BH_ONESHOT:
 185            replay_put_qword(event->id);
 186            break;
 187        case REPLAY_ASYNC_EVENT_INPUT:
 188            replay_save_input_event(event->opaque);
 189            break;
 190        case REPLAY_ASYNC_EVENT_INPUT_SYNC:
 191            break;
 192        case REPLAY_ASYNC_EVENT_CHAR_READ:
 193            replay_event_char_read_save(event->opaque);
 194            break;
 195        case REPLAY_ASYNC_EVENT_BLOCK:
 196            replay_put_qword(event->id);
 197            break;
 198        case REPLAY_ASYNC_EVENT_NET:
 199            replay_event_net_save(event->opaque);
 200            break;
 201        default:
 202            error_report("Unknown ID %" PRId64 " of replay event", event->id);
 203            exit(1);
 204        }
 205    }
 206}
 207
 208/* Called with replay mutex locked */
 209void replay_save_events(int checkpoint)
 210{
 211    g_assert(replay_mutex_locked());
 212    g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START);
 213    g_assert(checkpoint != CHECKPOINT_CLOCK_VIRTUAL);
 214    while (!QTAILQ_EMPTY(&events_list)) {
 215        Event *event = QTAILQ_FIRST(&events_list);
 216        replay_save_event(event, checkpoint);
 217        replay_run_event(event);
 218        QTAILQ_REMOVE(&events_list, event, events);
 219        g_free(event);
 220    }
 221}
 222
 223static Event *replay_read_event(int checkpoint)
 224{
 225    Event *event;
 226    if (replay_state.read_event_kind == -1) {
 227        replay_state.read_event_checkpoint = replay_get_byte();
 228        replay_state.read_event_kind = replay_get_byte();
 229        replay_state.read_event_id = -1;
 230        replay_check_error();
 231    }
 232
 233    if (checkpoint != replay_state.read_event_checkpoint) {
 234        return NULL;
 235    }
 236
 237    /* Events that has not to be in the queue */
 238    switch (replay_state.read_event_kind) {
 239    case REPLAY_ASYNC_EVENT_BH:
 240    case REPLAY_ASYNC_EVENT_BH_ONESHOT:
 241        if (replay_state.read_event_id == -1) {
 242            replay_state.read_event_id = replay_get_qword();
 243        }
 244        break;
 245    case REPLAY_ASYNC_EVENT_INPUT:
 246        event = g_malloc0(sizeof(Event));
 247        event->event_kind = replay_state.read_event_kind;
 248        event->opaque = replay_read_input_event();
 249        return event;
 250    case REPLAY_ASYNC_EVENT_INPUT_SYNC:
 251        event = g_malloc0(sizeof(Event));
 252        event->event_kind = replay_state.read_event_kind;
 253        event->opaque = 0;
 254        return event;
 255    case REPLAY_ASYNC_EVENT_CHAR_READ:
 256        event = g_malloc0(sizeof(Event));
 257        event->event_kind = replay_state.read_event_kind;
 258        event->opaque = replay_event_char_read_load();
 259        return event;
 260    case REPLAY_ASYNC_EVENT_BLOCK:
 261        if (replay_state.read_event_id == -1) {
 262            replay_state.read_event_id = replay_get_qword();
 263        }
 264        break;
 265    case REPLAY_ASYNC_EVENT_NET:
 266        event = g_malloc0(sizeof(Event));
 267        event->event_kind = replay_state.read_event_kind;
 268        event->opaque = replay_event_net_load();
 269        return event;
 270    default:
 271        error_report("Unknown ID %d of replay event",
 272            replay_state.read_event_kind);
 273        exit(1);
 274        break;
 275    }
 276
 277    QTAILQ_FOREACH(event, &events_list, events) {
 278        if (event->event_kind == replay_state.read_event_kind
 279            && (replay_state.read_event_id == -1
 280                || replay_state.read_event_id == event->id)) {
 281            break;
 282        }
 283    }
 284
 285    if (event) {
 286        QTAILQ_REMOVE(&events_list, event, events);
 287    } else {
 288        return NULL;
 289    }
 290
 291    /* Read event-specific data */
 292
 293    return event;
 294}
 295
 296/* Called with replay mutex locked */
 297void replay_read_events(int checkpoint)
 298{
 299    g_assert(replay_mutex_locked());
 300    while (replay_state.data_kind == EVENT_ASYNC) {
 301        Event *event = replay_read_event(checkpoint);
 302        if (!event) {
 303            break;
 304        }
 305        replay_finish_event();
 306        replay_state.read_event_kind = -1;
 307        replay_run_event(event);
 308
 309        g_free(event);
 310    }
 311}
 312
 313void replay_init_events(void)
 314{
 315    replay_state.read_event_kind = -1;
 316}
 317
 318void replay_finish_events(void)
 319{
 320    events_enabled = false;
 321    replay_flush_events();
 322}
 323
 324bool replay_events_enabled(void)
 325{
 326    return events_enabled;
 327}
 328
 329uint64_t blkreplay_next_id(void)
 330{
 331    if (replay_events_enabled()) {
 332        return replay_state.block_request_id++;
 333    }
 334    return 0;
 335}
 336