* [Qemu-devel] [RFC PATCH V3 0/5] add direct support of event in qapi schema @ 2014-03-19 5:16 Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 1/5] os-posix: include sys/time.h Wenchao Xia ` (4 more replies) 0 siblings, 5 replies; 14+ messages in thread From: Wenchao Xia @ 2014-03-19 5:16 UTC (permalink / raw) To: qemu-devel; +Cc: mdroth, armbru, Wenchao Xia, lcapitulino This series add support for tag/keyword 'event' in qapi-schema. A new file was created to store some helper functions in patch 2, patch 4 is the test case, patch 5 is a convert example. The implemention is done by generate API and a batch of parameters for each event define, it doesn't generate a struture and visit function in the background for every event, so it doesn't support nested structure in the define to avoid trouble. A callback layer is added to control the behavior. More detail can be found in patch 3's message and incode comments. v2: Address Luiz's comments: patch 3: rename *err to *local_err, do not initialize *qmp = NULL, create a new function qmp_build_evet_dict(). Other change: reorgnized script in patch 3, it have a clear three steps, see patch 3's incode comments. v3: Address Luiz's comments: 2/5: use -1 when fail to get host timestamp. 3/5: typo fix and better incode comments. all unchanged functions are moved into qmp-event.c, from generated file. To do so without include issue, 'int' was used intead of 'enum' in function prototype declaration, since the 'enum' define is a generated type. Address Eric's comments: 2/5: use -1 when fail to get host timestamp. 3/5: typo fix, add docs/qapi-code-gen.txt. 4/5: typo fix, verify no other fields in member 'timestamp'. 5/5: add doc in qapi-schema.json. fix license, all script using GPL2, all C code use LGPL, just as other similar files in upstream. Wenchao Xia (5): 1 os-posix: include sys/time.h 2 qapi: add event helper functions 3 qapi script: add event support 4 test: add test cases for qapi event 5 qapi event: convert RTC_CHANGE Makefile | 9 +- Makefile.objs | 2 +- docs/qapi-code-gen.txt | 18 ++ include/qapi/qmp-event.h | 25 ++ include/sysemu/os-posix.h | 2 + monitor.c | 15 ++ qapi-schema.json | 13 + qapi/Makefile.objs | 1 + qapi/qmp-event.c | 71 ++++++ scripts/qapi-event.py | 373 +++++++++++++++++++++++++++++++ tests/Makefile | 14 +- tests/qapi-schema/qapi-schema-test.json | 12 + tests/qapi-schema/qapi-schema-test.out | 10 +- tests/test-qmp-event.c | 258 +++++++++++++++++++++ vl.c | 7 +- 15 files changed, 816 insertions(+), 14 deletions(-) create mode 100644 include/qapi/qmp-event.h create mode 100644 qapi/qmp-event.c create mode 100644 scripts/qapi-event.py create mode 100644 tests/test-qmp-event.c ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Qemu-devel] [RFC PATCH V3 1/5] os-posix: include sys/time.h 2014-03-19 5:16 [Qemu-devel] [RFC PATCH V3 0/5] add direct support of event in qapi schema Wenchao Xia @ 2014-03-19 5:16 ` Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 2/5] qapi: add event helper functions Wenchao Xia ` (3 subsequent siblings) 4 siblings, 0 replies; 14+ messages in thread From: Wenchao Xia @ 2014-03-19 5:16 UTC (permalink / raw) To: qemu-devel; +Cc: mdroth, armbru, Wenchao Xia, lcapitulino Since gettimeofday() is used in this header file as a macro define, include the function's define header file, to avoid compile warning when other file include os-posix.h. Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> Reviewed-by: Eric Blake <eblake@redhat.com> --- include/sysemu/os-posix.h | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h index 25d0b2a..f131521 100644 --- a/include/sysemu/os-posix.h +++ b/include/sysemu/os-posix.h @@ -26,6 +26,8 @@ #ifndef QEMU_OS_POSIX_H #define QEMU_OS_POSIX_H +#include <sys/time.h> + void os_set_line_buffering(void); void os_set_proc_name(const char *s); void os_setup_signal_handling(void); -- 1.7.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [Qemu-devel] [RFC PATCH V3 2/5] qapi: add event helper functions 2014-03-19 5:16 [Qemu-devel] [RFC PATCH V3 0/5] add direct support of event in qapi schema Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 1/5] os-posix: include sys/time.h Wenchao Xia @ 2014-03-19 5:16 ` Wenchao Xia 2014-03-20 22:53 ` Eric Blake 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 3/5] qapi script: add event support Wenchao Xia ` (2 subsequent siblings) 4 siblings, 1 reply; 14+ messages in thread From: Wenchao Xia @ 2014-03-19 5:16 UTC (permalink / raw) To: qemu-devel; +Cc: mdroth, armbru, Wenchao Xia, lcapitulino This file hold some functions that do not need to be generated. Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> --- include/qapi/qmp-event.h | 25 ++++++++++++++++ qapi/Makefile.objs | 1 + qapi/qmp-event.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 0 deletions(-) create mode 100644 include/qapi/qmp-event.h create mode 100644 qapi/qmp-event.c diff --git a/include/qapi/qmp-event.h b/include/qapi/qmp-event.h new file mode 100644 index 0000000..fdf1a7f --- /dev/null +++ b/include/qapi/qmp-event.h @@ -0,0 +1,25 @@ +/* + * QMP Event related + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef QMP_EVENT_H +#define QMP_EVENT_H + +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" + +typedef void (*QMPEventFuncEmit)(int event_kind, QDict *dict, Error **errp); + +void qmp_event_set_func_emit(QMPEventFuncEmit emit); + +QMPEventFuncEmit qmp_event_get_func_emit(void); + +QDict *qmp_event_build_dict(const char *event_name); +#endif diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index 1f9c973..d14b769 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -3,3 +3,4 @@ util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o util-obj-y += string-input-visitor.o string-output-visitor.o util-obj-y += opts-visitor.o +util-obj-y += qmp-event.o diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c new file mode 100644 index 0000000..191c7f0 --- /dev/null +++ b/qapi/qmp-event.c @@ -0,0 +1,71 @@ +/* + * QMP Event related + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include <inttypes.h> + +#include "qemu-common.h" +#include "qapi/qmp-event.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qjson.h" + +#ifdef _WIN32 +#include "sysemu/os-win32.h" +#endif + +#ifdef CONFIG_POSIX +#include "sysemu/os-posix.h" +#endif + +typedef struct QMPEventFunctions { + QMPEventFuncEmit emit; +} QMPEventFunctions; + +QMPEventFunctions qmp_event_functions; + +void qmp_event_set_func_emit(QMPEventFuncEmit emit) +{ + qmp_event_functions.emit = emit; +} + +QMPEventFuncEmit qmp_event_get_func_emit(void) +{ + return qmp_event_functions.emit; +} + +static void timestamp_put(QDict *qdict) +{ + int err; + QObject *obj; + qemu_timeval tv; + + err = qemu_gettimeofday(&tv); + if (err < 0) { + /* Put -1 to indicate failure of getting host time */ + tv.tv_sec = tv.tv_usec = -1; + } + + obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", " + "'microseconds': %" PRId64 " }", + (int64_t) tv.tv_sec, (int64_t) tv.tv_usec); + qdict_put_obj(qdict, "timestamp", obj); +} + +/* + * Build a QDict, then fill event name and time stamp, caller should free the + * QDict after usage. + */ +QDict *qmp_event_build_dict(const char *event_name) +{ + QDict *dict = qdict_new(); + qdict_put(dict, "event", qstring_from_str(event_name)); + timestamp_put(dict); + return dict; +} -- 1.7.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [RFC PATCH V3 2/5] qapi: add event helper functions 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 2/5] qapi: add event helper functions Wenchao Xia @ 2014-03-20 22:53 ` Eric Blake 2014-03-24 0:53 ` Wenchao Xia 0 siblings, 1 reply; 14+ messages in thread From: Eric Blake @ 2014-03-20 22:53 UTC (permalink / raw) To: Wenchao Xia, qemu-devel; +Cc: mdroth, armbru, lcapitulino [-- Attachment #1: Type: text/plain, Size: 2833 bytes --] On 03/18/2014 11:16 PM, Wenchao Xia wrote: > This file hold some functions that do not need to be generated. s/hold/holds/ > > Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> > --- > include/qapi/qmp-event.h | 25 ++++++++++++++++ > qapi/Makefile.objs | 1 + > qapi/qmp-event.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 97 insertions(+), 0 deletions(-) > create mode 100644 include/qapi/qmp-event.h > create mode 100644 qapi/qmp-event.c > > diff --git a/include/qapi/qmp-event.h b/include/qapi/qmp-event.h > new file mode 100644 > index 0000000..fdf1a7f > --- /dev/null > +++ b/include/qapi/qmp-event.h > @@ -0,0 +1,25 @@ > +/* > + * QMP Event related > + * > + * Authors: > + * Wenchao Xia <wenchaoqemu@gmail.com> > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. > + * See the COPYING.LIB file in the top-level directory. For the [L]GPL to work, someone must assert copyright. > +++ b/qapi/qmp-event.c > @@ -0,0 +1,71 @@ > +/* > + * QMP Event related > + * > + * Authors: > + * Wenchao Xia <wenchaoqemu@gmail.com> > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. > + * See the COPYING.LIB file in the top-level directory. Again, missing an actual use of the word "Copyright". > + > +typedef struct QMPEventFunctions { > + QMPEventFuncEmit emit; > +} QMPEventFunctions; > + > +QMPEventFunctions qmp_event_functions; > + > +void qmp_event_set_func_emit(QMPEventFuncEmit emit) > +{ > + qmp_event_functions.emit = emit; > +} > + > +QMPEventFuncEmit qmp_event_get_func_emit(void) > +{ > + return qmp_event_functions.emit; > +} Is this struct a bit overkill, or do you extend it to include other fields later? > + err = qemu_gettimeofday(&tv); > + if (err < 0) { > + /* Put -1 to indicate failure of getting host time */ > + tv.tv_sec = tv.tv_usec = -1; Believe it or not, this is NOT portable. Let's consider what happens when tv_sec is int64_t and tv_usec is uint32_t. Assignments happen right to left, so tv_usec gets the unsigned value 0xffffffff, then since all uint32_t values fit in int64_t, integer promotion says that the value is 0-extended (not sign-extended), and tv_sec is NOT assigned -1. Solution: break this into two separate statements: tv.tv_sec = -1; tv.tv_usec = -1; > + } > + > + obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", " > + "'microseconds': %" PRId64 " }", > + (int64_t) tv.tv_sec, (int64_t) tv.tv_usec); Indentation is odd, but that's cosmetic. -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 604 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [RFC PATCH V3 2/5] qapi: add event helper functions 2014-03-20 22:53 ` Eric Blake @ 2014-03-24 0:53 ` Wenchao Xia 2014-03-24 13:11 ` Eric Blake 0 siblings, 1 reply; 14+ messages in thread From: Wenchao Xia @ 2014-03-24 0:53 UTC (permalink / raw) To: Eric Blake; +Cc: mdroth, lcapitulino, qemu-devel, armbru 于 2014/3/21 6:53, Eric Blake 写道: > On 03/18/2014 11:16 PM, Wenchao Xia wrote: >> This file hold some functions that do not need to be generated. > s/hold/holds/ > >> Signed-off-by: Wenchao Xia<wenchaoqemu@gmail.com> >> --- >> include/qapi/qmp-event.h | 25 ++++++++++++++++ >> qapi/Makefile.objs | 1 + >> qapi/qmp-event.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++ >> 3 files changed, 97 insertions(+), 0 deletions(-) >> create mode 100644 include/qapi/qmp-event.h >> create mode 100644 qapi/qmp-event.c >> >> diff --git a/include/qapi/qmp-event.h b/include/qapi/qmp-event.h >> new file mode 100644 >> index 0000000..fdf1a7f >> --- /dev/null >> +++ b/include/qapi/qmp-event.h >> @@ -0,0 +1,25 @@ >> +/* >> + * QMP Event related >> + * >> + * Authors: >> + * Wenchao Xia<wenchaoqemu@gmail.com> >> + * >> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. >> + * See the COPYING.LIB file in the top-level directory. > For the [L]GPL to work, someone must assert copyright. Will fix. >> +++ b/qapi/qmp-event.c >> @@ -0,0 +1,71 @@ >> +/* >> + * QMP Event related >> + * >> + * Authors: >> + * Wenchao Xia<wenchaoqemu@gmail.com> >> + * >> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. >> + * See the COPYING.LIB file in the top-level directory. > Again, missing an actual use of the word "Copyright". > >> + >> +typedef struct QMPEventFunctions { >> + QMPEventFuncEmit emit; >> +} QMPEventFunctions; >> + >> +QMPEventFunctions qmp_event_functions; >> + >> +void qmp_event_set_func_emit(QMPEventFuncEmit emit) >> +{ >> + qmp_event_functions.emit = emit; >> +} >> + >> +QMPEventFuncEmit qmp_event_get_func_emit(void) >> +{ >> + return qmp_event_functions.emit; >> +} > Is this struct a bit overkill, or do you extend it to include other > fields later? No other fields will be added in this series, it allow different emit function hooked. Do you mean remove it and put it into generated qapi-event.c? > >> + err = qemu_gettimeofday(&tv); >> + if (err< 0) { >> + /* Put -1 to indicate failure of getting host time */ >> + tv.tv_sec = tv.tv_usec = -1; > Believe it or not, this is NOT portable. Let's consider what happens > when tv_sec is int64_t and tv_usec is uint32_t. Assignments happen > right to left, so tv_usec gets the unsigned value 0xffffffff, then since > all uint32_t values fit in int64_t, integer promotion says that the > value is 0-extended (not sign-extended), and tv_sec is NOT assigned -1. > Solution: break this into two separate statements: > > tv.tv_sec = -1; > tv.tv_usec = -1; Good catch, thanks! >> + } >> + >> + obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", " >> + "'microseconds': %" PRId64 " }", >> + (int64_t) tv.tv_sec, (int64_t) tv.tv_usec); > Indentation is odd, but that's cosmetic. Will fix. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [RFC PATCH V3 2/5] qapi: add event helper functions 2014-03-24 0:53 ` Wenchao Xia @ 2014-03-24 13:11 ` Eric Blake 0 siblings, 0 replies; 14+ messages in thread From: Eric Blake @ 2014-03-24 13:11 UTC (permalink / raw) To: Wenchao Xia; +Cc: mdroth, lcapitulino, qemu-devel, armbru [-- Attachment #1: Type: text/plain, Size: 1406 bytes --] On 03/23/2014 06:53 PM, Wenchao Xia wrote: > 于 2014/3/21 6:53, Eric Blake 写道: >> On 03/18/2014 11:16 PM, Wenchao Xia wrote: >>> This file hold some functions that do not need to be generated. >> s/hold/holds/ >> >>> + >>> +typedef struct QMPEventFunctions { >>> + QMPEventFuncEmit emit; >>> +} QMPEventFunctions; >>> + >>> +QMPEventFunctions qmp_event_functions; >>> + >>> +void qmp_event_set_func_emit(QMPEventFuncEmit emit) >>> +{ >>> + qmp_event_functions.emit = emit; >>> +} >>> + >>> +QMPEventFuncEmit qmp_event_get_func_emit(void) >>> +{ >>> + return qmp_event_functions.emit; >>> +} >> Is this struct a bit overkill, or do you extend it to include other >> fields later? > No other fields will be added in this series, it allow different emit > function hooked. > Do you mean remove it and put it into generated qapi-event.c? Keeping accessor functions is still a bit nicer than directly making a global variable; but my point is that your accessors can just directly access a static variable instead of wrapping things in a pointless struct: static QMPEventFuncEmit qmp_emit; void qmp_event_set_func_emit(QMPEventFuncEmit emit) { qmp_emit = emit; } QMPEventFuncEmit qmp_event_get_func_emit(void) { return qmp_emit; } -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 604 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Qemu-devel] [RFC PATCH V3 3/5] qapi script: add event support 2014-03-19 5:16 [Qemu-devel] [RFC PATCH V3 0/5] add direct support of event in qapi schema Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 1/5] os-posix: include sys/time.h Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 2/5] qapi: add event helper functions Wenchao Xia @ 2014-03-19 5:16 ` Wenchao Xia 2014-03-20 23:06 ` Eric Blake 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 4/5] test: add test cases for qapi event Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 5/5] qapi event: convert RTC_CHANGE Wenchao Xia 4 siblings, 1 reply; 14+ messages in thread From: Wenchao Xia @ 2014-03-19 5:16 UTC (permalink / raw) To: qemu-devel; +Cc: mdroth, armbru, Wenchao Xia, lcapitulino qapi-event.py will parse the schema and generate qapi-event.c, then the API in qapi-event.c can be used to handle event in qemu code. All API have prefix "qapi_event". The script mainly include two parts: generate API for each event define, generate an enum type for all defined events. Since in some case the real emit behavior may change, for example, qemu-img would not send a event, a callback layer is used to control the behavior. As a result, the stubs at compile time can be saved, the binding of block layer code and monitor code will become looser. Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> --- Makefile | 9 +- Makefile.objs | 2 +- docs/qapi-code-gen.txt | 18 +++ scripts/qapi-event.py | 373 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 398 insertions(+), 4 deletions(-) create mode 100644 scripts/qapi-event.py diff --git a/Makefile b/Makefile index ec74039..47cb661 100644 --- a/Makefile +++ b/Makefile @@ -45,8 +45,8 @@ endif endif GENERATED_HEADERS = config-host.h qemu-options.def -GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h -GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c +GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h +GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c GENERATED_HEADERS += trace/generated-events.h GENERATED_SOURCES += trace/generated-events.c @@ -207,7 +207,7 @@ Makefile: $(version-obj-y) $(version-lobj-y) # Build libraries libqemustub.a: $(stub-obj-y) -libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o +libqemuutil.a: $(util-obj-y) qapi-types.o qapi-visit.o qapi-event.o block-modules = $(foreach o,$(block-obj-m),"$(basename $(subst /,-,$o))",) NULL util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' @@ -251,6 +251,9 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py) qapi-visit.c qapi-visit.h :\ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." -b < $<, " GEN $@") +qapi-event.c qapi-event.h :\ +$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-event.py $(qapi-py) + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py $(gen-out-type) -o "." -b < $<, " GEN $@") qmp-commands.h qmp-marshal.c :\ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, " GEN $@") diff --git a/Makefile.objs b/Makefile.objs index a6e0e2a..93697a1 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -12,7 +12,7 @@ block-obj-y += main-loop.o iohandler.o qemu-timer.o block-obj-$(CONFIG_POSIX) += aio-posix.o block-obj-$(CONFIG_WIN32) += aio-win32.o block-obj-y += block/ -block-obj-y += qapi-types.o qapi-visit.o +block-obj-y += qapi-types.o qapi-visit.o qapi-event.o block-obj-y += qemu-io-cmds.o block-obj-y += qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt index d78921f..d87b8a7 100644 --- a/docs/qapi-code-gen.txt +++ b/docs/qapi-code-gen.txt @@ -180,6 +180,24 @@ An example command is: 'data': { 'arg1': 'str', '*arg2': 'str' }, 'returns': 'str' } +=== Events === + +Events are defined with key workd 'event'. When 'data' is also specified, +additional info will be carried on. Finally there will be C API generated +in qapi-event.h, and when called by QEMU code, message with timestamp will +be emit on the wire. If timestamp is -1, it means failure in host time +retrieving. + +An example event is: + +{ 'event': 'EVENT_C', + 'data': { '*a': 'int', 'b': 'str' } } + +Resulting in this JSON object: + +{ "event": "EVENT_C", + "data": { "b": "test string" }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } == Code generation == diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py new file mode 100644 index 0000000..5962483 --- /dev/null +++ b/scripts/qapi-event.py @@ -0,0 +1,373 @@ +# +# QAPI event generator +# +# Authors: +# Wenchao Xia <wenchaoqemu@gmail.com> +# +# This work is licensed under the terms of the GNU GPL, version 2. +# See the COPYING file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + +def _generate_event_api_name(event_name, params): + api_name = "void qapi_event_send_%s(" % c_fun(event_name).lower(); + l = len(api_name) + + if params: + for argname, argentry, optional, structured in parse_args(params): + if structured: + sys.stderr.write("Nested structure define in event is not " + "supported now, event '%s', argname '%s'\n" % + (event_name, argname)) + sys.exit(1) + continue + + if optional: + api_name += "bool has_%s,\n" % c_var(argname) + api_name += "".ljust(l) + + if argentry == "str": + api_name += "const " + api_name += "%s %s,\n" % (c_type(argentry), c_var(argname)) + api_name += "".ljust(l) + + api_name += "Error **errp)" + return api_name; + + +# Following are the core functions that generate C APIs to emit event. + +def generate_event_declaration(api_name): + return mcgen(''' + +%(api_name)s; +''', + api_name = api_name) + +def generate_event_implement(api_name, event_name, params): + # step 1: declare and variables + ret = mcgen(""" + +%(api_name)s +{ + QDict *qmp; + Error *local_err = NULL; + QMPEventFuncEmit emit; +""", + api_name = api_name) + + if params: + ret += mcgen(""" + QmpOutputVisitor *qov; + Visitor *v; + QObject *obj; + +""") + + # step 2: check emit function, create a dict + ret += mcgen(""" + emit = qmp_event_get_func_emit(); + if (!emit) { + return; + } + + qmp = qmp_event_build_dict("%(event_name)s"); + +""", + event_name = event_name) + + # step 3: visit the params if params != None + if params: + ret += mcgen(""" + qov = qmp_output_visitor_new(); + g_assert(qov); + + v = qmp_output_get_visitor(qov); + g_assert(v); + + /* Fake visit, as if all member are under a structure */ + visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err); + if (error_is_set(&local_err)) { + goto clean; + } + +""", + event_name = event_name) + + for argname, argentry, optional, structured in parse_args(params): + if structured: + sys.stderr.write("Nested structure define in event is not " + "supported now, event '%s', argname '%s'\n" % + (event_name, argname)) + sys.exit(1) + + if optional: + ret += mcgen(""" + if (has_%(var)s) { +""", + var = c_var(argname)) + push_indent() + + if argentry == "str": + var_type = "(char **)" + else: + var_type = "" + + ret += mcgen(""" + visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err); + if (error_is_set(&local_err)) { + goto clean; + } +""", + var_type = var_type, + var = c_var(argname), + type = type_name(argentry), + name = argname) + + if optional: + pop_indent() + ret += mcgen(""" + } +""") + + ret += mcgen(""" + + visit_end_struct(v, &local_err); + if (error_is_set(&local_err)) { + goto clean; + } + + obj = qmp_output_get_qobject(qov); + g_assert(obj != NULL); + + qdict_put_obj(qmp, "data", obj); +""") + + # step 4: call qmp event api + ret += mcgen(""" + emit(%(event_enum_value)s, qmp, &local_err); + +""", + event_enum_value = event_enum_value) + + # step 5: clean up + if params: + ret += mcgen(""" + clean: + qmp_output_visitor_cleanup(qov); +""") + ret += mcgen(""" + error_propagate(errp, local_err); + QDECREF(qmp); +} +""") + + return ret + + +# Following are the functions that generate an enum type for all defined +# events, similar with qapi-types.py. Here we already have enum name and +# values which is generated before and recorded in event_enum_*. It also +# walk around the issue that "import qapi-types" can't work. + +def generate_event_enum_decl(event_enum_name, event_enum_values): + lookup_decl = mcgen(''' + +extern const char *%(event_enum_name)s_lookup[]; +''', + event_enum_name = event_enum_name) + + enum_decl = mcgen(''' +typedef enum %(event_enum_name)s +{ +''', + event_enum_name = event_enum_name) + + # append automatically generated _MAX value + enum_max_value = generate_enum_full_value(event_enum_name, "MAX") + enum_values = event_enum_values + [ enum_max_value ] + + i = 0 + for value in enum_values: + enum_decl += mcgen(''' + %(value)s = %(i)d, +''', + value = value, + i = i) + i += 1 + + enum_decl += mcgen(''' +} %(event_enum_name)s; +''', + event_enum_name = event_enum_name) + + return lookup_decl + enum_decl + +def generate_event_enum_lookup(event_enum_name, event_enum_strings): + ret = mcgen(''' + +const char *%(event_enum_name)s_lookup[] = { +''', + event_enum_name = event_enum_name) + + i = 0 + for string in event_enum_strings: + ret += mcgen(''' + "%(string)s", +''', + string = string) + + ret += mcgen(''' + NULL, +}; +''') + return ret + + +# Start the real job + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:", + ["source", "header", "builtins", "prefix=", + "output-dir="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +c_file = 'qapi-event.c' +h_file = 'qapi-event.h' + +do_c = False +do_h = False +do_builtins = False + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + elif o in ("-c", "--source"): + do_c = True + elif o in ("-h", "--header"): + do_h = True + elif o in ("-b", "--builtins"): + do_builtins = True + +if not do_c and not do_h: + do_c = True + do_h = True + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +def maybe_open(really, name, opt): + if really: + return open(name, opt) + else: + import StringIO + return StringIO.StringIO() + +fdef = maybe_open(do_c, c_file, 'w') +fdecl = maybe_open(do_h, h_file, 'w') + +fdef.write(mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI event functions + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "%(header)s" +#include "%(prefix)sqapi-visit.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp-event.h" + +''', + prefix=prefix, header=basename(h_file))) + +fdecl.write(mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI event function + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#ifndef %(guard)s +#define %(guard)s + +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "%(prefix)sqapi-types.h" + +''', + prefix=prefix, guard=guardname(h_file))) + +exprs = parse_schema(sys.stdin) + +event_enum_name = "QAPIEvent" +event_enum_values = [] +event_enum_strings = [] + +for expr in exprs: + if expr.has_key('event'): + event_name = expr['event'] + params = expr.get('data') + if params and len(params) == 0: + params = None + + api_name = _generate_event_api_name(event_name, params) + ret = generate_event_declaration(api_name) + fdecl.write(ret) + + # We need an enum value per event + event_enum_value = generate_enum_full_value(event_enum_name, + event_name) + ret = generate_event_implement(api_name, event_name, params) + fdef.write(ret) + + # Record it, and generate enum later + event_enum_values.append(event_enum_value) + event_enum_strings.append(event_name) + +ret = generate_event_enum_decl(event_enum_name, event_enum_values) +fdecl.write(ret) +ret = generate_event_enum_lookup(event_enum_name, event_enum_strings) +fdef.write(ret) + +fdecl.write(''' +#endif +''') + +fdecl.flush() +fdecl.close() + +fdef.flush() +fdef.close() -- 1.7.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [RFC PATCH V3 3/5] qapi script: add event support 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 3/5] qapi script: add event support Wenchao Xia @ 2014-03-20 23:06 ` Eric Blake 2014-03-24 0:59 ` Wenchao Xia 0 siblings, 1 reply; 14+ messages in thread From: Eric Blake @ 2014-03-20 23:06 UTC (permalink / raw) To: Wenchao Xia, qemu-devel; +Cc: mdroth, armbru, lcapitulino [-- Attachment #1: Type: text/plain, Size: 3735 bytes --] On 03/18/2014 11:16 PM, Wenchao Xia wrote: > qapi-event.py will parse the schema and generate qapi-event.c, then > the API in qapi-event.c can be used to handle event in qemu code. > All API have prefix "qapi_event". > > The script mainly include two parts: generate API for each event s/include/includes/ > define, generate an enum type for all defined events. > > Since in some case the real emit behavior may change, for example, s/case/cases/ > qemu-img would not send a event, a callback layer is used to > control the behavior. As a result, the stubs at compile time > can be saved, the binding of block layer code and monitor code > will become looser. > > Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> > --- > Makefile | 9 +- > Makefile.objs | 2 +- > docs/qapi-code-gen.txt | 18 +++ > scripts/qapi-event.py | 373 ++++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 398 insertions(+), 4 deletions(-) > create mode 100644 scripts/qapi-event.py > > +++ b/docs/qapi-code-gen.txt > @@ -180,6 +180,24 @@ An example command is: > 'data': { 'arg1': 'str', '*arg2': 'str' }, > 'returns': 'str' } > > +=== Events === > + > +Events are defined with key workd 'event'. When 'data' is also specified, s/workd/word/ > +additional info will be carried on. Finally there will be C API generated > +in qapi-event.h, and when called by QEMU code, message with timestamp will s/message/a message/ > +be emit on the wire. If timestamp is -1, it means failure in host time s/emit/emitted/ > +retrieving. s/in host time retrieving/to retrieve host time/ > +++ b/scripts/qapi-event.py > @@ -0,0 +1,373 @@ > +# > +# QAPI event generator > +# > +# Authors: > +# Wenchao Xia <wenchaoqemu@gmail.com> > +# > +# This work is licensed under the terms of the GNU GPL, version 2. > +# See the COPYING file in the top-level directory. Needs to use "Copyright". > + > + if params: > + for argname, argentry, optional, structured in parse_args(params): > + if structured: > + sys.stderr.write("Nested structure define in event is not " > + "supported now, event '%s', argname '%s'\n" % > + (event_name, argname)) > + sys.exit(1) > + continue Isn't this 'continue' dead code? > + > +# Following are the functions that generate an enum type for all defined > +# events, similar with qapi-types.py. Here we already have enum name and s/with/to/ > +# values which is generated before and recorded in event_enum_*. It also s/is/were/ > +# walk around the issue that "import qapi-types" can't work. s/walk around/works around/ > + > +fdef.write(mcgen(''' > +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ > + > +/* > + * schema-defined QAPI event functions > + * > + * Authors: > + * Wenchao Xia <wenchaoqemu@gmail.com> > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. > + * See the COPYING.LIB file in the top-level directory. Also needs "Copyright" > +fdecl.write(mcgen(''' > +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ > + > +/* > + * schema-defined QAPI event function s/function/functions/ > + * > + * Authors: > + * Wenchao Xia <wenchaoqemu@gmail.com> > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. > + * See the COPYING.LIB file in the top-level directory. Needs "Copyright" -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 604 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [RFC PATCH V3 3/5] qapi script: add event support 2014-03-20 23:06 ` Eric Blake @ 2014-03-24 0:59 ` Wenchao Xia 0 siblings, 0 replies; 14+ messages in thread From: Wenchao Xia @ 2014-03-24 0:59 UTC (permalink / raw) To: Eric Blake; +Cc: mdroth, lcapitulino, qemu-devel, armbru 于 2014/3/21 7:06, Eric Blake 写道: > On 03/18/2014 11:16 PM, Wenchao Xia wrote: >> qapi-event.py will parse the schema and generate qapi-event.c, then >> the API in qapi-event.c can be used to handle event in qemu code. >> All API have prefix "qapi_event". >> >> The script mainly include two parts: generate API for each event > s/include/includes/ > >> define, generate an enum type for all defined events. >> >> Since in some case the real emit behavior may change, for example, > s/case/cases/ > >> qemu-img would not send a event, a callback layer is used to >> control the behavior. As a result, the stubs at compile time >> can be saved, the binding of block layer code and monitor code >> will become looser. >> >> Signed-off-by: Wenchao Xia<wenchaoqemu@gmail.com> >> --- >> Makefile | 9 +- >> Makefile.objs | 2 +- >> docs/qapi-code-gen.txt | 18 +++ >> scripts/qapi-event.py | 373 ++++++++++++++++++++++++++++++++++++++++++++++++ >> 4 files changed, 398 insertions(+), 4 deletions(-) >> create mode 100644 scripts/qapi-event.py >> >> +++ b/docs/qapi-code-gen.txt >> @@ -180,6 +180,24 @@ An example command is: >> 'data': { 'arg1': 'str', '*arg2': 'str' }, >> 'returns': 'str' } >> >> +=== Events === >> + >> +Events are defined with key workd 'event'. When 'data' is also specified, > s/workd/word/ > >> +additional info will be carried on. Finally there will be C API generated >> +in qapi-event.h, and when called by QEMU code, message with timestamp will > s/message/a message/ > >> +be emit on the wire. If timestamp is -1, it means failure in host time > s/emit/emitted/ > >> +retrieving. > s/in host time retrieving/to retrieve host time/ > >> +++ b/scripts/qapi-event.py >> @@ -0,0 +1,373 @@ >> +# >> +# QAPI event generator >> +# >> +# Authors: >> +# Wenchao Xia<wenchaoqemu@gmail.com> >> +# >> +# This work is licensed under the terms of the GNU GPL, version 2. >> +# See the COPYING file in the top-level directory. > Needs to use "Copyright". > > >> + >> + if params: >> + for argname, argentry, optional, structured in parse_args(params): >> + if structured: >> + sys.stderr.write("Nested structure define in event is not " >> + "supported now, event '%s', argname '%s'\n" % >> + (event_name, argname)) >> + sys.exit(1) >> + continue > Isn't this 'continue' dead code? Yes, I missed it. I'd like to respin a rfc v4, with better error check function moved into qapi.py, just like the series which check error for other kind of schema error. > >> + >> +# Following are the functions that generate an enum type for all defined >> +# events, similar with qapi-types.py. Here we already have enum name and > s/with/to/ > >> +# values which is generated before and recorded in event_enum_*. It also > s/is/were/ > >> +# walk around the issue that "import qapi-types" can't work. > s/walk around/works around/ > > >> + >> +fdef.write(mcgen(''' >> +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ >> + >> +/* >> + * schema-defined QAPI event functions >> + * >> + * Authors: >> + * Wenchao Xia<wenchaoqemu@gmail.com> >> + * >> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. >> + * See the COPYING.LIB file in the top-level directory. > Also needs "Copyright" > >> +fdecl.write(mcgen(''' >> +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ >> + >> +/* >> + * schema-defined QAPI event function > s/function/functions/ > >> + * >> + * Authors: >> + * Wenchao Xia<wenchaoqemu@gmail.com> >> + * >> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. >> + * See the COPYING.LIB file in the top-level directory. > Needs "Copyright" > ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Qemu-devel] [RFC PATCH V3 4/5] test: add test cases for qapi event 2014-03-19 5:16 [Qemu-devel] [RFC PATCH V3 0/5] add direct support of event in qapi schema Wenchao Xia ` (2 preceding siblings ...) 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 3/5] qapi script: add event support Wenchao Xia @ 2014-03-19 5:16 ` Wenchao Xia 2014-03-21 0:23 ` Eric Blake 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 5/5] qapi event: convert RTC_CHANGE Wenchao Xia 4 siblings, 1 reply; 14+ messages in thread From: Wenchao Xia @ 2014-03-19 5:16 UTC (permalink / raw) To: qemu-devel; +Cc: mdroth, armbru, Wenchao Xia, lcapitulino These cases will verify whether the expected qdict is built. Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> --- tests/Makefile | 14 ++- tests/qapi-schema/qapi-schema-test.json | 12 ++ tests/qapi-schema/qapi-schema-test.out | 10 +- tests/test-qmp-event.c | 258 +++++++++++++++++++++++++++++++ 4 files changed, 289 insertions(+), 5 deletions(-) create mode 100644 tests/test-qmp-event.c diff --git a/tests/Makefile b/tests/Makefile index 471b4c8..5e26542 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -27,6 +27,8 @@ check-unit-y += tests/test-string-input-visitor$(EXESUF) gcov-files-test-string-input-visitor-y = qapi/string-input-visitor.c check-unit-y += tests/test-string-output-visitor$(EXESUF) gcov-files-test-string-output-visitor-y = qapi/string-output-visitor.c +check-unit-y += tests/test-qmp-event$(EXESUF) +gcov-files-test-qmp-event-y += qapi/qmp-event.c check-unit-y += tests/test-opts-visitor$(EXESUF) gcov-files-test-opts-visitor-y = qapi/opts-visitor.c check-unit-y += tests/test-coroutine$(EXESUF) @@ -166,7 +168,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \ flat-union-invalid-branch-key.json flat-union-reverse-define.json \ flat-union-string-discriminator.json) -GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h +GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \ + tests/test-qapi-event.h tests/test-qmp-commands.h test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \ @@ -175,9 +178,10 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \ tests/test-qmp-input-visitor.o tests/test-qmp-input-strict.o \ tests/test-qmp-commands.o tests/test-visitor-serialization.o \ tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ - tests/test-opts-visitor.o + tests/test-opts-visitor.o tests/test-qmp-event.o -test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o +test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ + tests/test-qapi-event.o $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests @@ -219,12 +223,16 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-typ tests/test-qapi-visit.c tests/test-qapi-visit.h :\ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o tests -p "test-" < $<, " GEN $@") +tests/test-qapi-event.c tests/test-qapi-event.h :\ +$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-event.py + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py $(gen-out-type) -o tests -p "test-" < $<, " GEN $@") tests/test-qmp-commands.h tests/test-qmp-marshal.c :\ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o tests -p "test-" < $<, " GEN $@") tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a +tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a tests/test-qmp-output-visitor$(EXESUF): tests/test-qmp-output-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a tests/test-qmp-input-visitor$(EXESUF): tests/test-qmp-input-visitor.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a tests/test-qmp-input-strict$(EXESUF): tests/test-qmp-input-strict.o $(test-qapi-obj-y) libqemuutil.a libqemustub.a diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 818c06d..ab4d3d9 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -89,3 +89,15 @@ '*u16' : [ 'uint16' ], '*i64x': 'int' , '*u64x': 'uint64' } } + +# testing event +{ 'type': 'EventStructOne', + 'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } } + +{ 'event': 'EVENT_A' } +{ 'event': 'EVENT_B', + 'data': { } } +{ 'event': 'EVENT_C', + 'data': { '*a': 'int', '*b': 'UserDefOne', 'c': 'str' } } +{ 'event': 'EVENT_D', + 'data': { 'a' : 'EventStructOne', 'b' : 'str', '*c': 'str', '*enum3': 'EnumOne' } } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 6cd03f3..95e9899 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -15,7 +15,12 @@ OrderedDict([('command', 'user_def_cmd1'), ('data', OrderedDict([('ud1a', 'UserDefOne')]))]), OrderedDict([('command', 'user_def_cmd2'), ('data', OrderedDict([('ud1a', 'UserDefOne'), ('*ud1b', 'UserDefOne')])), ('returns', 'UserDefTwo')]), OrderedDict([('command', 'user_def_cmd3'), ('data', OrderedDict([('a', 'int'), ('*b', 'int')])), ('returns', 'int')]), - OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))])] + OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]), + OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))]), + OrderedDict([('event', 'EVENT_A')]), + OrderedDict([('event', 'EVENT_B'), ('data', OrderedDict())]), + OrderedDict([('event', 'EVENT_C'), ('data', OrderedDict([('*a', 'int'), ('*b', 'UserDefOne'), ('c', 'str')]))]), + OrderedDict([('event', 'EVENT_D'), ('data', OrderedDict([('a', 'EventStructOne'), ('b', 'str'), ('*c', 'str'), ('*enum3', 'EnumOne')]))])] [{'enum_name': 'EnumOne', 'enum_values': ['value1', 'value2', 'value3']}, {'enum_name': 'UserDefUnionKind', 'enum_values': None}, {'enum_name': 'UserDefAnonUnionKind', 'enum_values': None}, @@ -28,4 +33,5 @@ OrderedDict([('type', 'UserDefA'), ('data', OrderedDict([('boolean', 'bool')]))]), OrderedDict([('type', 'UserDefB'), ('data', OrderedDict([('integer', 'int')]))]), OrderedDict([('type', 'UserDefUnionBase'), ('data', OrderedDict([('string', 'str'), ('enum1', 'EnumOne')]))]), - OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))])] + OrderedDict([('type', 'UserDefOptions'), ('data', OrderedDict([('*i64', ['int']), ('*u64', ['uint64']), ('*u16', ['uint16']), ('*i64x', 'int'), ('*u64x', 'uint64')]))]), + OrderedDict([('type', 'EventStructOne'), ('data', OrderedDict([('struct1', 'UserDefOne'), ('string', 'str'), ('*enum2', 'EnumOne')]))])] diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c new file mode 100644 index 0000000..3b84911 --- /dev/null +++ b/tests/test-qmp-event.c @@ -0,0 +1,258 @@ +/* + * qapi event unit-tests. + * + * Authors: + * Wenchao Xia <wenchaoqemu@gmail.com> + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + * + */ + +#include <glib.h> +#include <stdarg.h> + +#include "qemu-common.h" +#include "test-qapi-types.h" +#include "test-qapi-visit.h" +#include "test-qapi-event.h" +#include "qapi/qmp/types.h" +#include "qapi/qmp/qobject.h" +#include "qapi/qmp-event.h" + +typedef struct TestEventData { + QDict *expect; +} TestEventData; + +typedef struct QDictCmpData { + QDict *expect; + bool result; +} QDictCmpData; + +TestEventData *test_event_data; +static GStaticMutex test_event_lock = G_STATIC_MUTEX_INIT; + +/* Only compares bool, int, string */ +static +void qdict_cmp_do_simple(const char *key, QObject *obj1, void *opaque) + +{ + QObject *obj2; + QDictCmpData d_new, *d = opaque; + + if (!d->result) { + return; + } + + obj2 = qdict_get(d->expect, key); + if (!obj2) { + d->result = false; + return; + } + + if (qobject_type(obj1) != qobject_type(obj2)) { + d->result = false; + return; + } + + switch (qobject_type(obj1)) { + case QTYPE_QBOOL: + d->result = (qbool_get_int(qobject_to_qbool(obj1)) == + qbool_get_int(qobject_to_qbool(obj2))); + return; + case QTYPE_QINT: + d->result = (qint_get_int(qobject_to_qint(obj1)) == + qint_get_int(qobject_to_qint(obj2))); + return; + case QTYPE_QSTRING: + if (!g_strcmp0(qstring_get_str(qobject_to_qstring(obj1)), + qstring_get_str(qobject_to_qstring(obj2)))) { + d->result = true; + } else { + d->result = false; + } + return; + case QTYPE_QDICT: + d_new.expect = qobject_to_qdict(obj2); + d_new.result = true; + qdict_iter(qobject_to_qdict(obj1), qdict_cmp_do_simple, &d_new); + d->result = d_new.result; + return; + default: + abort(); + } +} + +static bool qdict_cmp_simple(QDict *a, QDict *b) +{ + QDictCmpData d; + + d.expect = b; + d.result = true; + qdict_iter(a, qdict_cmp_do_simple, &d); + return d.result; +} + +/* This function is hooked as final emit function, which can verify the + correctness. */ +static void event_test_emit(int event_kind, QDict *d, Error **errp) +{ + QObject *obj; + QDict *t; + + /* Verify that we have timestamp, then remove it to compare other field */ + obj = qdict_get(d, "timestamp"); + g_assert(obj); + t = qobject_to_qdict(obj); + g_assert(t); + obj = qdict_get(t, "seconds"); + g_assert(obj && qobject_type(obj) == QTYPE_QINT); + obj = qdict_get(t, "microseconds"); + g_assert(obj && qobject_type(obj) == QTYPE_QINT); + g_assert(qdict_size(t) == 2); + + qdict_del(d, "timestamp"); + + g_assert(qdict_cmp_simple(d, test_event_data->expect)); + +} + +static void event_prepare(TestEventData *data, + const void *unused) +{ + /* Global variable test_event_data was used to pass the expectation, so + test cases can't be executed at same time. */ + g_static_mutex_lock(&test_event_lock); + + data->expect = qdict_new(); + test_event_data = data; +} + +static void event_teardown(TestEventData *data, + const void *unused) +{ + QDECREF(data->expect); + test_event_data = NULL; + + g_static_mutex_unlock(&test_event_lock); +} + +static void event_test_add(const char *testpath, + void (*test_func)(TestEventData *data, + const void *user_data)) +{ + g_test_add(testpath, TestEventData, NULL, event_prepare, test_func, + event_teardown); +} + + +/* Test cases */ + +static void test_event_a(TestEventData *data, + const void *unused) +{ + QDict *d; + d = data->expect; + qdict_put(d, "event", qstring_from_str("EVENT_A")); + qapi_event_send_event_a(NULL); +} + +static void test_event_b(TestEventData *data, + const void *unused) +{ + QDict *d; + d = data->expect; + qdict_put(d, "event", qstring_from_str("EVENT_B")); + qapi_event_send_event_b(NULL); +} + +static void test_event_c(TestEventData *data, + const void *unused) +{ + QDict *d, *d_data, *d_b; + + UserDefOne b; + UserDefZero z; + z.integer = 2; + b.base = &z; + b.string = g_strdup("test1"); + b.has_enum1 = false; + + d_b = qdict_new(); + qdict_put(d_b, "integer", qint_from_int(2)); + qdict_put(d_b, "string", qstring_from_str("test1")); + + d_data = qdict_new(); + qdict_put(d_data, "a", qint_from_int(1)); + qdict_put(d_data, "b", d_b); + qdict_put(d_data, "c", qstring_from_str("test2")); + + d = data->expect; + qdict_put(d, "event", qstring_from_str("EVENT_C")); + qdict_put(d, "data", d_data); + + qapi_event_send_event_c(true, 1, true, &b, "test2", NULL); + + g_free(b.string); +} + +/* Complex type */ +static void test_event_d(TestEventData *data, + const void *unused) +{ + UserDefOne struct1; + EventStructOne a; + UserDefZero z; + QDict *d, *d_data, *d_a, *d_struct1; + + z.integer = 2; + struct1.base = &z; + struct1.string = g_strdup("test1"); + struct1.has_enum1 = true; + struct1.enum1 = ENUM_ONE_VALUE1; + + a.struct1 = &struct1; + a.string = g_strdup("test2"); + a.has_enum2 = true; + a.enum2 = ENUM_ONE_VALUE2; + + d_struct1 = qdict_new(); + qdict_put(d_struct1, "integer", qint_from_int(2)); + qdict_put(d_struct1, "string", qstring_from_str("test1")); + qdict_put(d_struct1, "enum1", qstring_from_str("value1")); + + d_a = qdict_new(); + qdict_put(d_a, "struct1", d_struct1); + qdict_put(d_a, "string", qstring_from_str("test2")); + qdict_put(d_a, "enum2", qstring_from_str("value2")); + + d_data = qdict_new(); + qdict_put(d_data, "a", d_a); + qdict_put(d_data, "b", qstring_from_str("test3")); + qdict_put(d_data, "enum3", qstring_from_str("value3")); + + d = data->expect; + qdict_put(d, "event", qstring_from_str("EVENT_D")); + qdict_put(d, "data", d_data); + + qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3, + NULL); + + g_free(struct1.string); + g_free(a.string); +} + +int main(int argc, char **argv) +{ + qmp_event_set_func_emit(event_test_emit); + + g_test_init(&argc, &argv, NULL); + + event_test_add("/event/event_a", test_event_a); + event_test_add("/event/event_b", test_event_b); + event_test_add("/event/event_c", test_event_c); + event_test_add("/event/event_d", test_event_d); + g_test_run(); + + return 0; +} -- 1.7.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [RFC PATCH V3 4/5] test: add test cases for qapi event 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 4/5] test: add test cases for qapi event Wenchao Xia @ 2014-03-21 0:23 ` Eric Blake 2014-03-24 1:01 ` Wenchao Xia 0 siblings, 1 reply; 14+ messages in thread From: Eric Blake @ 2014-03-21 0:23 UTC (permalink / raw) To: Wenchao Xia, qemu-devel; +Cc: mdroth, armbru, lcapitulino [-- Attachment #1: Type: text/plain, Size: 1807 bytes --] On 03/18/2014 11:16 PM, Wenchao Xia wrote: > These cases will verify whether the expected qdict is built. > > Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> > --- > tests/Makefile | 14 ++- > tests/qapi-schema/qapi-schema-test.json | 12 ++ > tests/qapi-schema/qapi-schema-test.out | 10 +- > tests/test-qmp-event.c | 258 +++++++++++++++++++++++++++++++ > 4 files changed, 289 insertions(+), 5 deletions(-) > create mode 100644 tests/test-qmp-event.c > > +++ b/tests/test-qmp-event.c > @@ -0,0 +1,258 @@ > +/* > + * qapi event unit-tests. > + * > + * Authors: > + * Wenchao Xia <wenchaoqemu@gmail.com> > + * > + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. > + * See the COPYING.LIB file in the top-level directory. > + * Missing "Copyright" > + case QTYPE_QINT: > + d->result = (qint_get_int(qobject_to_qint(obj1)) == > + qint_get_int(qobject_to_qint(obj2))); > + return; > + case QTYPE_QSTRING: > + if (!g_strcmp0(qstring_get_str(qobject_to_qstring(obj1)), > + qstring_get_str(qobject_to_qstring(obj2)))) { > + d->result = true; > + } else { > + d->result = false; > + } Could also be written without 'if': d->result = g_strcmp0(...) == 0; > + obj = qdict_get(t, "seconds"); > + g_assert(obj && qobject_type(obj) == QTYPE_QINT); > + obj = qdict_get(t, "microseconds"); > + g_assert(obj && qobject_type(obj) == QTYPE_QINT); Might be worth asserting that microseconds is within the range [0,999999] (or -1 if seconds is -1) -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 604 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [RFC PATCH V3 4/5] test: add test cases for qapi event 2014-03-21 0:23 ` Eric Blake @ 2014-03-24 1:01 ` Wenchao Xia 0 siblings, 0 replies; 14+ messages in thread From: Wenchao Xia @ 2014-03-24 1:01 UTC (permalink / raw) To: Eric Blake; +Cc: mdroth, lcapitulino, qemu-devel, armbru 于 2014/3/21 8:23, Eric Blake 写道: > On 03/18/2014 11:16 PM, Wenchao Xia wrote: >> These cases will verify whether the expected qdict is built. >> >> Signed-off-by: Wenchao Xia<wenchaoqemu@gmail.com> >> --- >> tests/Makefile | 14 ++- >> tests/qapi-schema/qapi-schema-test.json | 12 ++ >> tests/qapi-schema/qapi-schema-test.out | 10 +- >> tests/test-qmp-event.c | 258 +++++++++++++++++++++++++++++++ >> 4 files changed, 289 insertions(+), 5 deletions(-) >> create mode 100644 tests/test-qmp-event.c >> > >> +++ b/tests/test-qmp-event.c >> @@ -0,0 +1,258 @@ >> +/* >> + * qapi event unit-tests. >> + * >> + * Authors: >> + * Wenchao Xia<wenchaoqemu@gmail.com> >> + * >> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. >> + * See the COPYING.LIB file in the top-level directory. >> + * > Missing "Copyright" > >> + case QTYPE_QINT: >> + d->result = (qint_get_int(qobject_to_qint(obj1)) == >> + qint_get_int(qobject_to_qint(obj2))); >> + return; >> + case QTYPE_QSTRING: >> + if (!g_strcmp0(qstring_get_str(qobject_to_qstring(obj1)), >> + qstring_get_str(qobject_to_qstring(obj2)))) { >> + d->result = true; >> + } else { >> + d->result = false; >> + } > Could also be written without 'if': > d->result = g_strcmp0(...) == 0; > >> + obj = qdict_get(t, "seconds"); >> + g_assert(obj&& qobject_type(obj) == QTYPE_QINT); >> + obj = qdict_get(t, "microseconds"); >> + g_assert(obj&& qobject_type(obj) == QTYPE_QINT); > Might be worth asserting that microseconds is within the range > [0,999999] (or -1 if seconds is -1) > will add those test. ^ permalink raw reply [flat|nested] 14+ messages in thread
* [Qemu-devel] [RFC PATCH V3 5/5] qapi event: convert RTC_CHANGE 2014-03-19 5:16 [Qemu-devel] [RFC PATCH V3 0/5] add direct support of event in qapi schema Wenchao Xia ` (3 preceding siblings ...) 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 4/5] test: add test cases for qapi event Wenchao Xia @ 2014-03-19 5:16 ` Wenchao Xia 2014-03-21 0:25 ` Eric Blake 4 siblings, 1 reply; 14+ messages in thread From: Wenchao Xia @ 2014-03-19 5:16 UTC (permalink / raw) To: qemu-devel; +Cc: mdroth, armbru, Wenchao Xia, lcapitulino This is just an example of how to use qapi event API, and it bypassed the event throttle queue. A complete convert should be first define all events in qapi-schema.json, use qapi event types in monitor functions, then change caller one by one. Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> --- monitor.c | 15 +++++++++++++++ qapi-schema.json | 13 +++++++++++++ vl.c | 7 ++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/monitor.c b/monitor.c index 342e83b..163f654 100644 --- a/monitor.c +++ b/monitor.c @@ -57,6 +57,7 @@ #include "qapi/qmp/qjson.h" #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" +#include "qapi/qmp-event.h" #include <qom/object_interfaces.h> #include "qemu/osdep.h" #include "cpu.h" @@ -76,6 +77,8 @@ #endif #include "hw/lm32/lm32_pic.h" +#include "qapi-event.h" + //#define DEBUG //#define DEBUG_COMPLETION @@ -632,6 +635,16 @@ monitor_protocol_event_throttle(MonitorEvent event, evstate->data = NULL; } +static void monitor_event_emit(int event_kind, QDict *d, Error **errp) +{ + Monitor *mon; + + QLIST_FOREACH(mon, &mon_list, entry) { + if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) { + monitor_json_emitter(mon, QOBJECT(d)); + } + } +} /* Global, one-time initializer to configure the rate limiting * and initialize state */ @@ -644,6 +657,8 @@ static void monitor_protocol_event_init(void) /* limit the rate of quorum events to avoid hammering the management */ monitor_protocol_event_throttle(QEVENT_QUORUM_REPORT_BAD, 1000); monitor_protocol_event_throttle(QEVENT_QUORUM_FAILURE, 1000); + + qmp_event_set_func_emit(monitor_event_emit); } /** diff --git a/qapi-schema.json b/qapi-schema.json index b68cd44..62918c5 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -4686,3 +4686,16 @@ 'btn' : 'InputBtnEvent', 'rel' : 'InputMoveEvent', 'abs' : 'InputMoveEvent' } } + +## +# @RTC_CHANGE +# +# Emitted when the guest changes the RTC time. +# +# @offset: Offset between base RTC clock (as specified by -rtc base), and +# new RTC clock value +# +# Since: 2.1 +## +{ 'event': 'RTC_CHANGE', + 'data': { 'offset' : 'int' } } diff --git a/vl.c b/vl.c index f0fe48b..08a0e56 100644 --- a/vl.c +++ b/vl.c @@ -117,6 +117,7 @@ int main(int argc, char **argv) #include "ui/qemu-spice.h" #include "qapi/string-input-visitor.h" #include "qom/object_interfaces.h" +#include "qapi-event.h" #define DEFAULT_RAM_SIZE 128 @@ -723,11 +724,7 @@ int qemu_timedate_diff(struct tm *tm) void rtc_change_mon_event(struct tm *tm) { - QObject *data; - - data = qobject_from_jsonf("{ 'offset': %d }", qemu_timedate_diff(tm)); - monitor_protocol_event(QEVENT_RTC_CHANGE, data); - qobject_decref(data); + qapi_event_send_rtc_change(qemu_timedate_diff(tm), NULL); } static void configure_rtc_date_offset(const char *startdate, int legacy) -- 1.7.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [Qemu-devel] [RFC PATCH V3 5/5] qapi event: convert RTC_CHANGE 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 5/5] qapi event: convert RTC_CHANGE Wenchao Xia @ 2014-03-21 0:25 ` Eric Blake 0 siblings, 0 replies; 14+ messages in thread From: Eric Blake @ 2014-03-21 0:25 UTC (permalink / raw) To: Wenchao Xia, qemu-devel; +Cc: mdroth, armbru, lcapitulino [-- Attachment #1: Type: text/plain, Size: 842 bytes --] On 03/18/2014 11:16 PM, Wenchao Xia wrote: > This is just an example of how to use qapi event API, and it > bypassed the event throttle queue. A complete convert should > be first define all events in qapi-schema.json, use qapi > event types in monitor functions, then change caller one > by one. This commit message will need to be adjusted when you change this to be a non-RFC series. > > Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com> > --- > monitor.c | 15 +++++++++++++++ > qapi-schema.json | 13 +++++++++++++ > vl.c | 7 ++----- > 3 files changed, 30 insertions(+), 5 deletions(-) > I'm liking where this is headed, and looking forward to the full conversion in 2.1 -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 604 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2014-03-24 13:19 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-03-19 5:16 [Qemu-devel] [RFC PATCH V3 0/5] add direct support of event in qapi schema Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 1/5] os-posix: include sys/time.h Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 2/5] qapi: add event helper functions Wenchao Xia 2014-03-20 22:53 ` Eric Blake 2014-03-24 0:53 ` Wenchao Xia 2014-03-24 13:11 ` Eric Blake 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 3/5] qapi script: add event support Wenchao Xia 2014-03-20 23:06 ` Eric Blake 2014-03-24 0:59 ` Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 4/5] test: add test cases for qapi event Wenchao Xia 2014-03-21 0:23 ` Eric Blake 2014-03-24 1:01 ` Wenchao Xia 2014-03-19 5:16 ` [Qemu-devel] [RFC PATCH V3 5/5] qapi event: convert RTC_CHANGE Wenchao Xia 2014-03-21 0:25 ` Eric Blake
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).