Add a possibility to trace some qmp commands by user selection. User API is simple: it looks like after this patch we have trace-points for all qmp commands, in format "qmp:<qmp-command>". So user may do
--trace qmp:drive-backup or run qmp command trace-event-set-state with arguments name="qmp:drive-backup", enable=true Patterns are supported in the same way as for general trace events, for example: --trace "qmp:query-*" to cover query-block, query-block-jobs, and a lot of other query- commands or --trace "qmp:job-*" --trace "qmp:block-job-*" to cover job manipulation commands. Trace event qmp covers both qmp command call and its respond. Implementation details: In qmp_dispatch() we add two trace points, similar to trace_handle_qmp_command. We also check for cmd->tracking field being set, and for handle-qmp-command/monitor-qmp-respond trace points being disabled, to not print same information twice. For qmp trace-event-{get,set}-state and for command line parse additionally new "qmp:" namespace Signed-off-by: Vladimir Sementsov-Ogievskiy <vsement...@virtuozzo.com> --- qapi/qmp-dispatch.c | 20 ++++++++++++++++++++ softmmu/vl.c | 21 ++++++++++++++++++++- trace/qmp.c | 20 ++++++++++++++++++++ qapi/trace-events | 2 ++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 59600210ce..40fd008fa8 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -26,6 +26,9 @@ #include "qemu/coroutine.h" #include "qemu/main-loop.h" +#include "trace.h" +#include "trace/trace-monitor.h" + CompatPolicy compat_policy; Visitor *qobject_input_visitor_new_qmp(QObject *obj) @@ -133,6 +136,12 @@ static void do_qmp_dispatch_bh(void *opaque) aio_co_wake(data->co); } +static void do_trace_qmp(const char *what, QObject *obj) +{ + g_autoptr(GString) req_json = qobject_to_json(obj); + trace_qmp(what, req_json->str); +} + /* * Runs outside of coroutine context for OOB commands, but in coroutine * context for everything else. @@ -176,6 +185,11 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request, "The command %s has not been found", command); goto out; } + if (!trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND) && + trace_event_get_state_backends(TRACE_QMP) && cmd->tracing) + { + do_trace_qmp(">>", request); + } if (cmd->options & QCO_DEPRECATED) { switch (compat_policy.deprecated_input) { case COMPAT_POLICY_INPUT_ACCEPT: @@ -282,5 +296,11 @@ out: qdict_put_obj(rsp, "id", qobject_ref(id)); } + if (!trace_event_get_state_backends(TRACE_MONITOR_QMP_RESPOND) && + trace_event_get_state_backends(TRACE_QMP) && cmd->tracing) + { + do_trace_qmp("<<", QOBJECT(rsp)); + } + return rsp; } diff --git a/softmmu/vl.c b/softmmu/vl.c index 55ab70eb97..fd452b7013 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -2722,6 +2722,25 @@ void qmp_x_exit_preconfig(Error **errp) } } +static void trace_opt_parse_with_qmp(const char *optarg) +{ + const char *pattern; + QemuOpts *opts = qemu_opts_parse_noisily(qemu_find_opts("trace"), + optarg, true); + if (!opts) { + exit(1); + } + + pattern = qemu_opt_get(opts, "enable"); + if (pattern && !strncmp(pattern, "qmp:", 4)) { + monitor_qmp_set_tracing(pattern + 4, true); + qemu_opt_del_all(opts, "enable"); + qemu_opt_set(opts, "enable", "qmp", &error_abort); + } + + trace_opt_parse_opts(opts); +} + void qemu_init(int argc, char **argv, char **envp) { QemuOpts *opts; @@ -3480,7 +3499,7 @@ void qemu_init(int argc, char **argv, char **envp) xen_domid_restrict = true; break; case QEMU_OPTION_trace: - trace_opt_parse(optarg); + trace_opt_parse_with_qmp(optarg); break; case QEMU_OPTION_plugin: qemu_plugin_opt_parse(optarg, &plugin_list); diff --git a/trace/qmp.c b/trace/qmp.c index 3b4f4702b4..87766422d9 100644 --- a/trace/qmp.c +++ b/trace/qmp.c @@ -11,6 +11,7 @@ #include "qapi/error.h" #include "qapi/qapi-commands-trace.h" #include "control-vcpu.h" +#include "monitor/monitor.h" static CPUState *get_cpu(bool has_vcpu, int vcpu, Error **errp) @@ -77,6 +78,14 @@ TraceEventInfoList *qmp_trace_event_get_state(const char *name, bool is_pattern = trace_event_is_pattern(name); CPUState *cpu; + if (!strncmp(name, "qmp:", 4)) { + if (!monitor_qmp_is_tracing_enabled(name + 4)) { + return NULL; + } + + return qmp_trace_event_get_state("qmp", has_vcpu, vcpu, errp); + } + /* Check provided vcpu */ cpu = get_cpu(has_vcpu, vcpu, &err); if (err) { @@ -139,6 +148,17 @@ void qmp_trace_event_set_state(const char *name, bool enable, bool is_pattern = trace_event_is_pattern(name); CPUState *cpu; + if (!strncmp(name, "qmp:", 4)) { + monitor_qmp_set_tracing(name + 4, enable); + + if (enable || !monitor_qmp_is_tracing_enabled(NULL)) { + qmp_trace_event_set_state("qmp", enable, has_ignore_unavailable, + ignore_unavailable, has_vcpu, vcpu, errp); + } + + return; + } + /* Check provided vcpu */ cpu = get_cpu(has_vcpu, vcpu, &err); if (err) { diff --git a/qapi/trace-events b/qapi/trace-events index cccafc07e5..753433856c 100644 --- a/qapi/trace-events +++ b/qapi/trace-events @@ -36,3 +36,5 @@ visit_type_str(void *v, const char *name, char **obj) "v=%p name=%s obj=%p" visit_type_number(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" visit_type_any(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" visit_type_null(void *v, const char *name, void *obj) "v=%p name=%s obj=%p" + +qmp(const char *what, const char *json) "%s %s" -- 2.29.2