qemu/replay/replay.c
<<
>>
Prefs
   1/*
   2 * replay.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 "qapi/error.h"
  14#include "sysemu/replay.h"
  15#include "replay-internal.h"
  16#include "qemu/timer.h"
  17#include "qemu/main-loop.h"
  18#include "qemu/option.h"
  19#include "sysemu/cpus.h"
  20#include "sysemu/sysemu.h"
  21#include "qemu/error-report.h"
  22
  23/* Current version of the replay mechanism.
  24   Increase it when file format changes. */
  25#define REPLAY_VERSION              0xe02007
  26/* Size of replay log header */
  27#define HEADER_SIZE                 (sizeof(uint32_t) + sizeof(uint64_t))
  28
  29ReplayMode replay_mode = REPLAY_MODE_NONE;
  30char *replay_snapshot;
  31
  32/* Name of replay file  */
  33static char *replay_filename;
  34ReplayState replay_state;
  35static GSList *replay_blockers;
  36
  37bool replay_next_event_is(int event)
  38{
  39    bool res = false;
  40
  41    /* nothing to skip - not all instructions used */
  42    if (replay_state.instructions_count != 0) {
  43        assert(replay_state.data_kind == EVENT_INSTRUCTION);
  44        return event == EVENT_INSTRUCTION;
  45    }
  46
  47    while (true) {
  48        if (event == replay_state.data_kind) {
  49            res = true;
  50        }
  51        switch (replay_state.data_kind) {
  52        case EVENT_SHUTDOWN ... EVENT_SHUTDOWN_LAST:
  53            replay_finish_event();
  54            qemu_system_shutdown_request(replay_state.data_kind -
  55                                         EVENT_SHUTDOWN);
  56            break;
  57        default:
  58            /* clock, time_t, checkpoint and other events */
  59            return res;
  60        }
  61    }
  62    return res;
  63}
  64
  65uint64_t replay_get_current_step(void)
  66{
  67    return cpu_get_icount_raw();
  68}
  69
  70int replay_get_instructions(void)
  71{
  72    int res = 0;
  73    replay_mutex_lock();
  74    if (replay_next_event_is(EVENT_INSTRUCTION)) {
  75        res = replay_state.instructions_count;
  76    }
  77    replay_mutex_unlock();
  78    return res;
  79}
  80
  81void replay_account_executed_instructions(void)
  82{
  83    if (replay_mode == REPLAY_MODE_PLAY) {
  84        g_assert(replay_mutex_locked());
  85        if (replay_state.instructions_count > 0) {
  86            int count = (int)(replay_get_current_step()
  87                              - replay_state.current_step);
  88
  89            /* Time can only go forward */
  90            assert(count >= 0);
  91
  92            replay_state.instructions_count -= count;
  93            replay_state.current_step += count;
  94            if (replay_state.instructions_count == 0) {
  95                assert(replay_state.data_kind == EVENT_INSTRUCTION);
  96                replay_finish_event();
  97                /* Wake up iothread. This is required because
  98                   timers will not expire until clock counters
  99                   will be read from the log. */
 100                qemu_notify_event();
 101            }
 102        }
 103    }
 104}
 105
 106bool replay_exception(void)
 107{
 108
 109    if (replay_mode == REPLAY_MODE_RECORD) {
 110        g_assert(replay_mutex_locked());
 111        replay_save_instructions();
 112        replay_put_event(EVENT_EXCEPTION);
 113        return true;
 114    } else if (replay_mode == REPLAY_MODE_PLAY) {
 115        g_assert(replay_mutex_locked());
 116        bool res = replay_has_exception();
 117        if (res) {
 118            replay_finish_event();
 119        }
 120        return res;
 121    }
 122
 123    return true;
 124}
 125
 126bool replay_has_exception(void)
 127{
 128    bool res = false;
 129    if (replay_mode == REPLAY_MODE_PLAY) {
 130        g_assert(replay_mutex_locked());
 131        replay_account_executed_instructions();
 132        res = replay_next_event_is(EVENT_EXCEPTION);
 133    }
 134
 135    return res;
 136}
 137
 138bool replay_interrupt(void)
 139{
 140    if (replay_mode == REPLAY_MODE_RECORD) {
 141        g_assert(replay_mutex_locked());
 142        replay_save_instructions();
 143        replay_put_event(EVENT_INTERRUPT);
 144        return true;
 145    } else if (replay_mode == REPLAY_MODE_PLAY) {
 146        g_assert(replay_mutex_locked());
 147        bool res = replay_has_interrupt();
 148        if (res) {
 149            replay_finish_event();
 150        }
 151        return res;
 152    }
 153
 154    return true;
 155}
 156
 157bool replay_has_interrupt(void)
 158{
 159    bool res = false;
 160    if (replay_mode == REPLAY_MODE_PLAY) {
 161        g_assert(replay_mutex_locked());
 162        replay_account_executed_instructions();
 163        res = replay_next_event_is(EVENT_INTERRUPT);
 164    }
 165    return res;
 166}
 167
 168void replay_shutdown_request(ShutdownCause cause)
 169{
 170    if (replay_mode == REPLAY_MODE_RECORD) {
 171        g_assert(replay_mutex_locked());
 172        replay_put_event(EVENT_SHUTDOWN + cause);
 173    }
 174}
 175
 176bool replay_checkpoint(ReplayCheckpoint checkpoint)
 177{
 178    bool res = false;
 179    static bool in_checkpoint;
 180    assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
 181
 182    if (!replay_file) {
 183        return true;
 184    }
 185
 186    if (in_checkpoint) {
 187        /* If we are already in checkpoint, then there is no need
 188           for additional synchronization.
 189           Recursion occurs when HW event modifies timers.
 190           Timer modification may invoke the checkpoint and
 191           proceed to recursion. */
 192        return true;
 193    }
 194    in_checkpoint = true;
 195
 196    replay_save_instructions();
 197
 198    if (replay_mode == REPLAY_MODE_PLAY) {
 199        g_assert(replay_mutex_locked());
 200        if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
 201            replay_finish_event();
 202        } else if (replay_state.data_kind != EVENT_ASYNC) {
 203            res = false;
 204            goto out;
 205        }
 206        replay_read_events(checkpoint);
 207        /* replay_read_events may leave some unread events.
 208           Return false if not all of the events associated with
 209           checkpoint were processed */
 210        res = replay_state.data_kind != EVENT_ASYNC;
 211    } else if (replay_mode == REPLAY_MODE_RECORD) {
 212        g_assert(replay_mutex_locked());
 213        replay_put_event(EVENT_CHECKPOINT + checkpoint);
 214        /* This checkpoint belongs to several threads.
 215           Processing events from different threads is
 216           non-deterministic */
 217        if (checkpoint != CHECKPOINT_CLOCK_WARP_START
 218            /* FIXME: this is temporary fix, other checkpoints
 219                      may also be invoked from the different threads someday.
 220                      Asynchronous event processing should be refactored
 221                      to create additional replay event kind which is
 222                      nailed to the one of the threads and which processes
 223                      the event queue. */
 224            && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
 225            replay_save_events(checkpoint);
 226        }
 227        res = true;
 228    }
 229out:
 230    in_checkpoint = false;
 231    return res;
 232}
 233
 234bool replay_has_checkpoint(void)
 235{
 236    bool res = false;
 237    if (replay_mode == REPLAY_MODE_PLAY) {
 238        g_assert(replay_mutex_locked());
 239        replay_account_executed_instructions();
 240        res = EVENT_CHECKPOINT <= replay_state.data_kind
 241              && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
 242    }
 243    return res;
 244}
 245
 246static void replay_enable(const char *fname, int mode)
 247{
 248    const char *fmode = NULL;
 249    assert(!replay_file);
 250
 251    switch (mode) {
 252    case REPLAY_MODE_RECORD:
 253        fmode = "wb";
 254        break;
 255    case REPLAY_MODE_PLAY:
 256        fmode = "rb";
 257        break;
 258    default:
 259        fprintf(stderr, "Replay: internal error: invalid replay mode\n");
 260        exit(1);
 261    }
 262
 263    atexit(replay_finish);
 264
 265    replay_file = fopen(fname, fmode);
 266    if (replay_file == NULL) {
 267        fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
 268        exit(1);
 269    }
 270
 271    replay_filename = g_strdup(fname);
 272    replay_mode = mode;
 273    replay_mutex_init();
 274
 275    replay_state.data_kind = -1;
 276    replay_state.instructions_count = 0;
 277    replay_state.current_step = 0;
 278    replay_state.has_unread_data = 0;
 279
 280    /* skip file header for RECORD and check it for PLAY */
 281    if (replay_mode == REPLAY_MODE_RECORD) {
 282        fseek(replay_file, HEADER_SIZE, SEEK_SET);
 283    } else if (replay_mode == REPLAY_MODE_PLAY) {
 284        unsigned int version = replay_get_dword();
 285        if (version != REPLAY_VERSION) {
 286            fprintf(stderr, "Replay: invalid input log file version\n");
 287            exit(1);
 288        }
 289        /* go to the beginning */
 290        fseek(replay_file, HEADER_SIZE, SEEK_SET);
 291        replay_fetch_data_kind();
 292    }
 293
 294    replay_init_events();
 295}
 296
 297void replay_configure(QemuOpts *opts)
 298{
 299    const char *fname;
 300    const char *rr;
 301    ReplayMode mode = REPLAY_MODE_NONE;
 302    Location loc;
 303
 304    if (!opts) {
 305        return;
 306    }
 307
 308    loc_push_none(&loc);
 309    qemu_opts_loc_restore(opts);
 310
 311    rr = qemu_opt_get(opts, "rr");
 312    if (!rr) {
 313        /* Just enabling icount */
 314        goto out;
 315    } else if (!strcmp(rr, "record")) {
 316        mode = REPLAY_MODE_RECORD;
 317    } else if (!strcmp(rr, "replay")) {
 318        mode = REPLAY_MODE_PLAY;
 319    } else {
 320        error_report("Invalid icount rr option: %s", rr);
 321        exit(1);
 322    }
 323
 324    fname = qemu_opt_get(opts, "rrfile");
 325    if (!fname) {
 326        error_report("File name not specified for replay");
 327        exit(1);
 328    }
 329
 330    replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
 331    replay_vmstate_register();
 332    replay_enable(fname, mode);
 333
 334out:
 335    loc_pop(&loc);
 336}
 337
 338void replay_start(void)
 339{
 340    if (replay_mode == REPLAY_MODE_NONE) {
 341        return;
 342    }
 343
 344    if (replay_blockers) {
 345        error_reportf_err(replay_blockers->data, "Record/replay: ");
 346        exit(1);
 347    }
 348    if (!use_icount) {
 349        error_report("Please enable icount to use record/replay");
 350        exit(1);
 351    }
 352
 353    /* Timer for snapshotting will be set up here. */
 354
 355    replay_enable_events();
 356}
 357
 358void replay_finish(void)
 359{
 360    if (replay_mode == REPLAY_MODE_NONE) {
 361        return;
 362    }
 363
 364    replay_save_instructions();
 365
 366    /* finalize the file */
 367    if (replay_file) {
 368        if (replay_mode == REPLAY_MODE_RECORD) {
 369            /* write end event */
 370            replay_put_event(EVENT_END);
 371
 372            /* write header */
 373            fseek(replay_file, 0, SEEK_SET);
 374            replay_put_dword(REPLAY_VERSION);
 375        }
 376
 377        fclose(replay_file);
 378        replay_file = NULL;
 379    }
 380    if (replay_filename) {
 381        g_free(replay_filename);
 382        replay_filename = NULL;
 383    }
 384
 385    g_free(replay_snapshot);
 386    replay_snapshot = NULL;
 387
 388    replay_finish_events();
 389}
 390
 391void replay_add_blocker(Error *reason)
 392{
 393    replay_blockers = g_slist_prepend(replay_blockers, reason);
 394}
 395