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@xxxxxxxxxxxxx> > --- > 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