|  | /* | 
|  | * replay-internal.c | 
|  | * | 
|  | * Copyright (c) 2010-2015 Institute for System Programming | 
|  | *                         of the Russian Academy of Sciences. | 
|  | * | 
|  | * This work is licensed under the terms of the GNU GPL, version 2 or later. | 
|  | * See the COPYING file in the top-level directory. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "qemu-common.h" | 
|  | #include "sysemu/replay.h" | 
|  | #include "replay-internal.h" | 
|  | #include "qemu/error-report.h" | 
|  | #include "sysemu/sysemu.h" | 
|  |  | 
|  | unsigned int replay_data_kind = -1; | 
|  | static unsigned int replay_has_unread_data; | 
|  |  | 
|  | /* Mutex to protect reading and writing events to the log. | 
|  | replay_data_kind and replay_has_unread_data are also protected | 
|  | by this mutex. | 
|  | It also protects replay events queue which stores events to be | 
|  | written or read to the log. */ | 
|  | static QemuMutex lock; | 
|  |  | 
|  | /* File for replay writing */ | 
|  | FILE *replay_file; | 
|  |  | 
|  | void replay_put_byte(uint8_t byte) | 
|  | { | 
|  | if (replay_file) { | 
|  | putc(byte, replay_file); | 
|  | } | 
|  | } | 
|  |  | 
|  | void replay_put_event(uint8_t event) | 
|  | { | 
|  | assert(event < EVENT_COUNT); | 
|  | replay_put_byte(event); | 
|  | } | 
|  |  | 
|  |  | 
|  | void replay_put_word(uint16_t word) | 
|  | { | 
|  | replay_put_byte(word >> 8); | 
|  | replay_put_byte(word); | 
|  | } | 
|  |  | 
|  | void replay_put_dword(uint32_t dword) | 
|  | { | 
|  | replay_put_word(dword >> 16); | 
|  | replay_put_word(dword); | 
|  | } | 
|  |  | 
|  | void replay_put_qword(int64_t qword) | 
|  | { | 
|  | replay_put_dword(qword >> 32); | 
|  | replay_put_dword(qword); | 
|  | } | 
|  |  | 
|  | void replay_put_array(const uint8_t *buf, size_t size) | 
|  | { | 
|  | if (replay_file) { | 
|  | replay_put_dword(size); | 
|  | fwrite(buf, 1, size, replay_file); | 
|  | } | 
|  | } | 
|  |  | 
|  | uint8_t replay_get_byte(void) | 
|  | { | 
|  | uint8_t byte = 0; | 
|  | if (replay_file) { | 
|  | byte = getc(replay_file); | 
|  | } | 
|  | return byte; | 
|  | } | 
|  |  | 
|  | uint16_t replay_get_word(void) | 
|  | { | 
|  | uint16_t word = 0; | 
|  | if (replay_file) { | 
|  | word = replay_get_byte(); | 
|  | word = (word << 8) + replay_get_byte(); | 
|  | } | 
|  |  | 
|  | return word; | 
|  | } | 
|  |  | 
|  | uint32_t replay_get_dword(void) | 
|  | { | 
|  | uint32_t dword = 0; | 
|  | if (replay_file) { | 
|  | dword = replay_get_word(); | 
|  | dword = (dword << 16) + replay_get_word(); | 
|  | } | 
|  |  | 
|  | return dword; | 
|  | } | 
|  |  | 
|  | int64_t replay_get_qword(void) | 
|  | { | 
|  | int64_t qword = 0; | 
|  | if (replay_file) { | 
|  | qword = replay_get_dword(); | 
|  | qword = (qword << 32) + replay_get_dword(); | 
|  | } | 
|  |  | 
|  | return qword; | 
|  | } | 
|  |  | 
|  | void replay_get_array(uint8_t *buf, size_t *size) | 
|  | { | 
|  | if (replay_file) { | 
|  | *size = replay_get_dword(); | 
|  | if (fread(buf, 1, *size, replay_file) != *size) { | 
|  | error_report("replay read error"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void replay_get_array_alloc(uint8_t **buf, size_t *size) | 
|  | { | 
|  | if (replay_file) { | 
|  | *size = replay_get_dword(); | 
|  | *buf = g_malloc(*size); | 
|  | if (fread(*buf, 1, *size, replay_file) != *size) { | 
|  | error_report("replay read error"); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void replay_check_error(void) | 
|  | { | 
|  | if (replay_file) { | 
|  | if (feof(replay_file)) { | 
|  | error_report("replay file is over"); | 
|  | qemu_system_vmstop_request_prepare(); | 
|  | qemu_system_vmstop_request(RUN_STATE_PAUSED); | 
|  | } else if (ferror(replay_file)) { | 
|  | error_report("replay file is over or something goes wrong"); | 
|  | qemu_system_vmstop_request_prepare(); | 
|  | qemu_system_vmstop_request(RUN_STATE_INTERNAL_ERROR); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void replay_fetch_data_kind(void) | 
|  | { | 
|  | if (replay_file) { | 
|  | if (!replay_has_unread_data) { | 
|  | replay_data_kind = replay_get_byte(); | 
|  | if (replay_data_kind == EVENT_INSTRUCTION) { | 
|  | replay_state.instructions_count = replay_get_dword(); | 
|  | } | 
|  | replay_check_error(); | 
|  | replay_has_unread_data = 1; | 
|  | if (replay_data_kind >= EVENT_COUNT) { | 
|  | error_report("Replay: unknown event kind %d", replay_data_kind); | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void replay_finish_event(void) | 
|  | { | 
|  | replay_has_unread_data = 0; | 
|  | replay_fetch_data_kind(); | 
|  | } | 
|  |  | 
|  | void replay_mutex_init(void) | 
|  | { | 
|  | qemu_mutex_init(&lock); | 
|  | } | 
|  |  | 
|  | void replay_mutex_destroy(void) | 
|  | { | 
|  | qemu_mutex_destroy(&lock); | 
|  | } | 
|  |  | 
|  | void replay_mutex_lock(void) | 
|  | { | 
|  | qemu_mutex_lock(&lock); | 
|  | } | 
|  |  | 
|  | void replay_mutex_unlock(void) | 
|  | { | 
|  | qemu_mutex_unlock(&lock); | 
|  | } | 
|  |  | 
|  | /*! Saves cached instructions. */ | 
|  | void replay_save_instructions(void) | 
|  | { | 
|  | if (replay_file && replay_mode == REPLAY_MODE_RECORD) { | 
|  | replay_mutex_lock(); | 
|  | int diff = (int)(replay_get_current_step() - replay_state.current_step); | 
|  | if (diff > 0) { | 
|  | replay_put_event(EVENT_INSTRUCTION); | 
|  | replay_put_dword(diff); | 
|  | replay_state.current_step += diff; | 
|  | } | 
|  | replay_mutex_unlock(); | 
|  | } | 
|  | } |