From 4e86df17326d2afaf74622c082d906ed3f96d1d7 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:24 +0100 Subject: qapi/gen: Add FOO.trace-events output module We are going to generate trace events for QMP commands. We should generate both trace_*() function calls and trace-events files listing events for trace generator. So, add an output module FOO.trace-events for each FOO schema module. Since we're going to add trace events only to command marshallers, make the trace-events output optional, so we don't generate so many useless empty files. Currently nobody set add_trace_events to True, so new functionality is disabled. It will be enabled for QAPISchemaGenCommandVisitor in a further commit. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-2-vsementsov@virtuozzo.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/gen.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 995a97d..113b491 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 -- cgit v1.1 From 167d913f34aa469fa30b0c51a04d8d2b9a5f1fa5 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:25 +0100 Subject: qapi/commands: refactor error handling code Move error_propagate() to if (err) and make "if (err)" block mandatory. This is to simplify further commit, which will bring trace events generation for QMP commands. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-3-vsementsov@virtuozzo.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/commands.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 21001bb..17e5ed2 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -74,14 +74,18 @@ def gen_call(name: str, ret = mcgen(''' %(lhs)sqmp_%(c_name)s(%(args)s&err); - error_propagate(errp, err); ''', c_name=c_name(name), args=argstr, lhs=lhs) - if ret_type: - ret += mcgen(''' + + ret += mcgen(''' if (err) { + error_propagate(errp, err); goto out; } +''') + + if ret_type: + ret += mcgen(''' qmp_marshal_output_%(c_name)s(retval, ret, errp); ''', -- cgit v1.1 From bd2017bc41566a94fbdfd8728b6e805ce2a2c124 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:26 +0100 Subject: qapi/commands: Optionally generate trace for QMP commands Add trace generation disabled by default and new option --gen-trace to enable it. The next commit will enable it for qapi/, but not for qga/ and tests/. Making it work for the latter two would involve some Meson hackery to ensure we generate the trace-events files before trace-tool uses them. Since we don't actually support tracing there, we'll bypass that problem. Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-4-vsementsov@virtuozzo.com> Reviewed-by: Markus Armbruster [Superfluous #include dropped] Signed-off-by: Markus Armbruster --- scripts/qapi/commands.py | 90 ++++++++++++++++++++++++++++++++++++++++++------ scripts/qapi/main.py | 14 ++++++-- 2 files changed, 90 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 17e5ed2..869d799 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,14 +72,37 @@ def gen_call(name: str, if ret_type: lhs = 'retval = ' - ret = mcgen(''' + name = c_name(name) + upper = name.upper() + + 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_%(c_name)s(%(args)s&err); + %(lhs)sqmp_%(name)s(%(args)s&err); ''', - c_name=c_name(name), args=argstr, lhs=lhs) + 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; } @@ -90,6 +114,25 @@ def gen_call(name: str, 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 @@ -126,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 @@ -184,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(''' @@ -242,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() @@ -265,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" @@ -326,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( @@ -336,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/main.py b/scripts/qapi/main.py index f2ea6e0..687d408 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 --gen-trace exists so we can avoid solving build system + # problems. TODO Drop it when we no longer need it. + parser.add_argument('--gen-trace', action='store_true', + help="add 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=args.gen_trace) except QAPIError as err: print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr) return 1 -- cgit v1.1 From 761a1a488e67a77858f3645a43fbdfe6208b51ce Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 26 Jan 2022 17:11:30 +0100 Subject: qapi: generate trace events by default We don't generate trace events for tests/ and qga/ because that it is not simple and not necessary. We have corresponding comments in both tests/meson.build and qga/meson.build. Still to not miss possible future qapi code generation call, and not to forget to enable trace events generation, let's enable it by default. So, turn option --gen-trace into opposite --no-trace-events and use new option only in tests/ and qga/ where we already have good comments why we don't generate trace events code. Note that this commit enables trace-events generation for qapi-gen.py call from tests/qapi-schema/meson.build and storage-daemon/meson.build. Still, both are kind of noop: tests/qapi-schema/ doesn't seem to generate any QMP command code and no .trace-events files anyway, storage-daemon/ uses common QMP command implementations and just generate empty .trace-events Signed-off-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Stefan Hajnoczi Message-Id: <20220126161130.3240892-8-vsementsov@virtuozzo.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- scripts/qapi/main.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py index 687d408..fc216a5 100644 --- a/scripts/qapi/main.py +++ b/scripts/qapi/main.py @@ -76,10 +76,10 @@ def main() -> int: dest='unmask', help="expose non-ABI names in introspection") - # Option --gen-trace exists so we can avoid solving build system + # Option --suppress-tracing exists so we can avoid solving build system # problems. TODO Drop it when we no longer need it. - parser.add_argument('--gen-trace', action='store_true', - help="add trace events to qmp marshals") + 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() @@ -96,7 +96,7 @@ def main() -> int: prefix=args.prefix, unmask=args.unmask, builtins=args.builtins, - gen_tracing=args.gen_trace) + gen_tracing=not args.suppress_tracing) except QAPIError as err: print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr) return 1 -- cgit v1.1