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(a)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