diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2022-01-27 15:45:13 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2022-01-27 15:45:13 +0000 |
commit | cfe63e46be0a1f8a7fd2fd5547222f8344a43279 (patch) | |
tree | 5f7929c565eab262b33aba9b1685ac7450452760 | |
parent | 48302d4eb628ff0bea4d7e92cbf6b726410eb4c3 (diff) | |
parent | 761a1a488e67a77858f3645a43fbdfe6208b51ce (diff) |
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2022-01-27-v2' into staging
QAPI patches patches for 2022-01-27
# gpg: Signature made Thu 27 Jan 2022 14:19:51 GMT
# gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg: issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653
* remotes/armbru/tags/pull-qapi-2022-01-27-v2:
qapi: generate trace events by default
meson: document why we don't generate trace events for tests/ and qga/
docs/qapi-code-gen: update to cover trace events code generation
meson: generate trace events for qmp commands
qapi/commands: Optionally generate trace for QMP commands
qapi/commands: refactor error handling code
qapi/gen: Add FOO.trace-events output module
schemas: add missing vim modeline
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r-- | docs/devel/qapi-code-gen.rst | 25 | ||||
-rw-r--r-- | docs/devel/tracing.rst | 2 | ||||
-rw-r--r-- | meson.build | 3 | ||||
-rw-r--r-- | qapi/audio.json | 1 | ||||
-rw-r--r-- | qapi/compat.json | 1 | ||||
-rw-r--r-- | qapi/meson.build | 7 | ||||
-rw-r--r-- | qapi/replay.json | 1 | ||||
-rw-r--r-- | qapi/trace.json | 1 | ||||
-rw-r--r-- | qga/meson.build | 10 | ||||
-rw-r--r-- | scripts/qapi/commands.py | 100 | ||||
-rw-r--r-- | scripts/qapi/gen.py | 31 | ||||
-rw-r--r-- | scripts/qapi/main.py | 14 | ||||
-rw-r--r-- | tests/meson.build | 10 | ||||
-rw-r--r-- | trace/meson.build | 11 |
14 files changed, 190 insertions, 27 deletions
diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index a3b5473089..246709ede8 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -1630,6 +1630,9 @@ The following files are generated: ``$(prefix)qapi-commands.h`` Function prototypes for the QMP commands specified in the schema + ``$(prefix)qapi-commands.trace-events`` + Trace event declarations, see :ref:`tracing`. + ``$(prefix)qapi-init-commands.h`` Command initialization prototype @@ -1650,6 +1653,13 @@ Example:: void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp); #endif /* EXAMPLE_QAPI_COMMANDS_H */ + + $ cat qapi-generated/example-qapi-commands.trace-events + # AUTOMATICALLY GENERATED, DO NOT MODIFY + + qmp_enter_my_command(const char *json) "%s" + qmp_exit_my_command(const char *result, bool succeeded) "%s %d" + $ cat qapi-generated/example-qapi-commands.c [Uninteresting stuff omitted...] @@ -1689,14 +1699,27 @@ Example:: goto out; } + if (trace_event_get_state_backends(TRACE_QMP_ENTER_MY_COMMAND)) { + g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args)); + + trace_qmp_enter_my_command(req_json->str); + } + retval = qmp_my_command(arg.arg1, &err); - error_propagate(errp, err); if (err) { + trace_qmp_exit_my_command(error_get_pretty(err), false); + error_propagate(errp, err); goto out; } qmp_marshal_output_UserDefOne(retval, ret, errp); + if (trace_event_get_state_backends(TRACE_QMP_EXIT_MY_COMMAND)) { + g_autoptr(GString) ret_json = qobject_to_json(*ret); + + trace_qmp_exit_my_command(ret_json->str, true); + } + out: visit_free(v); v = qapi_dealloc_visitor_new(); diff --git a/docs/devel/tracing.rst b/docs/devel/tracing.rst index ba83954899..4290ac42ee 100644 --- a/docs/devel/tracing.rst +++ b/docs/devel/tracing.rst @@ -1,3 +1,5 @@ +.. _tracing: + ======= Tracing ======= diff --git a/meson.build b/meson.build index 833fd6bc4c..e0cfafe8d9 100644 --- a/meson.build +++ b/meson.build @@ -41,6 +41,7 @@ qemu_icondir = get_option('datadir') / 'icons' config_host_data = configuration_data() genh = [] +qapi_trace_events = [] target_dirs = config_host['TARGET_DIRS'].split() have_linux_user = false @@ -2557,6 +2558,8 @@ if 'CONFIG_VHOST_USER' in config_host vhost_user = libvhost_user.get_variable('vhost_user_dep') endif +# NOTE: the trace/ subdirectory needs the qapi_trace_events variable +# that is filled in by qapi/. subdir('qapi') subdir('qobject') subdir('stubs') diff --git a/qapi/audio.json b/qapi/audio.json index 693e327c6b..0785e70a50 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -1,4 +1,5 @@ # -*- mode: python -*- +# vim: filetype=python # # Copyright (C) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com> # diff --git a/qapi/compat.json b/qapi/compat.json index dd7261ae2a..c53b69fe3f 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -1,4 +1,5 @@ # -*- Mode: Python -*- +# vim: filetype=python ## # = Compatibility policy diff --git a/qapi/meson.build b/qapi/meson.build index c0c49c15e4..656ef0e039 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -114,6 +114,7 @@ foreach module : qapi_all_modules 'qapi-events-@0@.h'.format(module), 'qapi-commands-@0@.c'.format(module), 'qapi-commands-@0@.h'.format(module), + 'qapi-commands-@0@.trace-events'.format(module), ] endif if module.endswith('-target') @@ -137,6 +138,9 @@ foreach output : qapi_util_outputs if output.endswith('.h') genh += qapi_files[i] endif + if output.endswith('.trace-events') + qapi_trace_events += qapi_files[i] + endif util_ss.add(qapi_files[i]) i = i + 1 endforeach @@ -145,6 +149,9 @@ foreach output : qapi_specific_outputs + qapi_nonmodule_outputs if output.endswith('.h') genh += qapi_files[i] endif + if output.endswith('.trace-events') + qapi_trace_events += qapi_files[i] + endif specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: qapi_files[i]) i = i + 1 endforeach diff --git a/qapi/replay.json b/qapi/replay.json index bfd83d7591..b4d1ba253b 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -1,4 +1,5 @@ # -*- Mode: Python -*- +# vim: filetype=python # ## diff --git a/qapi/trace.json b/qapi/trace.json index eedfded512..119509f565 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -1,4 +1,5 @@ # -*- mode: python -*- +# vim: filetype=python # # Copyright (C) 2011-2016 Lluís Vilanova <vilanova@ac.upc.edu> # diff --git a/qga/meson.build b/qga/meson.build index cfb1fbc085..f06b726ad3 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -15,10 +15,18 @@ qga_qapi_outputs = [ 'qga-qapi-visit.h', ] +# Problem: to generate trace events, we'd have to add the .trace-events +# file to qapi_trace_events like we do in qapi/meson.build. Since +# qapi_trace_events is used by trace/meson.build, we'd have to move +# subdir('qga') above subdir('trace') in the top-level meson.build. +# Can't, because it would break the dependency of qga on qemuutil (which +# depends on trace_ss). Not worth solving now; simply suppress trace +# event generation instead. qga_qapi_files = custom_target('QGA QAPI files', output: qga_qapi_outputs, input: 'qapi-schema.json', - command: [ qapi_gen, '-o', 'qga', '-p', 'qga-', '@INPUT0@' ], + command: [ qapi_gen, '-o', 'qga', '-p', 'qga-', '@INPUT0@', + '--suppress-tracing' ], depend_files: qapi_gen_depends) qga_ss = ss.source_set() diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 21001bbd6b..869d799ed2 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -53,7 +53,8 @@ def gen_command_decl(name: str, def gen_call(name: str, arg_type: Optional[QAPISchemaObjectType], boxed: bool, - ret_type: Optional[QAPISchemaType]) -> str: + ret_type: Optional[QAPISchemaType], + gen_tracing: bool) -> str: ret = '' argstr = '' @@ -71,21 +72,67 @@ def gen_call(name: str, if ret_type: lhs = 'retval = ' - ret = mcgen(''' + name = c_name(name) + upper = name.upper() - %(lhs)sqmp_%(c_name)s(%(args)s&err); - error_propagate(errp, err); -''', - c_name=c_name(name), args=argstr, lhs=lhs) - if ret_type: + if gen_tracing: ret += mcgen(''' + + if (trace_event_get_state_backends(TRACE_QMP_ENTER_%(upper)s)) { + g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args)); + + trace_qmp_enter_%(name)s(req_json->str); + } + ''', + upper=upper, name=name) + + ret += mcgen(''' + + %(lhs)sqmp_%(name)s(%(args)s&err); +''', + name=name, args=argstr, lhs=lhs) + + ret += mcgen(''' if (err) { +''') + + if gen_tracing: + ret += mcgen(''' + trace_qmp_exit_%(name)s(error_get_pretty(err), false); +''', + name=name) + + ret += mcgen(''' + error_propagate(errp, err); goto out; } +''') + + if ret_type: + ret += mcgen(''' qmp_marshal_output_%(c_name)s(retval, ret, errp); ''', c_name=ret_type.c_name()) + + if gen_tracing: + if ret_type: + ret += mcgen(''' + + if (trace_event_get_state_backends(TRACE_QMP_EXIT_%(upper)s)) { + g_autoptr(GString) ret_json = qobject_to_json(*ret); + + trace_qmp_exit_%(name)s(ret_json->str, true); + } + ''', + upper=upper, name=name) + else: + ret += mcgen(''' + + trace_qmp_exit_%(name)s("{}", true); + ''', + name=name) + return ret @@ -122,10 +169,19 @@ def gen_marshal_decl(name: str) -> str: proto=build_marshal_proto(name)) +def gen_trace(name: str) -> str: + return mcgen(''' +qmp_enter_%(name)s(const char *json) "%%s" +qmp_exit_%(name)s(const char *result, bool succeeded) "%%s %%d" +''', + name=c_name(name)) + + def gen_marshal(name: str, arg_type: Optional[QAPISchemaObjectType], boxed: bool, - ret_type: Optional[QAPISchemaType]) -> str: + ret_type: Optional[QAPISchemaType], + gen_tracing: bool) -> str: have_args = boxed or (arg_type and not arg_type.is_empty()) if have_args: assert arg_type is not None @@ -180,7 +236,7 @@ def gen_marshal(name: str, } ''') - ret += gen_call(name, arg_type, boxed, ret_type) + ret += gen_call(name, arg_type, boxed, ret_type, gen_tracing) ret += mcgen(''' @@ -238,11 +294,13 @@ def gen_register_command(name: str, class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): - def __init__(self, prefix: str): + def __init__(self, prefix: str, gen_tracing: bool): super().__init__( prefix, 'qapi-commands', - ' * Schema-defined QAPI/QMP commands', None, __doc__) + ' * Schema-defined QAPI/QMP commands', None, __doc__, + gen_tracing=gen_tracing) self._visited_ret_types: Dict[QAPIGenC, Set[QAPISchemaType]] = {} + self._gen_tracing = gen_tracing def _begin_user_module(self, name: str) -> None: self._visited_ret_types[self._genc] = set() @@ -261,6 +319,16 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): ''', commands=commands, visit=visit)) + + if self._gen_tracing and commands != 'qapi-commands': + self._genc.add(mcgen(''' +#include "qapi/qmp/qjson.h" +#include "trace/trace-%(nm)s_trace_events.h" +''', + nm=c_name(commands, protect=False))) + # We use c_name(commands, protect=False) to turn '-' into '_', to + # match .underscorify() in trace/meson.build + self._genh.add(mcgen(''' #include "%(types)s.h" @@ -322,7 +390,10 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) self._genh.add(gen_marshal_decl(name)) - self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) + self._genc.add(gen_marshal(name, arg_type, boxed, ret_type, + self._gen_tracing)) + if self._gen_tracing: + self._gen_trace_events.add(gen_trace(name)) with self._temp_module('./init'): with ifcontext(ifcond, self._genh, self._genc): self._genc.add(gen_register_command( @@ -332,7 +403,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) def gen_commands(schema: QAPISchema, output_dir: str, - prefix: str) -> None: - vis = QAPISchemaGenCommandVisitor(prefix) + prefix: str, + gen_tracing: bool) -> None: + vis = QAPISchemaGenCommandVisitor(prefix, gen_tracing) schema.visit(vis) vis.write(output_dir) diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 995a97d2b8..113b49134d 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -192,6 +192,11 @@ class QAPIGenH(QAPIGenC): return guardend(self.fname) +class QAPIGenTrace(QAPIGen): + def _top(self) -> str: + return super()._top() + '# AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n' + + @contextmanager def ifcontext(ifcond: QAPISchemaIfCond, *args: QAPIGenCCode) -> Iterator[None]: """ @@ -244,15 +249,18 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): what: str, user_blurb: str, builtin_blurb: Optional[str], - pydoc: str): + pydoc: str, + gen_tracing: bool = False): self._prefix = prefix self._what = what self._user_blurb = user_blurb self._builtin_blurb = builtin_blurb self._pydoc = pydoc self._current_module: Optional[str] = None - self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH]] = {} + self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH, + Optional[QAPIGenTrace]]] = {} self._main_module: Optional[str] = None + self._gen_tracing = gen_tracing @property def _genc(self) -> QAPIGenC: @@ -264,6 +272,14 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): assert self._current_module is not None return self._module[self._current_module][1] + @property + def _gen_trace_events(self) -> QAPIGenTrace: + assert self._gen_tracing + assert self._current_module is not None + gent = self._module[self._current_module][2] + assert gent is not None + return gent + @staticmethod def _module_dirname(name: str) -> str: if QAPISchemaModule.is_user_module(name): @@ -293,7 +309,12 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): basename = self._module_filename(self._what, name) genc = QAPIGenC(basename + '.c', blurb, self._pydoc) genh = QAPIGenH(basename + '.h', blurb, self._pydoc) - self._module[name] = (genc, genh) + + gent: Optional[QAPIGenTrace] = None + if self._gen_tracing: + gent = QAPIGenTrace(basename + '.trace-events') + + self._module[name] = (genc, genh, gent) self._current_module = name @contextmanager @@ -304,11 +325,13 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): self._current_module = old_module def write(self, output_dir: str, opt_builtins: bool = False) -> None: - for name, (genc, genh) in self._module.items(): + for name, (genc, genh, gent) in self._module.items(): if QAPISchemaModule.is_builtin_module(name) and not opt_builtins: continue genc.write(output_dir) genh.write(output_dir) + if gent is not None: + gent.write(output_dir) def _begin_builtin_module(self) -> None: pass diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index f2ea6e0ce4..fc216a53d3 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -32,7 +32,8 @@ def generate(schema_file: str, output_dir: str, prefix: str, unmask: bool = False, - builtins: bool = False) -> None: + builtins: bool = False, + gen_tracing: bool = False) -> None: """ Generate C code for the given schema into the target directory. @@ -49,7 +50,7 @@ def generate(schema_file: str, schema = QAPISchema(schema_file) gen_types(schema, output_dir, prefix, builtins) gen_visit(schema, output_dir, prefix, builtins) - gen_commands(schema, output_dir, prefix) + gen_commands(schema, output_dir, prefix, gen_tracing) gen_events(schema, output_dir, prefix) gen_introspect(schema, output_dir, prefix, unmask) @@ -74,6 +75,12 @@ def main() -> int: parser.add_argument('-u', '--unmask-non-abi-names', action='store_true', dest='unmask', help="expose non-ABI names in introspection") + + # Option --suppress-tracing exists so we can avoid solving build system + # problems. TODO Drop it when we no longer need it. + parser.add_argument('--suppress-tracing', action='store_true', + help="suppress adding trace events to qmp marshals") + parser.add_argument('schema', action='store') args = parser.parse_args() @@ -88,7 +95,8 @@ def main() -> int: output_dir=args.output_dir, prefix=args.prefix, unmask=args.unmask, - builtins=args.builtins) + builtins=args.builtins, + gen_tracing=not args.suppress_tracing) except QAPIError as err: print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr) return 1 diff --git a/tests/meson.build b/tests/meson.build index 3f3882748a..94a425d94a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -31,13 +31,21 @@ test_qapi_outputs = [ 'test-qapi-visit.h', ] +# Problem: to generate trace events, we'd have to add the .trace-events +# file to qapi_trace_events like we do in qapi/meson.build. Since +# qapi_trace_events is used by trace/meson.build, we'd have to move +# subdir('tests') above subdir('trace') in the top-level meson.build. +# Can't, because it would break the dependency of qga on qemuutil (which +# depends on trace_ss). Not worth solving now; simply suppress trace +# event generation instead. test_qapi_files = custom_target('Test QAPI files', output: test_qapi_outputs, input: files('qapi-schema/qapi-schema-test.json', 'qapi-schema/include/sub-module.json', 'qapi-schema/sub-sub-module.json'), command: [ qapi_gen, '-o', meson.current_build_dir(), - '-b', '-p', 'test-', '@INPUT0@' ], + '-b', '-p', 'test-', '@INPUT0@', + '--suppress-tracing' ], depend_files: qapi_gen_depends) # meson doesn't like generated output in other directories diff --git a/trace/meson.build b/trace/meson.build index 573dd699c6..c4794a1f2a 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -2,10 +2,15 @@ specific_ss.add(files('control-target.c')) trace_events_files = [] -foreach dir : [ '.' ] + trace_events_subdirs - trace_events_file = meson.project_source_root() / dir / 'trace-events' +foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events + if item in qapi_trace_events + trace_events_file = item + group_name = item.full_path().split('/')[-1].underscorify() + else + trace_events_file = meson.project_source_root() / item / 'trace-events' + group_name = item == '.' ? 'root' : item.underscorify() + endif trace_events_files += [ trace_events_file ] - group_name = dir == '.' ? 'root' : dir.underscorify() group = '--group=' + group_name fmt = '@0@-' + group_name + '.@1@' |