mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 03:59:52 +00:00
33577b47c6
This patch implements record and replay of character devices. It records chardevs communication in replay mode. Recorded information include data read from backend and counter of bytes written from frontend to backend to preserve frontend internal state. If character device was configured through the command line in record mode, then in replay mode it should be also added to command line. Backend of the character device could be changed in replay mode. Replaying of devices that perform ioctl and get_msgfd operations is not supported. gdbstub which also acts as a backend is not recorded to allow controlling the replaying through gdb. Monitor backends are also not recorded. Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> Message-Id: <20160314074436.4980.83856.stgit@PASHA-ISP> [Add stubs. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
169 lines
4.1 KiB
C
Executable File
169 lines
4.1 KiB
C
Executable File
/*
|
|
* replay-char.c
|
|
*
|
|
* Copyright (c) 2010-2016 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/error-report.h"
|
|
#include "sysemu/replay.h"
|
|
#include "replay-internal.h"
|
|
#include "sysemu/sysemu.h"
|
|
#include "sysemu/char.h"
|
|
|
|
/* Char drivers that generate qemu_chr_be_write events
|
|
that should be saved into the log. */
|
|
static CharDriverState **char_drivers;
|
|
static int drivers_count;
|
|
|
|
/* Char event attributes. */
|
|
typedef struct CharEvent {
|
|
int id;
|
|
uint8_t *buf;
|
|
size_t len;
|
|
} CharEvent;
|
|
|
|
static int find_char_driver(CharDriverState *chr)
|
|
{
|
|
int i = 0;
|
|
for ( ; i < drivers_count ; ++i) {
|
|
if (char_drivers[i] == chr) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void replay_register_char_driver(CharDriverState *chr)
|
|
{
|
|
if (replay_mode == REPLAY_MODE_NONE) {
|
|
return;
|
|
}
|
|
char_drivers = g_realloc(char_drivers,
|
|
sizeof(*char_drivers) * (drivers_count + 1));
|
|
char_drivers[drivers_count++] = chr;
|
|
}
|
|
|
|
void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
|
|
{
|
|
CharEvent *event = g_malloc0(sizeof(CharEvent));
|
|
|
|
event->id = find_char_driver(s);
|
|
if (event->id < 0) {
|
|
fprintf(stderr, "Replay: cannot find char driver\n");
|
|
exit(1);
|
|
}
|
|
event->buf = g_malloc(len);
|
|
memcpy(event->buf, buf, len);
|
|
event->len = len;
|
|
|
|
replay_add_event(REPLAY_ASYNC_EVENT_CHAR_READ, event, NULL, 0);
|
|
}
|
|
|
|
void replay_event_char_read_run(void *opaque)
|
|
{
|
|
CharEvent *event = (CharEvent *)opaque;
|
|
|
|
qemu_chr_be_write_impl(char_drivers[event->id], event->buf,
|
|
(int)event->len);
|
|
|
|
g_free(event->buf);
|
|
g_free(event);
|
|
}
|
|
|
|
void replay_event_char_read_save(void *opaque)
|
|
{
|
|
CharEvent *event = (CharEvent *)opaque;
|
|
|
|
replay_put_byte(event->id);
|
|
replay_put_array(event->buf, event->len);
|
|
}
|
|
|
|
void *replay_event_char_read_load(void)
|
|
{
|
|
CharEvent *event = g_malloc0(sizeof(CharEvent));
|
|
|
|
event->id = replay_get_byte();
|
|
replay_get_array_alloc(&event->buf, &event->len);
|
|
|
|
return event;
|
|
}
|
|
|
|
void replay_char_write_event_save(int res, int offset)
|
|
{
|
|
replay_save_instructions();
|
|
replay_mutex_lock();
|
|
replay_put_event(EVENT_CHAR_WRITE);
|
|
replay_put_dword(res);
|
|
replay_put_dword(offset);
|
|
replay_mutex_unlock();
|
|
}
|
|
|
|
void replay_char_write_event_load(int *res, int *offset)
|
|
{
|
|
replay_account_executed_instructions();
|
|
replay_mutex_lock();
|
|
if (replay_next_event_is(EVENT_CHAR_WRITE)) {
|
|
*res = replay_get_dword();
|
|
*offset = replay_get_dword();
|
|
replay_finish_event();
|
|
replay_mutex_unlock();
|
|
} else {
|
|
replay_mutex_unlock();
|
|
error_report("Missing character write event in the replay log");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
int replay_char_read_all_load(uint8_t *buf)
|
|
{
|
|
replay_mutex_lock();
|
|
if (replay_next_event_is(EVENT_CHAR_READ_ALL)) {
|
|
size_t size;
|
|
int res;
|
|
replay_get_array(buf, &size);
|
|
replay_finish_event();
|
|
replay_mutex_unlock();
|
|
res = (int)size;
|
|
assert(res >= 0);
|
|
return res;
|
|
} else if (replay_next_event_is(EVENT_CHAR_READ_ALL_ERROR)) {
|
|
int res = replay_get_dword();
|
|
replay_finish_event();
|
|
replay_mutex_unlock();
|
|
return res;
|
|
} else {
|
|
replay_mutex_unlock();
|
|
error_report("Missing character read all event in the replay log");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void replay_char_read_all_save_error(int res)
|
|
{
|
|
assert(res < 0);
|
|
replay_save_instructions();
|
|
replay_mutex_lock();
|
|
replay_put_event(EVENT_CHAR_READ_ALL_ERROR);
|
|
replay_put_dword(res);
|
|
replay_mutex_unlock();
|
|
}
|
|
|
|
void replay_char_read_all_save_buf(uint8_t *buf, int offset)
|
|
{
|
|
replay_save_instructions();
|
|
replay_mutex_lock();
|
|
replay_put_event(EVENT_CHAR_READ_ALL);
|
|
replay_put_array(buf, offset);
|
|
replay_mutex_unlock();
|
|
}
|