diff mbox

[V6,03/29] qapi script: add event support

Message ID 1401970944-18735-4-git-send-email-wenchaoqemu@gmail.com
State New
Headers show

Commit Message

Wenchao Xia June 5, 2014, 12:21 p.m. UTC
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 includes two parts: generate API for each event
define, generate an enum type for all defined events.

Since in some cases 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                                 |   11 +-
 Makefile.objs                            |    2 +-
 docs/qapi-code-gen.txt                   |   18 ++
 scripts/qapi-event.py                    |  369 ++++++++++++++++++++++++++++++
 scripts/qapi.py                          |   12 +
 tests/Makefile                           |    2 +-
 tests/qapi-schema/event-nest-struct.err  |    1 +
 tests/qapi-schema/event-nest-struct.exit |    1 +
 tests/qapi-schema/event-nest-struct.json |    2 +
 9 files changed, 413 insertions(+), 5 deletions(-)
 create mode 100644 scripts/qapi-event.py
 create mode 100644 tests/qapi-schema/event-nest-struct.err
 create mode 100644 tests/qapi-schema/event-nest-struct.exit
 create mode 100644 tests/qapi-schema/event-nest-struct.json
 create mode 100644 tests/qapi-schema/event-nest-struct.out

diff --git a/tests/qapi-schema/event-nest-struct.out b/tests/qapi-schema/event-nest-struct.out
new file mode 100644
index 0000000..e69de29

Comments

Eric Blake June 13, 2014, 4:47 p.m. UTC | #1
On 06/05/2014 06:21 AM, 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.

s/event in/events in/

> All API have prefix "qapi_event".
> 
> The script mainly includes two parts: generate API for each event
> define, generate an enum type for all defined events.
> 
> Since in some cases 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                                 |   11 +-
>  Makefile.objs                            |    2 +-
>  docs/qapi-code-gen.txt                   |   18 ++
>  scripts/qapi-event.py                    |  369 ++++++++++++++++++++++++++++++
>  scripts/qapi.py                          |   12 +
>  tests/Makefile                           |    2 +-
>  tests/qapi-schema/event-nest-struct.err  |    1 +
>  tests/qapi-schema/event-nest-struct.exit |    1 +
>  tests/qapi-schema/event-nest-struct.json |    2 +
>  9 files changed, 413 insertions(+), 5 deletions(-)
>  create mode 100644 scripts/qapi-event.py
>  create mode 100644 tests/qapi-schema/event-nest-struct.err
>  create mode 100644 tests/qapi-schema/event-nest-struct.exit
>  create mode 100644 tests/qapi-schema/event-nest-struct.json
>  create mode 100644 tests/qapi-schema/event-nest-struct.out

My python is weak, but I did apply this patch (or rather, used Paolo's
rebase of this patch) to test the generated files.

At this point, I'm interested in seeing the series go in sooner rather
than later, and my findings below are minor enough that I'm okay fixing
them in a followup patch rather than waiting for a respin of the whole
series.

Your patch fails to update .gitignore for the new generated files:

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	qapi-event.c
	qapi-event.h

>  
> +=== Events ===
> +
> +Events are defined with key word 'event'.  When 'data' is also specified,
> +additional info will be carried on.  Finally there will be C API generated

s/carried on/included in the event/

> +++ b/scripts/qapi-event.py
> +
> +def _generate_event_api_name(event_name, params):
> +    api_name = "void qapi_event_send_%s(" % c_fun(event_name).lower();

As of this commit, no events using qapi_event_send are generated yet.
I'm trusting that this code works, but I reserve the right to revisit
this patch later once I look at later patches and see what the generated
code is doing there.  But for now, the generated code has merely:

const char *QAPIEvent_lookup[] = {
    NULL,
};

which looks correct, even if a bit sparse :)

