diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 4739810c9b..c2808c75a3 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4297,3 +4297,123 @@ qemuMonitorGetMigrationBlockers(qemuMonitor *mon, return qemuMonitorJSONGetMigrationBlockers(mon, blockers); } + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsTarget, + QEMU_MONITOR_QUERY_STATS_TARGET_LAST, + "vm", + "vcpu", +); + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsName, + QEMU_MONITOR_QUERY_STATS_NAME_LAST, + "halt_poll_success_ns", + "halt_poll_fail_ns", +); + + +VIR_ENUM_IMPL(qemuMonitorQueryStatsProvider, + QEMU_MONITOR_QUERY_STATS_PROVIDER_LAST, + "kvm", +); + + +void +qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider) +{ + virBitmapFree(provider->names); + g_free(provider); +} + + +qemuMonitorQueryStatsProvider * +qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsProviderType provider_type, + ...) +{ + qemuMonitorQueryStatsProvider *provider = g_new0(qemuMonitorQueryStatsProvider, 1); + qemuMonitorQueryStatsNameType stat; + va_list name_list; + + /* + * This can be lowered later in case of the enum getting quite large, hence + * the virBitmapSetExpand below which also incidently makes this function + * non-fallible. + */ + provider->names = virBitmapNew(QEMU_MONITOR_QUERY_STATS_NAME_LAST); + provider->type = provider_type; + + va_start(name_list, provider_type); + + while ((stat = va_arg(name_list, qemuMonitorQueryStatsNameType)) != + QEMU_MONITOR_QUERY_STATS_NAME_LAST) + virBitmapSetBitExpand(provider->names, stat); + + va_end(name_list); + + return provider; +} + + +virJSONValue * +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); +} + + +/** + * qemuMonitorExtractQueryStats: + * @info: One of the array members returned by qemuMonitorQueryStat + * + * Converts all the statistics into a GHashTable similar to virQEMU + * except only object with the key "value" is stored as the value i + * + * Returns NULL on failure. + */ +GHashTable * +qemuMonitorExtractQueryStats(virJSONValue *info) +{ + g_autoptr(GHashTable) hash_table = NULL; + virJSONValue *stats = NULL; + size_t i; + + if (!virJSONValueIsObject(info)) + return NULL; + + stats = virJSONValueObjectGetArray(info, "stats"); + if (!stats) + return NULL; + + hash_table = virHashNew(virJSONValueHashFree); + + for (i = 0; i < virJSONValueArraySize(stats); i++) { + virJSONValue *stat = virJSONValueArrayGet(stats, i); + virJSONValue *value = NULL; + const char *name = NULL; + + if (!virJSONValueIsObject(stat)) + continue; + + name = virJSONValueObjectGetString(stat, "name"); + if (!name) + continue; + + if (virJSONValueObjectRemoveKey(stat, "value", &value) < 0) + continue; + + virHashAddEntry(hash_table, name, value); + } + + return g_steal_pointer(&hash_table); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 78e2ebf0bd..63269e15bc 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1481,3 +1481,51 @@ qemuMonitorMigrateRecover(qemuMonitor *mon, int qemuMonitorGetMigrationBlockers(qemuMonitor *mon, char ***blockers); + +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_NAME_HALT_POLL_SUCCESS_NS, + QEMU_MONITOR_QUERY_STATS_NAME_HALT_POLL_FAIL_NS, + QEMU_MONITOR_QUERY_STATS_NAME_LAST, +} qemuMonitorQueryStatsNameType; + +VIR_ENUM_DECL(qemuMonitorQueryStatsName); + +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 type; + virBitmap *names; +}; + +void +qemuMonitorQueryStatsProviderFree(qemuMonitorQueryStatsProvider *provider); + +qemuMonitorQueryStatsProvider * +qemuMonitorQueryStatsProviderNew(qemuMonitorQueryStatsProviderType provider_type, + ...); + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMonitorQueryStatsProvider, + qemuMonitorQueryStatsProviderFree); + +virJSONValue * +qemuMonitorQueryStats(qemuMonitor *mon, + qemuMonitorQueryStatsTargetType target, + char **vcpus, + GPtrArray *providers); + +GHashTable * +qemuMonitorExtractQueryStats(virJSONValue *info); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d664e827dd..70fba50e6c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -8574,3 +8574,94 @@ qemuMonitorJSONMigrateRecover(qemuMonitor *mon, return qemuMonitorJSONCheckError(cmd, reply); } + + +/** + * 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 NULL on failure. + */ +virJSONValue * +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; + + 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->type); + virBitmap *names = provider->names; + int rc; + + rc = virJSONValueObjectAppendString(provider_obj, "provider", type_str); + + if (rc < 0) + return NULL; + + if (!virBitmapIsAllClear(names)) { + g_autoptr(virJSONValue) provider_names = virJSONValueNewArray(); + ssize_t curBit = -1; + + while ((curBit = virBitmapNextSetBit(names, curBit)) != -1) { + const char *name = qemuMonitorQueryStatsNameTypeToString(curBit); + + if (virJSONValueArrayAppendString(provider_names, name) < 0) + return NULL; + } + + 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 (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) + return NULL; + + return virJSONValueObjectStealArray(reply, "return"); +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 5154c195c9..a53e6423df 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -812,3 +812,9 @@ qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon, int qemuMonitorJSONMigrateRecover(qemuMonitor *mon, const char *uri); + +virJSONValue * +qemuMonitorJSONQueryStats(qemuMonitor *mon, + qemuMonitorQueryStatsTargetType target, + char **vcpus, + GPtrArray *providers);