qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

commands.py (11325B)


      1 """
      2 QAPI command marshaller generator
      3 
      4 Copyright IBM, Corp. 2011
      5 Copyright (C) 2014-2018 Red Hat, Inc.
      6 
      7 Authors:
      8  Anthony Liguori <aliguori@us.ibm.com>
      9  Michael Roth <mdroth@linux.vnet.ibm.com>
     10  Markus Armbruster <armbru@redhat.com>
     11 
     12 This work is licensed under the terms of the GNU GPL, version 2.
     13 See the COPYING file in the top-level directory.
     14 """
     15 
     16 from typing import (
     17     Dict,
     18     List,
     19     Optional,
     20     Set,
     21 )
     22 
     23 from .common import c_name, mcgen
     24 from .gen import (
     25     QAPIGenC,
     26     QAPISchemaModularCVisitor,
     27     build_params,
     28     gen_special_features,
     29     ifcontext,
     30 )
     31 from .schema import (
     32     QAPISchema,
     33     QAPISchemaFeature,
     34     QAPISchemaIfCond,
     35     QAPISchemaObjectType,
     36     QAPISchemaType,
     37 )
     38 from .source import QAPISourceInfo
     39 
     40 
     41 def gen_command_decl(name: str,
     42                      arg_type: Optional[QAPISchemaObjectType],
     43                      boxed: bool,
     44                      ret_type: Optional[QAPISchemaType]) -> str:
     45     return mcgen('''
     46 %(c_type)s qmp_%(c_name)s(%(params)s);
     47 ''',
     48                  c_type=(ret_type and ret_type.c_type()) or 'void',
     49                  c_name=c_name(name),
     50                  params=build_params(arg_type, boxed, 'Error **errp'))
     51 
     52 
     53 def gen_call(name: str,
     54              arg_type: Optional[QAPISchemaObjectType],
     55              boxed: bool,
     56              ret_type: Optional[QAPISchemaType],
     57              gen_tracing: bool) -> str:
     58     ret = ''
     59 
     60     argstr = ''
     61     if boxed:
     62         assert arg_type
     63         argstr = '&arg, '
     64     elif arg_type:
     65         assert not arg_type.variants
     66         for memb in arg_type.members:
     67             if memb.optional:
     68                 argstr += 'arg.has_%s, ' % c_name(memb.name)
     69             argstr += 'arg.%s, ' % c_name(memb.name)
     70 
     71     lhs = ''
     72     if ret_type:
     73         lhs = 'retval = '
     74 
     75     name = c_name(name)
     76     upper = name.upper()
     77 
     78     if gen_tracing:
     79         ret += mcgen('''
     80 
     81     if (trace_event_get_state_backends(TRACE_QMP_ENTER_%(upper)s)) {
     82         g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args));
     83 
     84         trace_qmp_enter_%(name)s(req_json->str);
     85     }
     86     ''',
     87                      upper=upper, name=name)
     88 
     89     ret += mcgen('''
     90 
     91     %(lhs)sqmp_%(name)s(%(args)s&err);
     92 ''',
     93                  name=name, args=argstr, lhs=lhs)
     94 
     95     ret += mcgen('''
     96     if (err) {
     97 ''')
     98 
     99     if gen_tracing:
    100         ret += mcgen('''
    101         trace_qmp_exit_%(name)s(error_get_pretty(err), false);
    102 ''',
    103                      name=name)
    104 
    105     ret += mcgen('''
    106         error_propagate(errp, err);
    107         goto out;
    108     }
    109 ''')
    110 
    111     if ret_type:
    112         ret += mcgen('''
    113 
    114     qmp_marshal_output_%(c_name)s(retval, ret, errp);
    115 ''',
    116                      c_name=ret_type.c_name())
    117 
    118     if gen_tracing:
    119         if ret_type:
    120             ret += mcgen('''
    121 
    122     if (trace_event_get_state_backends(TRACE_QMP_EXIT_%(upper)s)) {
    123         g_autoptr(GString) ret_json = qobject_to_json(*ret);
    124 
    125         trace_qmp_exit_%(name)s(ret_json->str, true);
    126     }
    127     ''',
    128                          upper=upper, name=name)
    129         else:
    130             ret += mcgen('''
    131 
    132     trace_qmp_exit_%(name)s("{}", true);
    133     ''',
    134                          name=name)
    135 
    136     return ret
    137 
    138 
    139 def gen_marshal_output(ret_type: QAPISchemaType) -> str:
    140     return mcgen('''
    141 
    142 static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in,
    143                                 QObject **ret_out, Error **errp)
    144 {
    145     Visitor *v;
    146 
    147     v = qobject_output_visitor_new_qmp(ret_out);
    148     if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) {
    149         visit_complete(v, ret_out);
    150     }
    151     visit_free(v);
    152     v = qapi_dealloc_visitor_new();
    153     visit_type_%(c_name)s(v, "unused", &ret_in, NULL);
    154     visit_free(v);
    155 }
    156 ''',
    157                  c_type=ret_type.c_type(), c_name=ret_type.c_name())
    158 
    159 
    160 def build_marshal_proto(name: str) -> str:
    161     return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)'
    162             % c_name(name))
    163 
    164 
    165 def gen_marshal_decl(name: str) -> str:
    166     return mcgen('''
    167 %(proto)s;
    168 ''',
    169                  proto=build_marshal_proto(name))
    170 
    171 
    172 def gen_trace(name: str) -> str:
    173     return mcgen('''
    174 qmp_enter_%(name)s(const char *json) "%%s"
    175 qmp_exit_%(name)s(const char *result, bool succeeded) "%%s %%d"
    176 ''',
    177                  name=c_name(name))
    178 
    179 
    180 def gen_marshal(name: str,
    181                 arg_type: Optional[QAPISchemaObjectType],
    182                 boxed: bool,
    183                 ret_type: Optional[QAPISchemaType],
    184                 gen_tracing: bool) -> str:
    185     have_args = boxed or (arg_type and not arg_type.is_empty())
    186     if have_args:
    187         assert arg_type is not None
    188         arg_type_c_name = arg_type.c_name()
    189 
    190     ret = mcgen('''
    191 
    192 %(proto)s
    193 {
    194     Error *err = NULL;
    195     bool ok = false;
    196     Visitor *v;
    197 ''',
    198                 proto=build_marshal_proto(name))
    199 
    200     if ret_type:
    201         ret += mcgen('''
    202     %(c_type)s retval;
    203 ''',
    204                      c_type=ret_type.c_type())
    205 
    206     if have_args:
    207         ret += mcgen('''
    208     %(c_name)s arg = {0};
    209 ''',
    210                      c_name=arg_type_c_name)
    211 
    212     ret += mcgen('''
    213 
    214     v = qobject_input_visitor_new_qmp(QOBJECT(args));
    215     if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
    216         goto out;
    217     }
    218 ''')
    219 
    220     if have_args:
    221         ret += mcgen('''
    222     if (visit_type_%(c_arg_type)s_members(v, &arg, errp)) {
    223         ok = visit_check_struct(v, errp);
    224     }
    225 ''',
    226                      c_arg_type=arg_type_c_name)
    227     else:
    228         ret += mcgen('''
    229     ok = visit_check_struct(v, errp);
    230 ''')
    231 
    232     ret += mcgen('''
    233     visit_end_struct(v, NULL);
    234     if (!ok) {
    235         goto out;
    236     }
    237 ''')
    238 
    239     ret += gen_call(name, arg_type, boxed, ret_type, gen_tracing)
    240 
    241     ret += mcgen('''
    242 
    243 out:
    244     visit_free(v);
    245 ''')
    246 
    247     ret += mcgen('''
    248     v = qapi_dealloc_visitor_new();
    249     visit_start_struct(v, NULL, NULL, 0, NULL);
    250 ''')
    251 
    252     if have_args:
    253         ret += mcgen('''
    254     visit_type_%(c_arg_type)s_members(v, &arg, NULL);
    255 ''',
    256                      c_arg_type=arg_type_c_name)
    257 
    258     ret += mcgen('''
    259     visit_end_struct(v, NULL);
    260     visit_free(v);
    261 ''')
    262 
    263     ret += mcgen('''
    264 }
    265 ''')
    266     return ret
    267 
    268 
    269 def gen_register_command(name: str,
    270                          features: List[QAPISchemaFeature],
    271                          success_response: bool,
    272                          allow_oob: bool,
    273                          allow_preconfig: bool,
    274                          coroutine: bool) -> str:
    275     options = []
    276 
    277     if not success_response:
    278         options += ['QCO_NO_SUCCESS_RESP']
    279     if allow_oob:
    280         options += ['QCO_ALLOW_OOB']
    281     if allow_preconfig:
    282         options += ['QCO_ALLOW_PRECONFIG']
    283     if coroutine:
    284         options += ['QCO_COROUTINE']
    285 
    286     ret = mcgen('''
    287     qmp_register_command(cmds, "%(name)s",
    288                          qmp_marshal_%(c_name)s, %(opts)s, %(feats)s);
    289 ''',
    290                 name=name, c_name=c_name(name),
    291                 opts=' | '.join(options) or 0,
    292                 feats=gen_special_features(features))
    293     return ret
    294 
    295 
    296 class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
    297     def __init__(self, prefix: str, gen_tracing: bool):
    298         super().__init__(
    299             prefix, 'qapi-commands',
    300             ' * Schema-defined QAPI/QMP commands', None, __doc__,
    301             gen_tracing=gen_tracing)
    302         self._visited_ret_types: Dict[QAPIGenC, Set[QAPISchemaType]] = {}
    303         self._gen_tracing = gen_tracing
    304 
    305     def _begin_user_module(self, name: str) -> None:
    306         self._visited_ret_types[self._genc] = set()
    307         commands = self._module_basename('qapi-commands', name)
    308         types = self._module_basename('qapi-types', name)
    309         visit = self._module_basename('qapi-visit', name)
    310         self._genc.add(mcgen('''
    311 #include "qemu/osdep.h"
    312 #include "qapi/compat-policy.h"
    313 #include "qapi/visitor.h"
    314 #include "qapi/qmp/qdict.h"
    315 #include "qapi/dealloc-visitor.h"
    316 #include "qapi/error.h"
    317 #include "%(visit)s.h"
    318 #include "%(commands)s.h"
    319 
    320 ''',
    321                              commands=commands, visit=visit))
    322 
    323         if self._gen_tracing and commands != 'qapi-commands':
    324             self._genc.add(mcgen('''
    325 #include "qapi/qmp/qjson.h"
    326 #include "trace/trace-%(nm)s_trace_events.h"
    327 ''',
    328                                  nm=c_name(commands, protect=False)))
    329             # We use c_name(commands, protect=False) to turn '-' into '_', to
    330             # match .underscorify() in trace/meson.build
    331 
    332         self._genh.add(mcgen('''
    333 #include "%(types)s.h"
    334 
    335 ''',
    336                              types=types))
    337 
    338     def visit_begin(self, schema: QAPISchema) -> None:
    339         self._add_module('./init', ' * QAPI Commands initialization')
    340         self._genh.add(mcgen('''
    341 #include "qapi/qmp/dispatch.h"
    342 
    343 void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
    344 ''',
    345                              c_prefix=c_name(self._prefix, protect=False)))
    346         self._genc.add(mcgen('''
    347 #include "qemu/osdep.h"
    348 #include "%(prefix)sqapi-commands.h"
    349 #include "%(prefix)sqapi-init-commands.h"
    350 
    351 void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
    352 {
    353     QTAILQ_INIT(cmds);
    354 
    355 ''',
    356                              prefix=self._prefix,
    357                              c_prefix=c_name(self._prefix, protect=False)))
    358 
    359     def visit_end(self) -> None:
    360         with self._temp_module('./init'):
    361             self._genc.add(mcgen('''
    362 }
    363 '''))
    364 
    365     def visit_command(self,
    366                       name: str,
    367                       info: Optional[QAPISourceInfo],
    368                       ifcond: QAPISchemaIfCond,
    369                       features: List[QAPISchemaFeature],
    370                       arg_type: Optional[QAPISchemaObjectType],
    371                       ret_type: Optional[QAPISchemaType],
    372                       gen: bool,
    373                       success_response: bool,
    374                       boxed: bool,
    375                       allow_oob: bool,
    376                       allow_preconfig: bool,
    377                       coroutine: bool) -> None:
    378         if not gen:
    379             return
    380         # FIXME: If T is a user-defined type, the user is responsible
    381         # for making this work, i.e. to make T's condition the
    382         # conjunction of the T-returning commands' conditions.  If T
    383         # is a built-in type, this isn't possible: the
    384         # qmp_marshal_output_T() will be generated unconditionally.
    385         if ret_type and ret_type not in self._visited_ret_types[self._genc]:
    386             self._visited_ret_types[self._genc].add(ret_type)
    387             with ifcontext(ret_type.ifcond,
    388                            self._genh, self._genc):
    389                 self._genc.add(gen_marshal_output(ret_type))
    390         with ifcontext(ifcond, self._genh, self._genc):
    391             self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
    392             self._genh.add(gen_marshal_decl(name))
    393             self._genc.add(gen_marshal(name, arg_type, boxed, ret_type,
    394                                        self._gen_tracing))
    395             if self._gen_tracing:
    396                 self._gen_trace_events.add(gen_trace(name))
    397         with self._temp_module('./init'):
    398             with ifcontext(ifcond, self._genh, self._genc):
    399                 self._genc.add(gen_register_command(
    400                     name, features, success_response, allow_oob,
    401                     allow_preconfig, coroutine))
    402 
    403 
    404 def gen_commands(schema: QAPISchema,
    405                  output_dir: str,
    406                  prefix: str,
    407                  gen_tracing: bool) -> None:
    408     vis = QAPISchemaGenCommandVisitor(prefix, gen_tracing)
    409     schema.visit(vis)
    410     vis.write(output_dir)