[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC PATCH v3 44/49] replay: serial port
From: |
Pavel Dovgalyuk |
Subject: |
[Qemu-devel] [RFC PATCH v3 44/49] replay: serial port |
Date: |
Thu, 31 Jul 2014 16:57:37 +0400 |
User-agent: |
StGit/0.16 |
This patch implements record and replay of serial ports.
In record mode serial port can be connected to the source of the data:
-serial tcp:127.0.0.1:23,nowait,server
In replay mode it should not be connected to any data source, but should be
initialized with null: -serial null
Signed-off-by: Pavel Dovgalyuk <address@hidden>
---
include/sysemu/char.h | 25 ++++++++++++
qemu-char.c | 56 +++++++++++++++++++++++---
replay/Makefile.objs | 1
replay/replay-char.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++
replay/replay-events.c | 18 ++++++++
replay/replay-internal.h | 14 ++++++
replay/replay.c | 14 ++++++
replay/replay.h | 13 ++++++
vl.c | 4 +-
9 files changed, 238 insertions(+), 7 deletions(-)
create mode 100755 replay/replay-char.c
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 0bbd631..a7eb578 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -85,6 +85,7 @@ struct CharDriverState {
int is_mux;
guint fd_in_tag;
QemuOpts *opts;
+ bool replay;
QTAILQ_ENTRY(CharDriverState) next;
};
@@ -126,6 +127,21 @@ CharDriverState *qemu_chr_new(const char *label, const
char *filename,
void (*init)(struct CharDriverState *s));
/**
+ * @qemu_chr_new_replay:
+ *
+ * Create a new character backend from a URI.
+ * All recieved data will be logged by replay module.
+ *
+ * @label the name of the backend
+ * @filename the URI
+ * @init not sure..
+ *
+ * Returns: a new character backend
+ */
+CharDriverState *qemu_chr_new_replay(const char *label, const char *filename,
+ void (*init)(struct CharDriverState *s));
+
+/**
* @qemu_chr_delete:
*
* Destroy a character backend.
@@ -320,6 +336,15 @@ int qemu_chr_be_can_write(CharDriverState *s);
*/
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:
diff --git a/qemu-char.c b/qemu-char.c
index 956be49..046e88b 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -28,6 +28,7 @@
#include "sysemu/char.h"
#include "hw/usb.h"
#include "qmp-commands.h"
+#include "replay/replay.h"
#include <unistd.h>
#include <fcntl.h>
@@ -126,6 +127,9 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t
*buf, int len)
qemu_mutex_lock(&s->chr_write_lock);
ret = s->chr_write(s, buf, len);
+ if (s->replay) {
+ replay_data_int(&ret);
+ }
qemu_mutex_unlock(&s->chr_write_lock);
return ret;
}
@@ -195,9 +199,19 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf,
int len)
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) {
+ res = -ENOTSUP;
+ } else {
+ res = s->chr_ioctl(s, cmd, arg);
+ if (s->replay) {
+ fprintf(stderr,
+ "Replay: ioctl is not supported for serial devices yet\n");
+ exit(1);
+ }
+ }
+
+ return res;
}
int qemu_chr_be_can_write(CharDriverState *s)
@@ -207,17 +221,34 @@ 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) {
+ fprintf(stderr, "Replay: calling qemu_chr_be_write in play
mode\n");
+ exit(1);
+ }
+ 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) {
+ replay_data_int(&res);
+ }
+ return res;
}
int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len)
@@ -3631,6 +3662,21 @@ CharDriverState *qemu_chr_new(const char *label, const
char *filename, void (*in
return chr;
}
+CharDriverState *qemu_chr_new_replay(const char *label, const char *filename,
+ void (*init)(struct CharDriverState *s))
+{
+ if (replay_mode == REPLAY_MODE_PLAY && (strcmp(filename, "null")
+ && strcmp(filename, "vc:80Cx24C"))) {
+ fprintf(stderr, "Only \"-serial null\" can be used with replay\n");
+ exit(1);
+ }
+ CharDriverState *chr = qemu_chr_new(label, filename, init);
+ if (strcmp(filename, "vc:80Cx24C")) {
+ replay_register_char_driver(chr);
+ }
+ return chr;
+}
+
void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
{
if (chr->chr_set_echo) {
diff --git a/replay/Makefile.objs b/replay/Makefile.objs
index 9fea604..c528bcb 100755
--- a/replay/Makefile.objs
+++ b/replay/Makefile.objs
@@ -6,3 +6,4 @@ obj-y += replay-icount.o
obj-y += replay-input.o
obj-y += replay-net.o
obj-y += replay-audio.o
+obj-y += replay-char.o
diff --git a/replay/replay-char.c b/replay/replay-char.c
new file mode 100755
index 0000000..1f25eae
--- /dev/null
+++ b/replay/replay-char.c
@@ -0,0 +1,100 @@
+/*
+ * replay-char.c
+ *
+ * Copyright (c) 2010-2014 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 "replay.h"
+#include "replay-internal.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/char.h"
+
+#define MAX_CHAR_DRIVERS MAX_SERIAL_PORTS
+/* Char drivers that generate qemu_chr_be_write events
+ that should be saved into the log. */
+static CharDriverState *char_drivers[MAX_CHAR_DRIVERS];
+
+/* 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;
+ while (i < MAX_CHAR_DRIVERS && char_drivers[i] != chr) {
+ ++i;
+ }
+
+ return i >= MAX_CHAR_DRIVERS ? -1 : i;
+}
+
+
+void replay_register_char_driver(CharDriverState *chr)
+{
+ chr->replay = true;
+ int i = find_char_driver(NULL);
+
+ if (i < 0) {
+ fprintf(stderr, "Replay: cannot register char driver\n");
+ exit(1);
+ } else {
+ char_drivers[i] = 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, event);
+}
+
+void replay_event_char_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_save(void *opaque)
+{
+ CharEvent *event = (CharEvent *)opaque;
+
+ replay_put_byte(event->id);
+ replay_put_array(event->buf, event->len);
+}
+
+void *replay_event_char_read(void)
+{
+ CharEvent *event = g_malloc0(sizeof(CharEvent));
+
+ event->id = replay_get_byte();
+ replay_get_array_alloc(&event->buf, &event->len);
+
+ return event;
+}
diff --git a/replay/replay-events.c b/replay/replay-events.c
index 9dacb52..362fef8 100755
--- a/replay/replay-events.c
+++ b/replay/replay-events.c
@@ -56,6 +56,9 @@ static void replay_run_event(Event *event)
case REPLAY_ASYNC_EVENT_NETWORK:
replay_net_send_packet(event->opaque);
break;
+ case REPLAY_ASYNC_EVENT_CHAR:
+ replay_event_char_run(event->opaque);
+ break;
default:
fprintf(stderr, "Replay: invalid async event ID (%d) in the queue\n",
event->event_kind);
@@ -186,6 +189,9 @@ void replay_save_events(int opt)
case REPLAY_ASYNC_EVENT_NETWORK:
replay_net_save_packet(event->opaque);
break;
+ case REPLAY_ASYNC_EVENT_CHAR:
+ replay_event_char_save(event->opaque);
+ break;
}
}
@@ -257,6 +263,18 @@ void replay_read_events(int opt)
replay_fetch_data_kind();
/* continue with the next event */
continue;
+ case REPLAY_ASYNC_EVENT_CHAR:
+ e.event_kind = read_event_kind;
+ e.opaque = replay_event_char_read();
+
+ replay_has_unread_data = 0;
+ read_event_kind = -1;
+ read_opt = -1;
+ replay_fetch_data_kind();
+
+ replay_run_event(&e);
+ /* continue with the next event */
+ continue;
default:
fprintf(stderr, "Unknown ID %d of replay event\n",
read_event_kind);
exit(1);
diff --git a/replay/replay-internal.h b/replay/replay-internal.h
index d2b1936..9bbfcde 100755
--- a/replay/replay-internal.h
+++ b/replay/replay-internal.h
@@ -38,6 +38,8 @@
/* for async events */
#define EVENT_ASYNC 24
#define EVENT_ASYNC_OPT 25
+/* for int data */
+#define EVENT_DATA_INT 26
/* for instruction event */
#define EVENT_INSTRUCTION 32
/* for clock read/writes */
@@ -56,7 +58,8 @@
#define REPLAY_ASYNC_EVENT_INPUT 2
#define REPLAY_ASYNC_EVENT_INPUT_SYNC 3
#define REPLAY_ASYNC_EVENT_NETWORK 4
-#define REPLAY_ASYNC_COUNT 5
+#define REPLAY_ASYNC_EVENT_CHAR 5
+#define REPLAY_ASYNC_COUNT 6
typedef struct ReplayState {
/*! Cached clock values. */
@@ -183,4 +186,13 @@ void replay_read_sound_out(void);
void replay_read_sound_in(void);
void replay_sound_flush_queue(void);
+/* Character devices */
+
+/*! Called to run char device event. */
+void replay_event_char_run(void *opaque);
+/*! Writes char event to the file. */
+void replay_event_char_save(void *opaque);
+/*! Reads char event from the file. */
+void *replay_event_char_read(void);
+
#endif
diff --git a/replay/replay.c b/replay/replay.c
index bcd8864..7a16da3 100755
--- a/replay/replay.c
+++ b/replay/replay.c
@@ -600,3 +600,17 @@ void replay_finish(void)
replay_net_free();
replay_finish_events();
}
+
+void replay_data_int(int *data)
+{
+ if (replay_file && replay_mode == REPLAY_MODE_PLAY) {
+ skip_async_events_until(EVENT_DATA_INT);
+ *data = replay_get_dword();
+ replay_check_error();
+ replay_has_unread_data = 0;
+ } else if (replay_file && replay_mode == REPLAY_MODE_RECORD) {
+ replay_save_instructions();
+ replay_put_event(EVENT_DATA_INT);
+ replay_put_dword(*data);
+ }
+}
diff --git a/replay/replay.h b/replay/replay.h
index 1d32a2a..e8fd20c 100755
--- a/replay/replay.h
+++ b/replay/replay.h
@@ -24,6 +24,7 @@
struct QemuOpts;
struct InputEvent;
struct NetClientState;
+struct CharDriverState;
/* replay clock kinds */
/* rdtsc */
@@ -149,4 +150,16 @@ void replay_init_sound_in(void *instance, WAVEHDR *hdrs,
int sz);
void replay_init_sound_out(void *instance, WAVEHDR *hdrs, int sz);
#endif
+/* 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);
+
+/* Other data */
+
+/*! Writes or reads integer value to/from replay log. */
+void replay_data_int(int *data);
+
#endif
diff --git a/vl.c b/vl.c
index b0127a2..52fffab 100644
--- a/vl.c
+++ b/vl.c
@@ -2569,7 +2569,9 @@ static int serial_parse(const char *devname)
exit(1);
}
snprintf(label, sizeof(label), "serial%d", index);
- serial_hds[index] = qemu_chr_new(label, devname, NULL);
+ serial_hds[index] = (replay_mode == REPLAY_MODE_NONE ? qemu_chr_new
+ : qemu_chr_new_replay)
+ (label, devname, NULL);
if (!serial_hds[index]) {
fprintf(stderr, "qemu: could not connect serial device"
" to character backend '%s'\n", devname);
- [Qemu-devel] [RFC PATCH v3 34/49] replay: replay aio requests, (continued)
- [Qemu-devel] [RFC PATCH v3 34/49] replay: replay aio requests, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 35/49] replay: thread pool, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 36/49] pl031: vmstate in replay mode, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 37/49] replay: initialization and deinitialization, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 38/49] replay: command line options, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 39/49] replay: snapshotting the virtual machine, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 40/49] replay: recording of the user input, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 41/49] tap-win32: destroy the thread at exit, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 42/49] replay: network packets record/replay, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 43/49] replay: audio data record/replay, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 44/49] replay: serial port,
Pavel Dovgalyuk <=
- [Qemu-devel] [RFC PATCH v3 45/49] replay: USB passthrough, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 46/49] replay: replay_info command, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 47/49] replay: replay_break command, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 48/49] replay: replay_seek_step command, Pavel Dovgalyuk, 2014/07/31
- [Qemu-devel] [RFC PATCH v3 49/49] gdbstub: reverse debugging, Pavel Dovgalyuk, 2014/07/31