[PATCH 1/3] qemu_monitor: add qemuMonitorQueryStats

Michal Prívozník mprivozn at redhat.com
Tue Jun 28 15:23:09 UTC 2022


On 6/24/22 10:14, Amneesh Singh wrote:
> Related: https://gitlab.com/libvirt/libvirt/-/issues/276
> 
> This patch adds an API for the "query-stats" QMP command.
> 
> The query returns a JSON containing the statistics based on the target,
> which can either be vCPU or VM, and the providers. The API deserializes
> the query result into an array of GHashMaps, which can later be used to
> extract all the query statistics. GHashMaps are used to avoid traversing
> the entire array to find the statistics you are looking for. This would
> be a singleton array if the target is a VM since the returned JSON is
> also a singleton array in that case.
> 
> Signed-off-by: Amneesh Singh <natto at weirdnatto.in>
> ---
>  src/qemu/qemu_monitor.c      |  46 +++++++++++++
>  src/qemu/qemu_monitor.h      |  36 ++++++++++
>  src/qemu/qemu_monitor_json.c | 128 +++++++++++++++++++++++++++++++++++
>  src/qemu/qemu_monitor_json.h |   6 ++
>  4 files changed, 216 insertions(+)
> 
> diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
> index fda5d2f3..bedb5308 100644
> --- a/src/qemu/qemu_monitor.c
> +++ b/src/qemu/qemu_monitor.c
> @@ -4541,3 +4541,49 @@ qemuMonitorMigrateRecover(qemuMonitor *mon,
>  
>      return qemuMonitorJSONMigrateRecover(mon, uri);
>  }
> +
> +VIR_ENUM_IMPL(qemuMonitorQueryStatsTarget,
> +              QEMU_MONITOR_QUERY_STATS_TARGET_LAST,
> +              "vm",
> +              "vcpu",
> +);
> +
> +VIR_ENUM_IMPL(qemuMonitorQueryStatsProvider,
> +              QEMU_MONITOR_QUERY_STATS_PROVIDER_LAST,
> +              "kvm",
> +);
> +
> +void
> +qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider)
> +{
> +    g_strfreev(provider->names);
> +    g_free(provider);
> +}
> +
> +qemuMonitorQueryStatsProvider *
> +qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsProviderType provider_type)
> +{
> +    g_autoptr(qemuMonitorQueryStatsProvider) provider = NULL;
> +
> +    provider = g_new0(qemuMonitorQueryStatsProvider, 1);
> +    provider->provider = provider_type;
> +    provider->names = NULL;
> +
> +    return g_steal_pointer(&provider);
> +}
> +
> +GPtrArray *
> +qemuMonitorQueryStats(qemuMonitor *mon,
> +                      qemuMonitorQueryStatsTargetType target,
> +                      char **vcpus,
> +                      GPtrArray *providers)
> +{
> +    VIR_DEBUG("target=%u vcpus=%p providers=%p", target, vcpus, providers);
> +
> +    QEMU_CHECK_MONITOR_NULL(mon);
> +
> +    if (target != QEMU_MONITOR_QUERY_STATS_TARGET_VCPU && !vcpus)
> +        return NULL;
> +
> +    return qemuMonitorJSONQueryStats(mon, target, vcpus, providers);
> +}
> diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
> index 95267ec6..ddfdb526 100644
> --- a/src/qemu/qemu_monitor.h
> +++ b/src/qemu/qemu_monitor.h
> @@ -1554,3 +1554,39 @@ qemuMonitorChangeMemoryRequestedSize(qemuMonitor *mon,
>  int
>  qemuMonitorMigrateRecover(qemuMonitor *mon,
>                            const char *uri);
> +
> +typedef enum {
> +    QEMU_MONITOR_QUERY_STATS_TARGET_VM,
> +    QEMU_MONITOR_QUERY_STATS_TARGET_VCPU,
> +    QEMU_MONITOR_QUERY_STATS_TARGET_LAST,
> +} qemuMonitorQueryStatsTargetType;
> +
> +VIR_ENUM_DECL(qemuMonitorQueryStatsTarget);
> +
> +typedef enum {
> +    QEMU_MONITOR_QUERY_STATS_PROVIDER_KVM,
> +    QEMU_MONITOR_QUERY_STATS_PROVIDER_LAST,
> +} qemuMonitorQueryStatsProviderType;
> +
> +VIR_ENUM_DECL(qemuMonitorQueryStatsProvider);
> +
> +typedef struct _qemuMonitorQueryStatsProvider qemuMonitorQueryStatsProvider;
> +struct _qemuMonitorQueryStatsProvider {
> +    qemuMonitorQueryStatsProviderType provider;
> +    GStrv names;
> +};
> +
> +void
> +qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider);
> +
> +qemuMonitorQueryStatsProvider *
> +qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsProviderType provider_type);
> +
> +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMonitorQueryStatsProvider,
> +                              qemuMonitorQueryStatsProviderFree);
> +
> +GPtrArray *
> +qemuMonitorQueryStats(qemuMonitor *mon,
> +                      qemuMonitorQueryStatsTargetType target,
> +                      char **vcpus,
> +                      GPtrArray *providers);
> diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
> index 3aad2ab2..d6757042 100644
> --- a/src/qemu/qemu_monitor_json.c
> +++ b/src/qemu/qemu_monitor_json.c
> @@ -9006,3 +9006,131 @@ qemuMonitorJSONMigrateRecover(qemuMonitor *mon,
>  
>      return qemuMonitorJSONCheckError(cmd, reply);
>  }
> +
> +static GPtrArray *
> +qemuMonitorJSONExtractQueryStats(virJSONValue *arr)
> +{
> +    g_autoptr(GPtrArray) queried_stats = NULL;
> +    size_t nstats = virJSONValueArraySize(arr);
> +    size_t i;
> +
> +    /* Create a GPtrArray for GHashTables */
> +    queried_stats = g_ptr_array_new_full(nstats, (GDestroyNotify) g_hash_table_destroy);
> +
> +    for (i = 0; i < nstats; i++) {
> +        virJSONValue *obj = virJSONValueArrayGet(arr, i);
> +        virJSONValue *stats = virJSONValueObjectGetArray(obj, "stats");
> +        size_t j;
> +
> +        /* Create a GHashTable for virJSONValues */
> +        GHashTable *hash_table = virHashNew((GDestroyNotify) virJSONValueFree);
> +
> +        for (j = 0; j < virJSONValueArraySize(stats); j++) {
> +            virJSONValue *stat = virJSONValueArrayGet(stats, j);
> +
> +            g_hash_table_insert(hash_table,
> +                                g_strdup(virJSONValueObjectGetString(stat, "name")),
> +                                virJSONValueObjectGet(stat, "value"));
> +        }
> +
> +        g_ptr_array_add(queried_stats, hash_table);
> +    }
> +
> +    return g_steal_pointer(&queried_stats);
> +}
> +
> +
> +/**
> + * qemuMonitorJSONQueryStats:
> + * @mon: monitor object
> + * @target: the target type for the query
> + * @vcpus: a list of vCPU QOM paths for filtering the statistics
> + * @providers: an array of providers to filter statistics
> + *
> + * @vcpus is a NULL terminated array of strings. @providers is a GPtrArray
> + * for qemuMonitorQueryStatsProvider.
> + * @vcpus and @providers are optional and can be NULL.
> + *
> + * Queries for the @target based statistics.
> + * Returns a GPtrArray of GHashTables containing the statistics on success or
> + * NULL on failure.
> + */
> +
> +GPtrArray *
> +qemuMonitorJSONQueryStats(qemuMonitor *mon,
> +                          qemuMonitorQueryStatsTargetType target,
> +                          char **vcpus,
> +                          GPtrArray *providers)
> +{
> +    g_autoptr(virJSONValue) cmd = NULL;
> +    g_autoptr(virJSONValue) reply = NULL;
> +    g_autoptr(virJSONValue) vcpu_list = NULL;
> +    g_autoptr(virJSONValue) provider_list = NULL;
> +    virJSONValue *rv = NULL;
> +
> +    size_t i;
> +
> +    if (providers) {
> +        provider_list = virJSONValueNewArray();
> +
> +        for (i = 0; i < providers->len; i++) {
> +            int rc;
> +            g_autoptr(virJSONValue) provider_obj = virJSONValueNewObject();
> +            qemuMonitorQueryStatsProvider *provider = providers->pdata[i];
> +            const char *type_str = qemuMonitorQueryStatsProviderTypeToString(provider->provider);


Nit pick, I think this block would look a bit better if @rc was declared
at its end.

> +
> +            rc = virJSONValueObjectAppendString(provider_obj, "provider", type_str);
> +
> +            if (rc < 0)
> +                return NULL;
> +
> +            if (provider->names) {
> +                g_autoptr(virJSONValue) providerNames = virJSONValueNewArray();
> +                size_t j = 0;
> +                const char *name = provider->names[j];

Another nitpick: here I'd prefer names[0] instead if names[j]. While it
may not matter now, @j and @name are declared next to each other, that
may not be always the case and I have to remember one thing less when
reading the code.

> +
> +                while (name) {
> +                    if (virJSONValueArrayAppendString(providerNames, name) < 0)
> +                        return NULL;
> +
> +                    name = provider->names[++j];
> +                }
> +
> +                rc = virJSONValueObjectAppend(provider_obj, "names", &providerNames);
> +
> +                if (rc < 0)
> +                    return NULL;
> +                }

Misleading alignment.

> +
> +            if (virJSONValueArrayAppend(provider_list, &provider_obj) < 0)
> +                return NULL;
> +        }
> +    }
> +
> +    if (vcpus) {
> +        vcpu_list = virJSONValueNewArray();
> +
> +        for (i = 0; vcpus[i]; i++)
> +            if (virJSONValueArrayAppendString(vcpu_list, vcpus[i]) < 0)
> +                return NULL;
> +    }
> +
> +    cmd = qemuMonitorJSONMakeCommand("query-stats",
> +                                     "s:target", qemuMonitorQueryStatsTargetTypeToString(target),
> +                                     "A:vcpus", &vcpu_list,
> +                                     "A:providers", &provider_list,
> +                                     NULL);
> +
> +    if (!cmd)
> +        return NULL;
> +
> +    if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
> +        return NULL;
> +
> +    if (qemuMonitorJSONCheckError(cmd, reply) < 0)
> +        return NULL;
> +
> +    rv = virJSONValueObjectStealArray(reply, "return");
> +
> +    return qemuMonitorJSONExtractQueryStats(rv);
> +}

Michal



More information about the libvir-list mailing list