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 "qemu-common.h"
  15#include "sysemu/replay.h"
  16#include "replay-internal.h"
  17#include "qemu/timer.h"
  18#include "qemu/main-loop.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              0xe02006
  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        replay_mutex_lock();
  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        replay_mutex_unlock();
 104    }
 105}
 106
 107bool replay_exception(void)
 108{
 109    if (replay_mode == REPLAY_MODE_RECORD) {
 110        replay_save_instructions();
 111        replay_mutex_lock();
 112        replay_put_event(EVENT_EXCEPTION);
 113        replay_mutex_unlock();
 114        return true;
 115    } else if (replay_mode == REPLAY_MODE_PLAY) {
 116        bool res = replay_has_exception();
 117        if (res) {
 118            replay_mutex_lock();
 119            replay_finish_event();
 120            replay_mutex_unlock();
 121        }
 122        return res;
 123    }
 124
 125    return true;
 126}
 127
 128bool replay_has_exception(void)
 129{
 130    bool res = false;
 131    if (replay_mode == REPLAY_MODE_PLAY) {
 132        replay_account_executed_instructions();
 133        replay_mutex_lock();
 134        res = replay_next_event_is(EVENT_EXCEPTION);
 135        replay_mutex_unlock();
 136    }
 137
 138    return res;
 139}
 140
 141bool replay_interrupt(void)
 142{
 143    if (replay_mode == REPLAY_MODE_RECORD) {
 144        replay_save_instructions();
 145        replay_mutex_lock();
 146        replay_put_event(EVENT_INTERRUPT);
 147        replay_mutex_unlock();
 148        return true;
 149    } else if (replay_mode == REPLAY_MODE_PLAY) {
 150        bool res = replay_has_interrupt();
 151        if (res) {
 152            replay_mutex_lock();
 153            replay_finish_event();
 154            replay_mutex_unlock();
 155        }
 156        return res;
 157    }
 158
 159    return true;
 160}
 161
 162bool replay_has_interrupt(void)
 163{
 164    bool res = false;
 165    if (replay_mode == REPLAY_MODE_PLAY) {
 166        replay_account_executed_instructions();
 167        replay_mutex_lock();
 168        res = replay_next_event_is(EVENT_INTERRUPT);
 169        replay_mutex_unlock();
 170    }
 171    return res;
 172}
 173
 174void replay_shutdown_request(ShutdownCause cause)
 175{
 176    if (replay_mode == REPLAY_MODE_RECORD) {
 177        replay_mutex_lock();
 178        replay_put_event(EVENT_SHUTDOWN + cause);
 179        replay_mutex_unlock();
 180    }
 181}
 182
 183bool replay_checkpoint(ReplayCheckpoint checkpoint)
 184{
 185    bool res = false;
 186    assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
 187    replay_save_instructions();
 188
 189    if (!replay_file) {
 190        return true;
 191    }
 192
 193    replay_mutex_lock();
 194
 195    if (replay_mode == REPLAY_MODE_PLAY) {
 196        if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
 197            replay_finish_event();
 198        } else if (replay_state.data_kind != EVENT_ASYNC) {
 199            res = false;
 200            goto out;
 201        }
 202        replay_read_events(checkpoint);
 203        /* replay_read_events may leave some unread events.
 204           Return false if not all of the events associated with
 205           checkpoint were processed */
 206        res = replay_state.data_kind != EVENT_ASYNC;
 207    } else if (replay_mode == REPLAY_MODE_RECORD) {
 208        replay_put_event(EVENT_CHECKPOINT + checkpoint);
 209        replay_save_events(checkpoint);
 210        res = true;
 211    }
 212out:
 213    replay_mutex_unlock();
 214    return res;
 215}
 216
 217static void replay_enable(const char *fname, int mode)
 218{
 219    const char *fmode = NULL;
 220    assert(!replay_file);
 221
 222    switch (mode) {
 223    case REPLAY_MODE_RECORD:
 224        fmode = "wb";
 225        break;
 226    case REPLAY_MODE_PLAY:
 227        fmode = "rb";
 228        break;
 229    default:
 230        fprintf(stderr, "Replay: internal error: invalid replay mode\n");
 231        exit(1);
 232    }
 233
 234    atexit(replay_finish);
 235
 236    replay_mutex_init();
 237
 238    replay_file = fopen(fname, fmode);
 239    if (replay_file == NULL) {
 240        fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
 241        exit(1);
 242    }
 243
 244    replay_filename = g_strdup(fname);
 245
 246    replay_mode = mode;
 247    replay_state.data_kind = -1;
 248    replay_state.instructions_count = 0;
 249    replay_state.current_step = 0;
 250    replay_state.has_unread_data = 0;
 251
 252    /* skip file header for RECORD and check it for PLAY */
 253    if (replay_mode == REPLAY_MODE_RECORD) {
 254        fseek(replay_file, HEADER_SIZE, SEEK_SET);
 255    } else if (replay_mode == REPLAY_MODE_PLAY) {
 256        unsigned int version = replay_get_dword();
 257        if (version != REPLAY_VERSION) {
 258            fprintf(stderr, "Replay: invalid input log file version\n");
 259            exit(1);
 260        }
 261        /* go to the beginning */
 262        fseek(replay_file, HEADER_SIZE, SEEK_SET);
 263        replay_fetch_data_kind();
 264    }
 265
 266    replay_init_events();
 267}
 268
 269void replay_configure(QemuOpts *opts)
 270{
 271    const char *fname;
 272    const char *rr;
 273    ReplayMode mode = REPLAY_MODE_NONE;
 274    Location loc;
 275
 276    if (!opts) {
 277        return;
 278    }
 279
 280    loc_push_none(&loc);
 281    qemu_opts_loc_restore(opts);
 282
 283    rr = qemu_opt_get(opts, "rr");
 284    if (!rr) {
 285        /* Just enabling icount */
 286        goto out;
 287    } else if (!strcmp(rr, "record")) {
 288        mode = REPLAY_MODE_RECORD;
 289    } else if (!strcmp(rr, "replay")) {
 290        mode = REPLAY_MODE_PLAY;
 291    } else {
 292        error_report("Invalid icount rr option: %s", rr);
 293        exit(1);
 294    }
 295
 296    fname = qemu_opt_get(opts, "rrfile");
 297    if (!fname) {
 298        error_report("File name not specified for replay");
 299        exit(1);
 300    }
 301
 302    replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
 303    replay_vmstate_register();
 304    replay_enable(fname, mode);
 305
 306out:
 307    loc_pop(&loc);
 308}
 309
 310void replay_start(void)
 311{
 312    if (replay_mode == REPLAY_MODE_NONE) {
 313        return;
 314    }
 315
 316    if (replay_blockers) {
 317        error_reportf_err(replay_blockers->data, "Record/replay: ");
 318        exit(1);
 319    }
 320    if (!use_icount) {
 321        error_report("Please enable icount to use record/replay");
 322        exit(1);
 323    }
 324
 325    /* Timer for snapshotting will be set up here. */
 326
 327    replay_enable_events();
 328}
 329
 330void replay_finish(void)
 331{
 332    if (replay_mode == REPLAY_MODE_NONE) {
 333        return;
 334    }
 335
 336    replay_save_instructions();
 337
 338    /* finalize the file */
 339    if (replay_file) {
 340        if (replay_mode == REPLAY_MODE_RECORD) {
 341            /* write end event */
 342            replay_put_event(EVENT_END);
 343
 344            /* write header */
 345            fseek(replay_file, 0, SEEK_SET);
 346            replay_put_dword(REPLAY_VERSION);
 347        }
 348
 349        fclose(replay_file);
 350        replay_file = NULL;
 351    }
 352    if (replay_filename) {
 353        g_free(replay_filename);
 354        replay_filename = NULL;
 355    }
 356
 357    g_free(replay_snapshot);
 358    replay_snapshot = NULL;
 359
 360    replay_finish_events();
 361    replay_mutex_destroy();
 362}
 363
 364void replay_add_blocker(Error *reason)
 365{
 366    replay_blockers = g_slist_prepend(replay_blockers, reason);
 367}
 368