qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Luiz Capitulino <lcapitulino@redhat.com>
To: Luiz Capitulino <lcapitulino@redhat.com>
Cc: armbru@redhat.com, mreitz@redhat.com,
	Wenchao Xia <xiawenc@linux.vnet.ibm.com>,
	qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [RFC PATCH V2 3/5] qapi script: add event support by qapi-event.py
Date: Mon, 6 Jan 2014 18:17:00 -0500	[thread overview]
Message-ID: <20140106181700.3215f8ae@redhat.com> (raw)
In-Reply-To: <20140106181004.192b4472@redhat.com>


[Pressed enter too soon, forgot two things]

On Mon, 6 Jan 2014 18:10:04 -0500
Luiz Capitulino <lcapitulino@redhat.com> wrote:

> On Fri,  3 Jan 2014 07:10:32 +0800
> Wenchao Xia <xiawenc@linux.vnet.ibm.com> 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", all types have prefix "QAPIEvent".
> > Examples can be found in following patches.
> > 
> > The script mainly include three parts: generate API for each event
> > define, generate an enum type for all defined event, generate behavior
> > control functions.
> > 
> > Since in some case the real emit behavior may change, for example,
> > qemu-img would not send a event, a callback layer is added 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 <xiawenc@linux.vnet.ibm.com>
> > ---
> >  Makefile              |    9 +-
> >  Makefile.objs         |    2 +-
> >  scripts/qapi-event.py |  432 +++++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 439 insertions(+), 4 deletions(-)
> >  create mode 100644 scripts/qapi-event.py
> > 
> > diff --git a/Makefile b/Makefile
> > index bdff4e4..fa59765 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
> > @@ -185,7 +185,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
> >  
> >  ######################################################################
> >  
> > @@ -226,6 +226,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 2b6c1fe..33f5950 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/scripts/qapi-event.py b/scripts/qapi-event.py
> > new file mode 100644
> > index 0000000..7526366
> > --- /dev/null
> > +++ b/scripts/qapi-event.py
> > @@ -0,0 +1,432 @@
> > +#
> > +# QAPI event generator
> > +#
> > +# Copyright IBM, Corp. 2014
> > +#
> > +# Authors:
> > +#  Wenchao Xia <xiawenc@linux.vnet.ibm.com>
> > +#
> > +# This work is licensed under the terms of the GNU GPLv2+ or later.
> > +# See the COPYING.LIB 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):
> 
> Why the underline? And, what you generate is a function declaration...
> 
> > +    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 transate user input into a qdict going
> 
> s/transate/translate
> 
> Although the comment doesn't make much sense to me.
> 
> > +# to be emitted in the wire.
> > +
> > +def generate_event_declaration(api_name):
> > +    return mcgen('''
> > +
> > +%(api_name)s;
> > +''',
> > +                 api_name = api_name)
> > +
> > +def generate_event_implement(api_name, event_name, params):
> 
> I wonder if it would be clearer to to generate the declaration here.
> 
> > +    # step 1: declare and variables
> > +    ret = mcgen("""
> > +
> > +%(api_name)s
> > +{
> > +    QDict *qmp;
> > +    Error *local_err = NULL;
> > +    QAPIEventFuncEmit 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 = qapi_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_string(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
> > +
> > +
> > +# Following are the functions that generate event behavior control functions.
> > +# Those functions are put here in the qapi-event.c, since it need to include
> > +# qapi-event.h for the event enum type declaration, put them in other file
> > +# requiring other file include qapi-event.h, causing a cross including. For
> > +# example: if we have qmp-event.c and qmp-event.h, then qmp-event.c
> > +# ->qmp-event.h->qapi-event.h, qapi-event.c->qmp-event.h. Another problem
> > +# follow: test-qapi-event.c will meet event enum double declaration since it
> > +# include both test-qapi-event.h and qmp-event.h. One solution is putting event
> > +# enum declaration in a separate header file, but then qmp-event.h need to
> > +# include test-qapi-event.h or qapi-event.h on compile time condition. So the
> > +# easist way is, just generate them here.
> > +
> > +def generate_event_behavior_control_decl(event_enum_name):
> > +    ret = mcgen('''
> > +
> > +typedef void (*QAPIEventFuncEmit)(%(event_enum_name)s ev,
> > +                                  QDict *dict,
> > +                                  Error **errp);

Why does the emit function need 'ev'? Doesn't 'dict' contain all the
info it needs? Also, it's better to rename it to 'event' or 'qmp_event'.

> > +
> > +void qapi_event_set_func_emit(QAPIEventFuncEmit emit);
> > +
> > +QAPIEventFuncEmit qapi_event_get_func_emit(void);
> > +''',
> > +                event_enum_name = event_enum_name)
> > +    return ret;
> > +
> > +def generate_event_behavior_control_implement():
> > +    ret = mcgen('''
> > +
> > +typedef struct QAPIEventFunctions {
> > +    QAPIEventFuncEmit emit;
> > +} QAPIEventFunctions;
> > +
> > +QAPIEventFunctions qapi_event_functions;
> > +
> > +void qapi_event_set_func_emit(QAPIEventFuncEmit emit)
> > +{
> > +    qapi_event_functions.emit = emit;
> > +}

If this function and the typedefs don't change, I think they shouldn't
be generated.

> > +
> > +QAPIEventFuncEmit qapi_event_get_func_emit(void)
> > +{
> > +    return qapi_event_functions.emit;
> > +}
> > +''')
> > +    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
> > + *
> > + * Copyright IBM, Corp. 2014
> > + *
> > + * Authors:
> > + *  Wenchao Xia   <xiawenc@linux.vnet.ibm.com>
> > + *
> > + * This work is licensed under the terms of the GNU GPLv2+ 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
> > + *
> > + * Copyright IBM, Corp. 2014
> > + *
> > + * Authors:
> > + *  Wenchao Xia  <xiawenc@linux.vnet.ibm.com>
> > + *
> > + * This work is licensed under the terms of the GNU GPLv2+ 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_string(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)
> > +ret = generate_event_behavior_control_decl(event_enum_name)
> > +fdecl.write(ret)
> > +ret = generate_event_behavior_control_implement()
> > +fdef.write(ret)
> > +
> > +fdecl.write('''
> > +#endif
> > +''')
> > +
> > +fdecl.flush()
> > +fdecl.close()
> > +
> > +fdef.flush()
> > +fdef.close()
> 

  reply	other threads:[~2014-01-06 23:17 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-01-02 23:10 [Qemu-devel] [RFC PATCH V2 0/5] add direct support of event in qapi schema Wenchao Xia
2014-01-02 23:10 ` [Qemu-devel] [RFC PATCH V2 1/5] os-posix: include sys/time.h Wenchao Xia
2014-01-02 23:10 ` [Qemu-devel] [RFC PATCH V2 2/5] qapi: add event helper functions Wenchao Xia
2014-01-06 22:23   ` Luiz Capitulino
2014-01-07  2:28     ` Wenchao Xia
2014-03-06 18:26     ` Eric Blake
2014-01-02 23:10 ` [Qemu-devel] [RFC PATCH V2 3/5] qapi script: add event support by qapi-event.py Wenchao Xia
2014-01-06 23:10   ` Luiz Capitulino
2014-01-06 23:17     ` Luiz Capitulino [this message]
2014-01-07  3:24       ` Wenchao Xia
2014-02-14  3:26         ` Wenchao Xia
2014-01-07  2:53     ` Wenchao Xia
2014-03-06 18:49   ` Eric Blake
2014-03-19  2:38     ` Wenchao Xia
2014-03-20 22:29       ` Eric Blake
2014-03-24  0:55         ` Wenchao Xia
2014-03-26 12:42           ` Markus Armbruster
2014-03-26 13:13             ` Benoît Canet
2014-03-27  7:52               ` Wenchao Xia
2014-01-02 23:10 ` [Qemu-devel] [RFC PATCH V2 4/5] test: add test cases for qapi event Wenchao Xia
2014-03-06 20:05   ` Eric Blake
2014-01-02 23:10 ` [Qemu-devel] [RFC PATCH V2 5/5] qapi event: convert RTC_CHANGE Wenchao Xia
2014-03-06 20:24   ` Eric Blake
2014-01-06 23:18 ` [Qemu-devel] [RFC PATCH V2 0/5] add direct support of event in qapi schema Luiz Capitulino
2014-03-06 18:14   ` Eric Blake
2014-03-06 19:58     ` Luiz Capitulino
2014-03-07  1:13       ` Wenchao Xia

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140106181700.3215f8ae@redhat.com \
    --to=lcapitulino@redhat.com \
    --cc=armbru@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=xiawenc@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).