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