Broken description. This one is correct: replay: recording of the user input This records user input (keyboard and mouse events) in record mode and replays these input events in replay mode.
Pavel Dovgalyuk > -----Original Message----- > From: Pavel Dovgalyuk [mailto:pavel.dovga...@ispras.ru] > Sent: Monday, May 25, 2015 3:09 PM > To: qemu-devel@nongnu.org > Cc: peter.mayd...@linaro.org; peter.crosthwa...@xilinx.com; ebl...@redhat.com; > mark.bur...@greensocs.com; r...@ispras.ru; batuz...@ispras.ru; > maria.klimushenk...@ispras.ru; > pavel.dovga...@ispras.ru; pbonz...@redhat.com; alex.ben...@linaro.org; > fred.kon...@greensocs.com > Subject: [PATCH v14 21/21] replay: ptimer > > This patch adds deterministic replay for hardware periodic countdown timers. > ptimer uses bottom halves layer to execute such an asynchronous callback. > We put this callback into the replay queue instead of bottom halves one. > When checkpoint is met by main loop thread, the replay queue is processed > and callback is executed. Binding callback moment to one of the checkpoints > makes it deterministic. > > Signed-off-by: Pavel Dovgalyuk <pavel.dovga...@ispras.ru> > --- > include/ui/input.h | 2 + > replay/Makefile.objs | 1 > replay/replay-events.c | 31 +++++++++ > replay/replay-input.c | 159 > ++++++++++++++++++++++++++++++++++++++++++++++ > replay/replay-internal.h | 13 ++++ > replay/replay.h | 4 + > ui/input.c | 27 +++++--- > 7 files changed, 229 insertions(+), 8 deletions(-) > create mode 100755 replay/replay-input.c > > diff --git a/include/ui/input.h b/include/ui/input.h > index 5d5ac00..d06a12d 100644 > --- a/include/ui/input.h > +++ b/include/ui/input.h > @@ -33,7 +33,9 @@ void qemu_input_handler_bind(QemuInputHandlerState *s, > const char *device_id, int head, > Error **errp); > void qemu_input_event_send(QemuConsole *src, InputEvent *evt); > +void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt); > void qemu_input_event_sync(void); > +void qemu_input_event_sync_impl(void); > > InputEvent *qemu_input_event_new_key(KeyValue *key, bool down); > void qemu_input_event_send_key(QemuConsole *src, KeyValue *key, bool down); > diff --git a/replay/Makefile.objs b/replay/Makefile.objs > index 257c320..3936296 100755 > --- a/replay/Makefile.objs > +++ b/replay/Makefile.objs > @@ -2,3 +2,4 @@ obj-y += replay.o > obj-y += replay-internal.o > obj-y += replay-events.o > obj-y += replay-time.o > +obj-y += replay-input.o > diff --git a/replay/replay-events.c b/replay/replay-events.c > index 1e14cce..6cf13f2 100755 > --- a/replay/replay-events.c > +++ b/replay/replay-events.c > @@ -14,6 +14,7 @@ > #include "replay.h" > #include "replay-internal.h" > #include "block/aio.h" > +#include "ui/input.h" > > typedef struct Event { > ReplayAsyncEventKind event_kind; > @@ -39,6 +40,13 @@ static void replay_run_event(Event *event) > case REPLAY_ASYNC_EVENT_PTIMER: > aio_bh_call(event->opaque); > break; > + case REPLAY_ASYNC_EVENT_INPUT: > + qemu_input_event_send_impl(NULL, (InputEvent *)event->opaque); > + qapi_free_InputEvent((InputEvent *)event->opaque); > + break; > + case REPLAY_ASYNC_EVENT_INPUT_SYNC: > + qemu_input_event_sync_impl(); > + break; > default: > error_report("Replay: invalid async event ID (%d) in the queue", > event->event_kind); > @@ -132,6 +140,16 @@ void replay_add_ptimer_event(void *bh, uint64_t id) > replay_add_event_internal(REPLAY_ASYNC_EVENT_PTIMER, bh, NULL, id); > } > > +void replay_add_input_event(struct InputEvent *event) > +{ > + replay_add_event_internal(REPLAY_ASYNC_EVENT_INPUT, event, NULL, 0); > +} > + > +void replay_add_input_sync_event(void) > +{ > + replay_add_event_internal(REPLAY_ASYNC_EVENT_INPUT_SYNC, NULL, NULL, 0); > +} > + > static void replay_save_event(Event *event, int checkpoint) > { > if (replay_mode != REPLAY_MODE_PLAY) { > @@ -145,6 +163,9 @@ static void replay_save_event(Event *event, int > checkpoint) > case REPLAY_ASYNC_EVENT_PTIMER: > replay_put_qword(event->id); > break; > + case REPLAY_ASYNC_EVENT_INPUT: > + replay_save_input_event(event->opaque); > + break; > } > } > } > @@ -185,6 +206,16 @@ static Event *replay_read_event(int checkpoint) > read_id = replay_get_qword(); > } > break; > + case REPLAY_ASYNC_EVENT_INPUT: > + event = g_malloc0(sizeof(Event)); > + event->event_kind = read_event_kind; > + event->opaque = replay_read_input_event(); > + return event; > + case REPLAY_ASYNC_EVENT_INPUT_SYNC: > + event = g_malloc0(sizeof(Event)); > + event->event_kind = read_event_kind; > + event->opaque = 0; > + return event; > default: > error_report("Unknown ID %d of replay event", read_event_kind); > exit(1); > diff --git a/replay/replay-input.c b/replay/replay-input.c > new file mode 100755 > index 0000000..54923b9 > --- /dev/null > +++ b/replay/replay-input.c > @@ -0,0 +1,159 @@ > +/* > + * replay-input.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 "replay.h" > +#include "replay-internal.h" > +#include "ui/input.h" > +#include "qapi/qmp-output-visitor.h" > +#include "qapi/qmp-input-visitor.h" > +#include "qapi-visit.h" > + > +static InputEvent *qapi_clone_InputEvent(InputEvent *src) > +{ > + QmpOutputVisitor *qov; > + QmpInputVisitor *qiv; > + Visitor *ov, *iv; > + QObject *obj; > + InputEvent *dst = NULL; > + > + qov = qmp_output_visitor_new(); > + ov = qmp_output_get_visitor(qov); > + visit_type_InputEvent(ov, &src, NULL, &error_abort); > + obj = qmp_output_get_qobject(qov); > + qmp_output_visitor_cleanup(qov); > + if (!obj) { > + return NULL; > + } > + > + qiv = qmp_input_visitor_new(obj); > + iv = qmp_input_get_visitor(qiv); > + visit_type_InputEvent(iv, &dst, NULL, &error_abort); > + qmp_input_visitor_cleanup(qiv); > + qobject_decref(obj); > + > + return dst; > +} > + > +void replay_save_input_event(InputEvent *evt) > +{ > + replay_put_dword(evt->kind); > + > + switch (evt->kind) { > + case INPUT_EVENT_KIND_KEY: > + replay_put_dword(evt->key->key->kind); > + > + switch (evt->key->key->kind) { > + case KEY_VALUE_KIND_NUMBER: > + replay_put_qword(evt->key->key->number); > + replay_put_byte(evt->key->down); > + break; > + case KEY_VALUE_KIND_QCODE: > + replay_put_dword(evt->key->key->qcode); > + replay_put_byte(evt->key->down); > + break; > + case KEY_VALUE_KIND_MAX: > + /* keep gcc happy */ > + break; > + } > + break; > + case INPUT_EVENT_KIND_BTN: > + replay_put_dword(evt->btn->button); > + replay_put_byte(evt->btn->down); > + break; > + case INPUT_EVENT_KIND_REL: > + replay_put_dword(evt->rel->axis); > + replay_put_qword(evt->rel->value); > + break; > + case INPUT_EVENT_KIND_ABS: > + replay_put_dword(evt->abs->axis); > + replay_put_qword(evt->abs->value); > + break; > + case INPUT_EVENT_KIND_MAX: > + /* keep gcc happy */ > + break; > + } > +} > + > +InputEvent *replay_read_input_event(void) > +{ > + InputEvent evt; > + KeyValue keyValue; > + InputKeyEvent key; > + key.key = &keyValue; > + InputBtnEvent btn; > + InputMoveEvent rel; > + InputMoveEvent abs; > + > + evt.kind = replay_get_dword(); > + switch (evt.kind) { > + case INPUT_EVENT_KIND_KEY: > + evt.key = &key; > + evt.key->key->kind = replay_get_dword(); > + > + switch (evt.key->key->kind) { > + case KEY_VALUE_KIND_NUMBER: > + evt.key->key->number = replay_get_qword(); > + evt.key->down = replay_get_byte(); > + break; > + case KEY_VALUE_KIND_QCODE: > + evt.key->key->qcode = (QKeyCode)replay_get_dword(); > + evt.key->down = replay_get_byte(); > + break; > + case KEY_VALUE_KIND_MAX: > + /* keep gcc happy */ > + break; > + } > + break; > + case INPUT_EVENT_KIND_BTN: > + evt.btn = &btn; > + evt.btn->button = (InputButton)replay_get_dword(); > + evt.btn->down = replay_get_byte(); > + break; > + case INPUT_EVENT_KIND_REL: > + evt.rel = &rel; > + evt.rel->axis = (InputAxis)replay_get_dword(); > + evt.rel->value = replay_get_qword(); > + break; > + case INPUT_EVENT_KIND_ABS: > + evt.abs = &abs; > + evt.abs->axis = (InputAxis)replay_get_dword(); > + evt.abs->value = replay_get_qword(); > + break; > + case INPUT_EVENT_KIND_MAX: > + /* keep gcc happy */ > + break; > + } > + > + return qapi_clone_InputEvent(&evt); > +} > + > +void replay_input_event(QemuConsole *src, InputEvent *evt) > +{ > + if (replay_mode == REPLAY_MODE_PLAY) { > + /* Nothing */ > + } else if (replay_mode == REPLAY_MODE_RECORD) { > + replay_add_input_event(qapi_clone_InputEvent(evt)); > + } else { > + qemu_input_event_send_impl(src, evt); > + } > +} > + > +void replay_input_sync_event(void) > +{ > + if (replay_mode == REPLAY_MODE_PLAY) { > + /* Nothing */ > + } else if (replay_mode == REPLAY_MODE_RECORD) { > + replay_add_input_sync_event(); > + } else { > + qemu_input_event_sync_impl(); > + } > +} > diff --git a/replay/replay-internal.h b/replay/replay-internal.h > index 36a6fd8..5e2e056 100755 > --- a/replay/replay-internal.h > +++ b/replay/replay-internal.h > @@ -42,6 +42,8 @@ enum ReplayEvents { > > enum ReplayAsyncEventKind { > REPLAY_ASYNC_EVENT_PTIMER, > + REPLAY_ASYNC_EVENT_INPUT, > + REPLAY_ASYNC_EVENT_INPUT_SYNC, > REPLAY_ASYNC_COUNT > }; > > @@ -126,4 +128,15 @@ void replay_read_events(int checkpoint); > /*! Adds specified async event to the queue */ > void replay_add_event(ReplayAsyncEventKind event_id, void *opaque); > > +/* Input events */ > + > +/*! Saves input event to the log */ > +void replay_save_input_event(InputEvent *evt); > +/*! Reads input event from the log */ > +InputEvent *replay_read_input_event(void); > +/*! Adds input event to the queue */ > +void replay_add_input_event(struct InputEvent *event); > +/*! Adds input sync event to the queue */ > +void replay_add_input_sync_event(void); > + > #endif > diff --git a/replay/replay.h b/replay/replay.h > index 9f6f288..db0bf82 100755 > --- a/replay/replay.h > +++ b/replay/replay.h > @@ -111,5 +111,9 @@ void replay_disable_events(void); > bool replay_events_enabled(void); > /*! Adds ptimer event to the queue */ > void replay_add_ptimer_event(void *bh, uint64_t id); > +/*! Adds input event to the queue */ > +void replay_input_event(QemuConsole *src, InputEvent *evt); > +/*! Adds input sync event to the queue */ > +void replay_input_sync_event(void); > > #endif > diff --git a/ui/input.c b/ui/input.c > index eeeabe8..2e50fa1 100644 > --- a/ui/input.c > +++ b/ui/input.c > @@ -5,6 +5,7 @@ > #include "trace.h" > #include "ui/input.h" > #include "ui/console.h" > +#include "replay/replay.h" > > struct QemuInputHandlerState { > DeviceState *dev; > @@ -298,14 +299,10 @@ static void qemu_input_queue_sync(struct > QemuInputEventQueueHead *queue) > QTAILQ_INSERT_TAIL(queue, item, node); > } > > -void qemu_input_event_send(QemuConsole *src, InputEvent *evt) > +void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt) > { > QemuInputHandlerState *s; > > - if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { > - return; > - } > - > qemu_input_event_trace(src, evt); > > /* pre processing */ > @@ -322,14 +319,19 @@ void qemu_input_event_send(QemuConsole *src, InputEvent > *evt) > s->events++; > } > > -void qemu_input_event_sync(void) > +void qemu_input_event_send(QemuConsole *src, InputEvent *evt) > { > - QemuInputHandlerState *s; > - > if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { > return; > } > > + replay_input_event(src, evt); > +} > + > +void qemu_input_event_sync_impl(void) > +{ > + QemuInputHandlerState *s; > + > trace_input_event_sync(); > > QTAILQ_FOREACH(s, &handlers, node) { > @@ -343,6 +345,15 @@ void qemu_input_event_sync(void) > } > } > > +void qemu_input_event_sync(void) > +{ > + if (!runstate_is_running() && !runstate_check(RUN_STATE_SUSPENDED)) { > + return; > + } > + > + replay_input_sync_event(); > +} > + > InputEvent *qemu_input_event_new_key(KeyValue *key, bool down) > { > InputEvent *evt = g_new0(InputEvent, 1);