qemu/replay/replay-internal.c
<<
>>
Prefs
   1/*
   2 * replay-internal.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 "sysemu/replay.h"
  14#include "sysemu/runstate.h"
  15#include "replay-internal.h"
  16#include "qemu/error-report.h"
  17#include "qemu/main-loop.h"
  18
  19/* Mutex to protect reading and writing events to the log.
  20   data_kind and has_unread_data are also protected
  21   by this mutex.
  22   It also protects replay events queue which stores events to be
  23   written or read to the log. */
  24static QemuMutex lock;
  25/* Condition and queue for fair ordering of mutex lock requests. */
  26static QemuCond mutex_cond;
  27static unsigned long mutex_head, mutex_tail;
  28
  29/* File for replay writing */
  30static bool write_error;
  31FILE *replay_file;
  32
  33static void replay_write_error(void)
  34{
  35    if (!write_error) {
  36        error_report("replay write error");
  37        write_error = true;
  38    }
  39}
  40
  41static void replay_read_error(void)
  42{
  43    error_report("error reading the replay data");
  44    exit(1);
  45}
  46
  47void replay_put_byte(uint8_t byte)
  48{
  49    if (replay_file) {
  50        if (putc(byte, replay_file) == EOF) {
  51            replay_write_error();
  52        }
  53    }
  54}
  55
  56void replay_put_event(uint8_t event)
  57{
  58    assert(event < EVENT_COUNT);
  59    replay_put_byte(event);
  60}
  61
  62
  63void replay_put_word(uint16_t word)
  64{
  65    replay_put_byte(word >> 8);
  66    replay_put_byte(word);
  67}
  68
  69void replay_put_dword(uint32_t dword)
  70{
  71    replay_put_word(dword >> 16);
  72    replay_put_word(dword);
  73}
  74
  75void replay_put_qword(int64_t qword)
  76{
  77    replay_put_dword(qword >> 32);
  78    replay_put_dword(qword);
  79}
  80
  81void replay_put_array(const uint8_t *buf, size_t size)
  82{
  83    if (replay_file) {
  84        replay_put_dword(size);
  85        if (fwrite(buf, 1, size, replay_file) != size) {
  86            replay_write_error();
  87        }
  88    }
  89}
  90
  91uint8_t replay_get_byte(void)
  92{
  93    uint8_t byte = 0;
  94    if (replay_file) {
  95        int r = getc(replay_file);
  96        if (r == EOF) {
  97            replay_read_error();
  98        }
  99        byte = r;
 100    }
 101    return byte;
 102}
 103
 104uint16_t replay_get_word(void)
 105{
 106    uint16_t word = 0;
 107    if (replay_file) {
 108        word = replay_get_byte();
 109        word = (word << 8) + replay_get_byte();
 110    }
 111
 112    return word;
 113}
 114
 115uint32_t replay_get_dword(void)
 116{
 117    uint32_t dword = 0;
 118    if (replay_file) {
 119        dword = replay_get_word();
 120        dword = (dword << 16) + replay_get_word();
 121    }
 122
 123    return dword;
 124}
 125
 126int64_t replay_get_qword(void)
 127{
 128    int64_t qword = 0;
 129    if (replay_file) {
 130        qword = replay_get_dword();
 131        qword = (qword << 32) + replay_get_dword();
 132    }
 133
 134    return qword;
 135}
 136
 137void replay_get_array(uint8_t *buf, size_t *size)
 138{
 139    if (replay_file) {
 140        *size = replay_get_dword();
 141        if (fread(buf, 1, *size, replay_file) != *size) {
 142            replay_read_error();
 143        }
 144    }
 145}
 146
 147void replay_get_array_alloc(uint8_t **buf, size_t *size)
 148{
 149    if (replay_file) {
 150        *size = replay_get_dword();
 151        *buf = g_malloc(*size);
 152        if (fread(*buf, 1, *size, replay_file) != *size) {
 153            replay_read_error();
 154        }
 155    }
 156}
 157
 158void replay_check_error(void)
 159{
 160    if (replay_file) {
 161        if (feof(replay_file)) {
 162            error_report("replay file is over");
 163            qemu_system_vmstop_request_prepare();
 164            qemu_system_vmstop_request(RUN_STATE_PAUSED);
 165        } else if (ferror(replay_file)) {
 166            error_report("replay file is over or something goes wrong");
 167            qemu_system_vmstop_request_prepare();
 168            qemu_system_vmstop_request(RUN_STATE_INTERNAL_ERROR);
 169        }
 170    }
 171}
 172
 173void replay_fetch_data_kind(void)
 174{
 175    if (replay_file) {
 176        if (!replay_state.has_unread_data) {
 177            replay_state.data_kind = replay_get_byte();
 178            if (replay_state.data_kind == EVENT_INSTRUCTION) {
 179                replay_state.instruction_count = replay_get_dword();
 180            }
 181            replay_check_error();
 182            replay_state.has_unread_data = 1;
 183            if (replay_state.data_kind >= EVENT_COUNT) {
 184                error_report("Replay: unknown event kind %d",
 185                             replay_state.data_kind);
 186                exit(1);
 187            }
 188        }
 189    }
 190}
 191
 192void replay_finish_event(void)
 193{
 194    replay_state.has_unread_data = 0;
 195    replay_fetch_data_kind();
 196}
 197
 198static __thread bool replay_locked;
 199
 200void replay_mutex_init(void)
 201{
 202    qemu_mutex_init(&lock);
 203    qemu_cond_init(&mutex_cond);
 204    /* Hold the mutex while we start-up */
 205    replay_locked = true;
 206    ++mutex_tail;
 207}
 208
 209bool replay_mutex_locked(void)
 210{
 211    return replay_locked;
 212}
 213
 214/* Ordering constraints, replay_lock must be taken before BQL */
 215void replay_mutex_lock(void)
 216{
 217    if (replay_mode != REPLAY_MODE_NONE) {
 218        unsigned long id;
 219        g_assert(!qemu_mutex_iothread_locked());
 220        g_assert(!replay_mutex_locked());
 221        qemu_mutex_lock(&lock);
 222        id = mutex_tail++;
 223        while (id != mutex_head) {
 224            qemu_cond_wait(&mutex_cond, &lock);
 225        }
 226        replay_locked = true;
 227        qemu_mutex_unlock(&lock);
 228    }
 229}
 230
 231void replay_mutex_unlock(void)
 232{
 233    if (replay_mode != REPLAY_MODE_NONE) {
 234        g_assert(replay_mutex_locked());
 235        qemu_mutex_lock(&lock);
 236        ++mutex_head;
 237        replay_locked = false;
 238        qemu_cond_broadcast(&mutex_cond);
 239        qemu_mutex_unlock(&lock);
 240    }
 241}
 242
 243void replay_advance_current_icount(uint64_t current_icount)
 244{
 245    int diff = (int)(current_icount - replay_state.current_icount);
 246
 247    /* Time can only go forward */
 248    assert(diff >= 0);
 249
 250    if (replay_mode == REPLAY_MODE_RECORD) {
 251        if (diff > 0) {
 252            replay_put_event(EVENT_INSTRUCTION);
 253            replay_put_dword(diff);
 254            replay_state.current_icount += diff;
 255        }
 256    } else if (replay_mode == REPLAY_MODE_PLAY) {
 257        if (diff > 0) {
 258            replay_state.instruction_count -= diff;
 259            replay_state.current_icount += diff;
 260            if (replay_state.instruction_count == 0) {
 261                assert(replay_state.data_kind == EVENT_INSTRUCTION);
 262                replay_finish_event();
 263                /* Wake up iothread. This is required because
 264                    timers will not expire until clock counters
 265                    will be read from the log. */
 266                qemu_notify_event();
 267            }
 268        }
 269        /* Execution reached the break step */
 270        if (replay_break_icount == replay_state.current_icount) {
 271            /* Cannot make callback directly from the vCPU thread */
 272            timer_mod_ns(replay_break_timer,
 273                qemu_clock_get_ns(QEMU_CLOCK_REALTIME));
 274        }
 275    }
 276}
 277
 278/*! Saves cached instructions. */
 279void replay_save_instructions(void)
 280{
 281    if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
 282        g_assert(replay_mutex_locked());
 283        replay_advance_current_icount(replay_get_current_icount());
 284    }
 285}
 286