> +++ b/scripts/qapi.py
> @@ -248,6 +248,16 @@ def discriminator_find_enum_define(expr):
>  
>      return find_enum(discriminator_type)
>  
> +def check_event(expr, expr_info):
> +    params = expr.get('data')
> +    if params:
> +        for argname, argentry, optional, structured in parse_args(params):
> +            if structured:
> +                raise QAPIExprError(expr_info,
> +                                    "Nested structure define in event is not "
> +                                    "supported now, event '%s', argname '%s'"

s/ now// - we don't EVER want to support nested structures in events
(for that matter, I've been arguing in other threads that we want to
completely ditch support for nested structures to simplify the generator
code-base and make it possible to cleanly support argument defaults).

> +++ b/tests/qapi-schema/event-nest-struct.err
> @@ -0,0 +1 @@
> +tests/qapi-schema/event-nest-struct.json:1: Nested structure define in event is not supported now, event 'EVENT_A', argname 'a'

Of course, if you tweak the message above, you'll also have to tweak
this test.  But thanks for adding a test!

Since I'm okay saving the cleanups mentioned above for a followup-patch,
and assuming I don't revisit this file:

Reviewed-by: Eric Blake <eblake@redhat.com>
Eric Blake June 13, 2014, 9:28 p.m. UTC | #2
On 06/05/2014 06:21 AM, 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".
> 

As promised, a revisit of the generator code now that I've looked at
more of it in use.  And since I've identified enough elsewhere that
warrants a series respin to v7, you should squash in fixes for these issues:

> +++ b/scripts/qapi-event.py

> +
> +def generate_event_implement(api_name, event_name, params):
> +    # step 1: declare and variables

s/and/any/


> +
> +    /* Fake visit, as if all member are under a structure */

s/member/members/

See also my comments on 16/29 - what if you change the generator to use
&error_abort instead of futzing around with local_error?  After all, the
qmp_output_visitor doesn't ever set errp; and all of your callers
silently ignore errp even if it were to be set.  Which makes sense,
since events are best-effort anyways (it's not like you are building up
a reply to a synchronous command, so you really don't have anyone to
inform about the failed event short of sending yet another event - but
if the first event failed, then your attempt to send another event to
inform about the failure is probably doomed).
Eric Blake June 18, 2014, 3:33 a.m. UTC | #3
On 06/05/2014 06:21 AM, 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 includes two parts: generate API for each event
> define, generate an enum type for all defined events.
> 
> Since in some cases 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.
> 

> +++ b/scripts/qapi-event.py
> @@ -0,0 +1,369 @@
> +#
> +# QAPI event generator
> +#
> +# Copyright (c) 2014 Wenchao Xia
> +#
> +# 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.

Any reason this can't be GPLv2+ instead of GPLv2-only?
Eric Blake June 18, 2014, 3:50 a.m. UTC | #4
On 06/05/2014 06:21 AM, 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 includes two parts: generate API for each event
> define, generate an enum type for all defined events.
> 

> +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 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))

This may need to be rebased or have a followup patch based on Amos' work:
https://lists.gnu.org/archive/html/qemu-devel/2014-06/msg02387.html
Paolo Bonzini June 18, 2014, 6:06 a.m. UTC | #5
Il 18/06/2014 05:33, Eric Blake ha scritto:
>> > +# This work is licensed under the terms of the GNU GPL, version 2.
>> > +# See the COPYING file in the top-level directory.
> Any reason this can't be GPLv2+ instead of GPLv2-only?

