On Wed, Jul 06, 2022 at 04:56:26AM +0530, 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 | 106 ++++++++++++++++++++++++++++
src/qemu/qemu_monitor.h | 56 +++++++++++++++
src/qemu/qemu_monitor_json.c | 130 +++++++++++++++++++++++++++++++++++
src/qemu/qemu_monitor_json.h | 6 ++
4 files changed, 298 insertions(+)
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index fda5d2f3..528aed49 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -4541,3 +4541,109 @@ qemuMonitorMigrateRecover(qemuMonitor *mon,
return qemuMonitorJSONMigrateRecover(mon, uri);
}
+
+VIR_ENUM_IMPL(qemuMonitorQueryStatsTarget,
+ QEMU_MONITOR_QUERY_STATS_TARGET_LAST,
+ "vm",
+ "vcpu",
+);
+
+VIR_ENUM_IMPL(qemuMonitorQueryStatsVmName,
+ QEMU_MONITOR_QUERY_STATS_VM_NAME_LAST,
+);
+
+VIR_ENUM_IMPL(qemuMonitorQueryStatsVcpuName,
+ QEMU_MONITOR_QUERY_STATS_VCPU_NAME_LAST,
+ "halt_poll_success_ns",
+ "halt_poll_fail_ns"
+);
+
As we discussed these two can be joined together and the code will look
much nicer. I don't think we need to differentiate between those,
definitely not now sice the former one is not even used yet.
+VIR_ENUM_IMPL(qemuMonitorQueryStatsProvider,
+ QEMU_MONITOR_QUERY_STATS_PROVIDER_LAST,
+ "kvm",
+);
+
+const char *
+qemuMonitorQueryStatsNameTypeToString(qemuMonitorQueryStatsTargetType target,
+ int type)
With the merged enums this function will not be needed at all, and there
are other similar cases, but since you suggested yourself you know it
very well too ;)
+{
+ switch (target) {
+ case QEMU_MONITOR_QUERY_STATS_TARGET_VM:
+ return qemuMonitorQueryStatsVmNameTypeToString(type);
+ case QEMU_MONITOR_QUERY_STATS_TARGET_VCPU:
+ return qemuMonitorQueryStatsVcpuNameTypeToString(type);
+
+ /* unreachable */
+ case QEMU_MONITOR_QUERY_STATS_TARGET_LAST:
+ ;
+ }
+
+ return NULL;
+}
+
+void
+qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider)
+{
+ virBitmapFree(provider->names);
+ g_free(provider);
+}
+
+qemuMonitorQueryStatsProvider *
+qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsTargetType target,
+ qemuMonitorQueryStatsProviderType provider_type,
+ ...)
+{
+ g_autoptr(qemuMonitorQueryStatsProvider) provider = NULL;
+ va_list name_list;
+ int stat;
+
+ provider = g_new0(qemuMonitorQueryStatsProvider, 1);
+ provider->provider = provider_type;
+ provider->names = NULL;
+
+ va_start(name_list, provider_type);
+ stat = va_arg(name_list, int);
I have a suspicion that sizeof(enum ...) is guaranteed to equal
sizeof(int) everywhere. But you should just be able to do
va_arg(name_list, qemuMonitorQueryStats)
+
+ if (stat != -1) {
and instead of -1 you can pass and check the _LAST value.
+ size_t sz = 0;
+
+ switch (target) {
+ case QEMU_MONITOR_QUERY_STATS_TARGET_VM:
+ sz = QEMU_MONITOR_QUERY_STATS_VM_NAME_LAST;
+ break;
+ case QEMU_MONITOR_QUERY_STATS_TARGET_VCPU:
+ sz = QEMU_MONITOR_QUERY_STATS_VCPU_NAME_LAST;
+ break;
+ case QEMU_MONITOR_QUERY_STATS_TARGET_LAST:
+ break;
+ }
+
+ provider->names = virBitmapNew(sz);
+
+ while (stat != -1) {
+ if (virBitmapSetBit(provider->names, stat) < 0)
+ return NULL;
+ stat = va_arg(name_list, int);
+ }
+ }
+
+ va_end(name_list);
+
+ 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)
I think you meant:
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..26df9c63 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -1554,3 +1554,59 @@ 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_VM_NAME_LAST,
+} qemuMonitorQueryStatsVmNameType;
+
+VIR_ENUM_DECL(qemuMonitorQueryStatsVmName);
+
+typedef enum {
+ QEMU_MONITOR_QUERY_STATS_VCPU_NAME_HALT_POLL_SUCCESS_NS,
+ QEMU_MONITOR_QUERY_STATS_VCPU_NAME_HALT_POLL_FAIL_NS,
+ QEMU_MONITOR_QUERY_STATS_VCPU_NAME_LAST,
+} qemuMonitorQueryStatsVcpuNameType;
+
+VIR_ENUM_DECL(qemuMonitorQueryStatsVcpuName);
+
+const char *
+qemuMonitorQueryStatsNameTypeToString(qemuMonitorQueryStatsTargetType target,
+ int type);
+
+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;
+ virBitmap *names;
+};
+
+void
+qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider);
+
+qemuMonitorQueryStatsProvider *
+qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsTargetType target,
+ 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..8774b1ff 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -9006,3 +9006,133 @@ 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++) {
+ g_autoptr(virJSONValue) provider_obj = virJSONValueNewObject();
+ qemuMonitorQueryStatsProvider *provider = providers->pdata[i];
+ const char *type_str =
qemuMonitorQueryStatsProviderTypeToString(provider->provider);
+ virBitmap *names = provider->names;
+ int rc;
+
+ rc = virJSONValueObjectAppendString(provider_obj, "provider",
type_str);
+
+ if (rc < 0)
+ return NULL;
+
+ if (names) {
+ g_autoptr(virJSONValue) provider_names = virJSONValueNewArray();
+ int curBit = virBitmapNextSetBit(names, -1);
+
+ while (curBit >= 0) {
+ const char *name = qemuMonitorQueryStatsNameTypeToString(target,
curBit);
+
+ if (virJSONValueArrayAppendString(provider_names, name) < 0)
+ return NULL;
+
+ curBit = virBitmapNextSetBit(names, curBit);
+ }
+
+ rc = virJSONValueObjectAppend(provider_obj, "names",
&provider_names);
+
+ if (rc < 0)
+ return NULL;
+ }
+
+ 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);
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index ad3853ae..db8dec80 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -878,3 +878,9 @@ qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon,
int
qemuMonitorJSONMigrateRecover(qemuMonitor *mon,
const char *uri);
+
+GPtrArray *
+qemuMonitorJSONQueryStats(qemuMonitor *mon,
+ qemuMonitorQueryStatsTargetType target,
+ char **vcpus,
+ GPtrArray *providers);
--
2.36.1