summaryrefslogtreecommitdiff
path: root/qemu-char.c
diff options
context:
space:
mode:
authorPavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru>2016-03-14 10:44:36 +0300
committerPaolo Bonzini <pbonzini@redhat.com>2016-03-15 18:23:40 +0100
commit33577b47c64435fcc2a1bc01c7e82534256f1fc3 (patch)
tree232204ccccc783d694b70e70d366e20a14a286ee /qemu-char.c
parent39c350ee12e733070e63d64a21bd42607366ea99 (diff)
downloadqemu-33577b47c64435fcc2a1bc01c7e82534256f1fc3.tar.gz
qemu-33577b47c64435fcc2a1bc01c7e82534256f1fc3.tar.bz2
qemu-33577b47c64435fcc2a1bc01c7e82534256f1fc3.zip
replay: character devices
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>
Diffstat (limited to 'qemu-char.c')
-rw-r--r--qemu-char.c138
1 files changed, 113 insertions, 25 deletions
diff --git a/qemu-char.c b/qemu-char.c
index 26202c3e63..0a14e57839 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -37,6 +37,7 @@
#include "io/channel-socket.h"
#include "io/channel-file.h"
#include "io/channel-tls.h"
+#include "sysemu/replay.h"
#include <zlib.h>
@@ -234,10 +235,46 @@ static void qemu_chr_fe_write_log(CharDriverState *s,
}
}
+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);
@@ -246,34 +283,31 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int 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;
@@ -289,6 +323,10 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
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 {
@@ -303,6 +341,9 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
}
if (res < 0) {
+ if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
+ replay_char_read_all_save_error(res);
+ }
return res;
}
@@ -313,14 +354,22 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
}
}
+ 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)
@@ -330,17 +379,35 @@ 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)
@@ -3821,7 +3888,8 @@ err:
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;
@@ -3847,6 +3915,21 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in
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) {
@@ -4455,6 +4538,11 @@ void qmp_chardev_remove(const char *id, Error **errp)
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);
}