I suppose because it copies parts of other qapi-* scripts. :(

Paolo
Wenchao Xia June 18, 2014, 10:45 p.m. UTC | #6
于 2014/6/18 14:06, Paolo Bonzini 写道:
> Il 18/06/2014 05:33, Eric Blake ha scritto:
>>> > +# This work is licensed under the terms of the GNU GPL, version 2.
>>> > +# See the COPYING file in the top-level directory.
>> Any reason this can't be GPLv2+ instead of GPLv2-only?
>
> I suppose because it copies parts of other qapi-* scripts. :(
>
> Paolo

   Yes, feel free to change the license.
diff mbox

Patch

diff --git a/Makefile b/Makefile
index d830483..237657e 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
@@ -202,7 +202,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)'
@@ -256,6 +256,11 @@  $(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 -i $<, \
 		"  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 -i $<, \
+		"  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 \
diff --git a/Makefile.objs b/Makefile.objs
index b897e1d..1f76cea 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 dea0d50..c3d315f 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -215,6 +215,24 @@  An example command is:
    'data': { 'arg1': 'str', '*arg2': 'str' },
    'returns': 'str' }
 
+=== Events ===
+
+Events are defined with key word '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, a message with timestamp will
+be emitted on the wire.  If timestamp is -1, it means failure to retrieve host
+time.
+
+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..e1dcc43
--- /dev/null
+++ b/scripts/qapi-event.py
@@ -0,0 +1,369 @@ 
+#
+# QAPI event generator
+#
+# Copyright (c) 2014 Wenchao Xia
+#
+# 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 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 (local_err) {
+        goto clean;
+    }
+
+""",
+                event_name = event_name)
+
+        for argname, argentry, optional, structured in parse_args(params):
+            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 (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 (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 to qapi-types.py. Here we already have enum name and
+# values which were generated before and recorded in event_enum_*. It also
+# works 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:i:o:",
+                                   ["source", "header", "builtins", "prefix=",
+                                    "input-file=", "output-dir="])
+except getopt.GetoptError, err:
+    print str(err)
+    sys.exit(1)
+
+input_file = ""
+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 ("-i", "--input-file"):
+        input_file = 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
+ *
+ * Copyright (c) 2014 Wenchao Xia
+ *
+ * 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 functions
+ *
+ * Copyright (c) 2014 Wenchao Xia
+ *
+ * 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(input_file)
+
+event_enum_name = prefix.upper().replace('-', '_') + "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()
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 86e9608..9b488f2 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -248,6 +248,16 @@  def discriminator_find_enum_define(expr):
 
     return find_enum(discriminator_type)
 
+def check_event(expr, expr_info):
+    params = expr.get('data')
+    if params:
+        for argname, argentry, optional, structured in parse_args(params):
+            if structured:
+                raise QAPIExprError(expr_info,
+                                    "Nested structure define in event is not "
+                                    "supported now, event '%s', argname '%s'"
+                                    % (expr['event'], argname))
+
 def check_union(expr, expr_info):
     name = expr['union']
     base = expr.get('base')
@@ -311,6 +321,8 @@  def check_exprs(schema):
         expr = expr_elem['expr']
         if expr.has_key('union'):
             check_union(expr, expr_elem['info'])
+        if expr.has_key('event'):
+            check_event(expr, expr_elem['info'])
 
 def parse_schema(input_file):
     try:
diff --git a/tests/Makefile b/tests/Makefile
index 6b294a7..287455f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -198,7 +198,7 @@  check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
         include-simple.json include-relpath.json include-format-err.json \
         include-non-file.json include-no-file.json include-before-err.json \
         include-nested-err.json include-self-cycle.json include-cycle.json \
-        include-repetition.json)
+        include-repetition.json event-nest-struct.json)
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h
 
diff --git a/tests/qapi-schema/event-nest-struct.err b/tests/qapi-schema/event-nest-struct.err
new file mode 100644
index 0000000..e4a0faa
--- /dev/null
+++ b/tests/qapi-schema/event-nest-struct.err
@@ -0,0 +1 @@ 
+tests/qapi-schema/event-nest-struct.json:1: Nested structure define in event is not supported now, event 'EVENT_A', argname 'a'
diff --git a/tests/qapi-schema/event-nest-struct.exit b/tests/qapi-schema/event-nest-struct.exit
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/tests/qapi-schema/event-nest-struct.exit
@@ -0,0 +1 @@ 
+1
diff --git a/tests/qapi-schema/event-nest-struct.json b/tests/qapi-schema/event-nest-struct.json
new file mode 100644
index 0000000..ee6f3ec
--- /dev/null
+++ b/tests/qapi-schema/event-nest-struct.json
@@ -0,0 +1,2 @@ 
+{ 'event': 'EVENT_A',
+  'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } }