sigaction(SIGINT, &act, NULL);
}
#endif
- chr = qemu_chr_new("gdb", device, NULL);
+ chr = qemu_chr_new_noreplay("gdb", device, NULL);
if (!chr)
return -1;
int is_mux;
guint fd_in_tag;
QemuOpts *opts;
+ bool replay;
QTAILQ_ENTRY(CharDriverState) next;
};
CharDriverState *qemu_chr_new(const char *label, const char *filename,
void (*init)(struct CharDriverState *s));
+/**
+ * @qemu_chr_new_noreplay:
+ *
+ * Create a new character backend from a URI.
+ * Character device communications are not written
+ * into the replay log.
+ *
+ * @label the name of the backend
+ * @filename the URI
+ * @init not sure..
+ *
+ * Returns: a new character backend
+ */
+CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
+ void (*init)(struct CharDriverState *s));
+
/**
* @qemu_chr_delete:
*
*/
void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
+/**
+ * @qemu_chr_be_write_impl:
+ *
+ * Implementation of back end writing. Used by replay module.
+ *
+ * @buf a buffer to receive data from the front end
+ * @len the number of bytes to receive from the front end
+ */
+void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len);
/**
* @qemu_chr_be_event:
/*! Adds input sync event to the queue */
void replay_input_sync_event(void);
+/* Character device */
+
+/*! Registers char driver to save it's events */
+void replay_register_char_driver(struct CharDriverState *chr);
+/*! Saves write to char device event to the log */
+void replay_chr_be_write(struct CharDriverState *s, uint8_t *buf, int len);
+/*! Writes char write return value to the replay log. */
+void replay_char_write_event_save(int res, int offset);
+/*! Reads char write return value from the replay log. */
+void replay_char_write_event_load(int *res, int *offset);
+/*! Reads information about read_all character event. */
+int replay_char_read_all_load(uint8_t *buf);
+/*! Writes character read_all error code into the replay log. */
+void replay_char_read_all_save_error(int res);
+/*! Writes character read_all execution result into the replay log. */
+void replay_char_read_all_save_buf(uint8_t *buf, int offset);
+
#endif
#include "io/channel-socket.h"
#include "io/channel-file.h"
#include "io/channel-tls.h"
+#include "sysemu/replay.h"
#include <zlib.h>
}
}
+static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int len, int *offset)
+{
+ int res = 0;
+ *offset = 0;
+
+ qemu_mutex_lock(&s->chr_write_lock);
+ while (*offset < len) {
+ do {
+ res = s->chr_write(s, buf + *offset, len - *offset);
+ if (res == -1 && errno == EAGAIN) {
+ g_usleep(100);
+ }
+ } while (res == -1 && errno == EAGAIN);
+
+ if (res <= 0) {
+ break;
+ }
+
+ *offset += res;
+ }
+ if (*offset > 0) {
+ qemu_chr_fe_write_log(s, buf, *offset);
+ }
+ qemu_mutex_unlock(&s->chr_write_lock);
+
+ return res;
+}
+
int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
{
int ret;
+ if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+ int offset;
+ replay_char_write_event_load(&ret, &offset);
+ assert(offset <= len);
+ qemu_chr_fe_write_buffer(s, buf, offset, &offset);
+ return ret;
+ }
+
qemu_mutex_lock(&s->chr_write_lock);
ret = s->chr_write(s, buf, len);
}
qemu_mutex_unlock(&s->chr_write_lock);
+
+ if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
+ }
+
return ret;
}
int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
{
- int offset = 0;
- int res = 0;
+ int offset;
+ int res;
- qemu_mutex_lock(&s->chr_write_lock);
- while (offset < len) {
- do {
- res = s->chr_write(s, buf + offset, len - offset);
- if (res == -1 && errno == EAGAIN) {
- g_usleep(100);
- }
- } while (res == -1 && errno == EAGAIN);
+ if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+ replay_char_write_event_load(&res, &offset);
+ assert(offset <= len);
+ qemu_chr_fe_write_buffer(s, buf, offset, &offset);
+ return res;
+ }
- if (res <= 0) {
- break;
- }
+ res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
- offset += res;
+ if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ replay_char_write_event_save(res, offset);
}
- if (offset > 0) {
- qemu_chr_fe_write_log(s, buf, offset);
- }
-
- qemu_mutex_unlock(&s->chr_write_lock);
if (res < 0) {
return res;
if (!s->chr_sync_read) {
return 0;
}
+
+ if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
+ return replay_char_read_all_load(buf);
+ }
while (offset < len) {
do {
}
if (res < 0) {
+ if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ replay_char_read_all_save_error(res);
+ }
return res;
}
}
}
+ if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ replay_char_read_all_save_buf(buf, offset);
+ }
return offset;
}
int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
{
- if (!s->chr_ioctl)
- return -ENOTSUP;
- return s->chr_ioctl(s, cmd, arg);
+ int res;
+ if (!s->chr_ioctl || s->replay) {
+ res = -ENOTSUP;
+ } else {
+ res = s->chr_ioctl(s, cmd, arg);
+ }
+
+ return res;
}
int qemu_chr_be_can_write(CharDriverState *s)
return s->chr_can_read(s->handler_opaque);
}
-void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
{
if (s->chr_read) {
s->chr_read(s->handler_opaque, buf, len);
}
}
+void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+{
+ if (s->replay) {
+ if (replay_mode == REPLAY_MODE_PLAY) {
+ return;
+ }
+ replay_chr_be_write(s, buf, len);
+ } else {
+ qemu_chr_be_write_impl(s, buf, len);
+ }
+}
+
int qemu_chr_fe_get_msgfd(CharDriverState *s)
{
int fd;
- return (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
+ int res = (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
+ if (s->replay) {
+ fprintf(stderr,
+ "Replay: get msgfd is not supported for serial devices yet\n");
+ exit(1);
+ }
+ return res;
}
int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len)
return NULL;
}
-CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
+CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
+ void (*init)(struct CharDriverState *s))
{
const char *p;
CharDriverState *chr;
return chr;
}
+CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
+{
+ CharDriverState *chr;
+ chr = qemu_chr_new_noreplay(label, filename, init);
+ if (chr) {
+ chr->replay = replay_mode != REPLAY_MODE_NONE;
+ if (chr->replay && chr->chr_ioctl) {
+ fprintf(stderr,
+ "Replay: ioctl is not supported for serial devices yet\n");
+ }
+ replay_register_char_driver(chr);
+ }
+ return chr;
+}
+
void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
{
if (chr->chr_set_echo) {
error_setg(errp, "Chardev '%s' is busy", id);
return;
}
+ if (chr->replay) {
+ error_setg(errp,
+ "Chardev '%s' cannot be unplugged in record/replay mode", id);
+ return;
+ }
qemu_chr_delete(chr);
}
common-obj-y += replay-events.o
common-obj-y += replay-time.o
common-obj-y += replay-input.o
+common-obj-y += replay-char.o
--- /dev/null
+/*
+ * 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();
+}
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
qemu_input_event_sync_impl();
break;
+ case REPLAY_ASYNC_EVENT_CHAR_READ:
+ replay_event_char_read_run(event->opaque);
+ break;
default:
error_report("Replay: invalid async event ID (%d) in the queue",
event->event_kind);
}
/*! Adds specified async event to the queue */
-static void replay_add_event(ReplayAsyncEventKind event_kind,
- void *opaque,
- void *opaque2, uint64_t id)
+void replay_add_event(ReplayAsyncEventKind event_kind,
+ void *opaque,
+ void *opaque2, uint64_t id)
{
assert(event_kind < REPLAY_ASYNC_COUNT);
break;
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
break;
+ case REPLAY_ASYNC_EVENT_CHAR_READ:
+ replay_event_char_read_save(event->opaque);
+ break;
default:
error_report("Unknown ID %d of replay event", read_event_kind);
exit(1);
event->event_kind = read_event_kind;
event->opaque = 0;
return event;
+ case REPLAY_ASYNC_EVENT_CHAR_READ:
+ event = g_malloc0(sizeof(Event));
+ event->event_kind = read_event_kind;
+ event->opaque = replay_event_char_read_load();
+ return event;
default:
error_report("Unknown ID %d of replay event", read_event_kind);
exit(1);
EVENT_ASYNC,
/* for shutdown request */
EVENT_SHUTDOWN,
+ /* for character device write event */
+ EVENT_CHAR_WRITE,
+ /* for character device read all event */
+ EVENT_CHAR_READ_ALL,
+ EVENT_CHAR_READ_ALL_ERROR,
/* for clock read/writes */
/* some of greater codes are reserved for clocks */
EVENT_CLOCK,
REPLAY_ASYNC_EVENT_BH,
REPLAY_ASYNC_EVENT_INPUT,
REPLAY_ASYNC_EVENT_INPUT_SYNC,
+ REPLAY_ASYNC_EVENT_CHAR_READ,
REPLAY_ASYNC_COUNT
};
void replay_save_events(int checkpoint);
/*! Read events from the file into the input queue */
void replay_read_events(int checkpoint);
+/*! Adds specified async event to the queue */
+void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque,
+ void *opaque2, uint64_t id);
/* Input events */
/*! Adds input sync event to the queue */
void replay_add_input_sync_event(void);
+/* Character devices */
+
+/*! Called to run char device read event. */
+void replay_event_char_read_run(void *opaque);
+/*! Writes char read event to the file. */
+void replay_event_char_read_save(void *opaque);
+/*! Reads char event read from the file. */
+void *replay_event_char_read_load(void);
+
#endif
/* Current version of the replay mechanism.
Increase it when file format changes. */
-#define REPLAY_VERSION 0xe02002
+#define REPLAY_VERSION 0xe02003
/* Size of replay log header */
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
void replay_finish(void)
{
}
+
+void replay_register_char_driver(CharDriverState *chr)
+{
+}
+
+void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
+{
+ abort();
+}
+
+void replay_char_write_event_save(int res, int offset)
+{
+ abort();
+}
+
+void replay_char_write_event_load(int *res, int *offset)
+{
+ abort();
+}
+
+int replay_char_read_all_load(uint8_t *buf)
+{
+ abort();
+}
+
+void replay_char_read_all_save_error(int res)
+{
+ abort();
+}
+
+void replay_char_read_all_save_buf(uint8_t *buf, int offset)
+{
+ abort();
+}