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