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            int count = (int)(replay_get_current_icount()
  98                              - replay_state.current_icount);
  99
 100            /* Time can only go forward */
 101            assert(count >= 0);
 102
 103            replay_state.instruction_count -= count;
 104            replay_state.current_icount += count;
 105            if (replay_state.instruction_count == 0) {
 106                assert(replay_state.data_kind == EVENT_INSTRUCTION);
 107                replay_finish_event();
 108                /* Wake up iothread. This is required because
 109                   timers will not expire until clock counters
 110                   will be read from the log. */
 111                qemu_notify_event();
 112            }
 113            /* Execution reached the break step */
 114            if (replay_break_icount == replay_state.current_icount) {
 115                /* Cannot make callback directly from the vCPU thread */
 116                timer_mod_ns(replay_break_timer,
 117                    qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
 118            }
 119        }
 120    }
 121}
 122
 123bool replay_exception(void)
 124{
 125
 126    if (replay_mode == REPLAY_MODE_RECORD) {
 127        g_assert(replay_mutex_locked());
 128        replay_save_instructions();
 129        replay_put_event(EVENT_EXCEPTION);
 130        return true;
 131    } else if (replay_mode == REPLAY_MODE_PLAY) {
 132        g_assert(replay_mutex_locked());
 133        bool res = replay_has_exception();
 134        if (res) {
 135            replay_finish_event();
 136        }
 137        return res;
 138    }
 139
 140    return true;
 141}
 142
 143bool replay_has_exception(void)
 144{
 145    bool res = false;
 146    if (replay_mode == REPLAY_MODE_PLAY) {
 147        g_assert(replay_mutex_locked());
 148        replay_account_executed_instructions();
 149        res = replay_next_event_is(EVENT_EXCEPTION);
 150    }
 151
 152    return res;
 153}
 154
 155bool replay_interrupt(void)
 156{
 157    if (replay_mode == REPLAY_MODE_RECORD) {
 158        g_assert(replay_mutex_locked());
 159        replay_save_instructions();
 160        replay_put_event(EVENT_INTERRUPT);
 161        return true;
 162    } else if (replay_mode == REPLAY_MODE_PLAY) {
 163        g_assert(replay_mutex_locked());
 164        bool res = replay_has_interrupt();
 165        if (res) {
 166            replay_finish_event();
 167        }
 168        return res;
 169    }
 170
 171    return true;
 172}
 173
 174bool replay_has_interrupt(void)
 175{
 176    bool res = false;
 177    if (replay_mode == REPLAY_MODE_PLAY) {
 178        g_assert(replay_mutex_locked());
 179        replay_account_executed_instructions();
 180        res = replay_next_event_is(EVENT_INTERRUPT);
 181    }
 182    return res;
 183}
 184
 185void replay_shutdown_request(ShutdownCause cause)
 186{
 187    if (replay_mode == REPLAY_MODE_RECORD) {
 188        g_assert(replay_mutex_locked());
 189        replay_put_event(EVENT_SHUTDOWN + cause);
 190    }
 191}
 192
 193bool replay_checkpoint(ReplayCheckpoint checkpoint)
 194{
 195    bool res = false;
 196    static bool in_checkpoint;
 197    assert(EVENT_CHECKPOINT + checkpoint <= EVENT_CHECKPOINT_LAST);
 198
 199    if (!replay_file) {
 200        return true;
 201    }
 202
 203    if (in_checkpoint) {
 204        /* If we are already in checkpoint, then there is no need
 205           for additional synchronization.
 206           Recursion occurs when HW event modifies timers.
 207           Timer modification may invoke the checkpoint and
 208           proceed to recursion. */
 209        return true;
 210    }
 211    in_checkpoint = true;
 212
 213    replay_save_instructions();
 214
 215    if (replay_mode == REPLAY_MODE_PLAY) {
 216        g_assert(replay_mutex_locked());
 217        if (replay_next_event_is(EVENT_CHECKPOINT + checkpoint)) {
 218            replay_finish_event();
 219        } else if (replay_state.data_kind != EVENT_ASYNC) {
 220            res = false;
 221            goto out;
 222        }
 223        replay_read_events(checkpoint);
 224        /* replay_read_events may leave some unread events.
 225           Return false if not all of the events associated with
 226           checkpoint were processed */
 227        res = replay_state.data_kind != EVENT_ASYNC;
 228    } else if (replay_mode == REPLAY_MODE_RECORD) {
 229        g_assert(replay_mutex_locked());
 230        replay_put_event(EVENT_CHECKPOINT + checkpoint);
 231        /* This checkpoint belongs to several threads.
 232           Processing events from different threads is
 233           non-deterministic */
 234        if (checkpoint != CHECKPOINT_CLOCK_WARP_START
 235            /* FIXME: this is temporary fix, other checkpoints
 236                      may also be invoked from the different threads someday.
 237                      Asynchronous event processing should be refactored
 238                      to create additional replay event kind which is
 239                      nailed to the one of the threads and which processes
 240                      the event queue. */
 241            && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) {
 242            replay_save_events(checkpoint);
 243        }
 244        res = true;
 245    }
 246out:
 247    in_checkpoint = false;
 248    return res;
 249}
 250
 251bool replay_has_checkpoint(void)
 252{
 253    bool res = false;
 254    if (replay_mode == REPLAY_MODE_PLAY) {
 255        g_assert(replay_mutex_locked());
 256        replay_account_executed_instructions();
 257        res = EVENT_CHECKPOINT <= replay_state.data_kind
 258              && replay_state.data_kind <= EVENT_CHECKPOINT_LAST;
 259    }
 260    return res;
 261}
 262
 263static void replay_enable(const char *fname, int mode)
 264{
 265    const char *fmode = NULL;
 266    assert(!replay_file);
 267
 268    switch (mode) {
 269    case REPLAY_MODE_RECORD:
 270        fmode = "wb";
 271        break;
 272    case REPLAY_MODE_PLAY:
 273        fmode = "rb";
 274        break;
 275    default:
 276        fprintf(stderr, "Replay: internal error: invalid replay mode\n");
 277        exit(1);
 278    }
 279
 280    atexit(replay_finish);
 281
 282    replay_file = fopen(fname, fmode);
 283    if (replay_file == NULL) {
 284        fprintf(stderr, "Replay: open %s: %s\n", fname, strerror(errno));
 285        exit(1);
 286    }
 287
 288    replay_filename = g_strdup(fname);
 289    replay_mode = mode;
 290    replay_mutex_init();
 291
 292    replay_state.data_kind = -1;
 293    replay_state.instruction_count = 0;
 294    replay_state.current_icount = 0;
 295    replay_state.has_unread_data = 0;
 296
 297    /* skip file header for RECORD and check it for PLAY */
 298    if (replay_mode == REPLAY_MODE_RECORD) {
 299        fseek(replay_file, HEADER_SIZE, SEEK_SET);
 300    } else if (replay_mode == REPLAY_MODE_PLAY) {
 301        unsigned int version = replay_get_dword();
 302        if (version != REPLAY_VERSION) {
 303            fprintf(stderr, "Replay: invalid input log file version\n");
 304            exit(1);
 305        }
 306        /* go to the beginning */
 307        fseek(replay_file, HEADER_SIZE, SEEK_SET);
 308        replay_fetch_data_kind();
 309    }
 310
 311    replay_init_events();
 312}
 313
 314void replay_configure(QemuOpts *opts)
 315{
 316    const char *fname;
 317    const char *rr;
 318    ReplayMode mode = REPLAY_MODE_NONE;
 319    Location loc;
 320
 321    if (!opts) {
 322        return;
 323    }
 324
 325    loc_push_none(&loc);
 326    qemu_opts_loc_restore(opts);
 327
 328    rr = qemu_opt_get(opts, "rr");
 329    if (!rr) {
 330        /* Just enabling icount */
 331        goto out;
 332    } else if (!strcmp(rr, "record")) {
 333        mode = REPLAY_MODE_RECORD;
 334    } else if (!strcmp(rr, "replay")) {
 335        mode = REPLAY_MODE_PLAY;
 336    } else {
 337        error_report("Invalid icount rr option: %s", rr);
 338        exit(1);
 339    }
 340
 341    fname = qemu_opt_get(opts, "rrfile");
 342    if (!fname) {
 343        error_report("File name not specified for replay");
 344        exit(1);
 345    }
 346
 347    replay_snapshot = g_strdup(qemu_opt_get(opts, "rrsnapshot"));
 348    replay_vmstate_register();
 349    replay_enable(fname, mode);
 350
 351out:
 352    loc_pop(&loc);
 353}
 354
 355void replay_start(void)
 356{
 357    if (replay_mode == REPLAY_MODE_NONE) {
 358        return;
 359    }
 360
 361    if (replay_blockers) {
 362        error_reportf_err(replay_blockers->data, "Record/replay: ");
 363        exit(1);
 364    }
 365    if (!icount_enabled()) {
 366        error_report("Please enable icount to use record/replay");
 367        exit(1);
 368    }
 369
 370    /* Timer for snapshotting will be set up here. */
 371
 372    replay_enable_events();
 373}
 374
 375void replay_finish(void)
 376{
 377    if (replay_mode == REPLAY_MODE_NONE) {
 378        return;
 379    }
 380
 381    replay_save_instructions();
 382
 383    /* finalize the file */
 384    if (replay_file) {
 385        if (replay_mode == REPLAY_MODE_RECORD) {
 386            /*
 387             * Can't do it in the signal handler, therefore
 388             * add shutdown event here for the case of Ctrl-C.
 389             */
 390            replay_shutdown_request(SHUTDOWN_CAUSE_HOST_SIGNAL);
 391            /* write end event */
 392            replay_put_event(EVENT_END);
 393
 394            /* write header */
 395            fseek(replay_file, 0, SEEK_SET);
 396            replay_put_dword(REPLAY_VERSION);
 397        }
 398
 399        fclose(replay_file);
 400        replay_file = NULL;
 401    }
 402    if (replay_filename) {
 403        g_free(replay_filename);
 404        replay_filename = NULL;
 405    }
 406
 407    g_free(replay_snapshot);
 408    replay_snapshot = NULL;
 409
 410    replay_mode = REPLAY_MODE_NONE;
 411
 412    replay_finish_events();
 413}
 414
 415void replay_add_blocker(Error *reason)
 416{
 417    replay_blockers = g_slist_prepend(replay_blockers, reason);
 418}
 419
 420const char *replay_get_filename(void)
 421{
 422    return replay_filename;
 423}
 424