diff options
author | Richard Henderson | 2022-06-16 16:13:04 +0200 |
---|---|---|
committer | Richard Henderson | 2022-06-16 16:13:04 +0200 |
commit | def6fd6c9ce9e00a30cdd0066e0fde206b3f3d2f (patch) | |
tree | dc547432f542e829ed0e770beca1dbb13e2223ef | |
parent | Merge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging (diff) | |
parent | build: include pc-bios/ part in the ROMS variable (diff) | |
download | qemu-def6fd6c9ce9e00a30cdd0066e0fde206b3f3d2f.tar.gz qemu-def6fd6c9ce9e00a30cdd0066e0fde206b3f3d2f.tar.xz qemu-def6fd6c9ce9e00a30cdd0066e0fde206b3f3d2f.zip |
Merge tag 'for-upstream' of https://gitlab.com/bonzini/qemu into staging
* statistics subsystem
* virtio reset cleanups
* build system cleanups
* fix Cirrus CI
# -----BEGIN PGP SIGNATURE-----
#
# iQFIBAABCAAyFiEE8TM4V0tmI4mGbHaCv/vSX3jHroMFAmKpooQUHHBib256aW5p
# QHJlZGhhdC5jb20ACgkQv/vSX3jHroNlFwf+OugLGRZl3KVc7akQwUJe9gg2T31h
# VkC+7Tei8FAwe8vDppVd+CYEIi0M3acxD2amRrv2etCCGSuySN1PbkfRcSfPBX01
# pRWpasdhfqnZR8Iidi7YW1Ou5CcGqKH49nunBhW10+osb/mu5sVscMuOJgTDj/lK
# CpsmDyk6572yGmczjNLlmhYcTU36clHpAZgazZHwk1PU+B3fCKlYYyvUpT3ItJvd
# cK92aIUWrfofl3yTy0k4IwvZwNjTBirlstOIomZ333xzSA+mm5TR+mTvGRTZ69+a
# v+snpMp4ILDMoB5kxQ42kK5WpdiN//LnriA9CBFDtOidsDDn8kx7gJe2RA==
# =Dxwa
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 15 Jun 2022 02:12:36 AM PDT
# gpg: using RSA key F13338574B662389866C7682BFFBD25F78C7AE83
# gpg: issuer "pbonzini@redhat.com"
# gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" [undefined]
# gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" [undefined]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1
# Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83
* tag 'for-upstream' of https://gitlab.com/bonzini/qemu: (21 commits)
build: include pc-bios/ part in the ROMS variable
meson: put cross compiler info in a separate section
q35:Enable TSEG only when G_SMRAME and TSEG_EN both enabled
build: fix check for -fsanitize-coverage-allowlist
tests/vm: allow running tests in an unconfigured source tree
configure: cleanup -fno-pie detection
configure: update list of preserved environment variables
virtio-mmio: cleanup reset
virtio: stop ioeventfd on reset
virtio-mmio: stop ioeventfd on legacy reset
s390x: simplify virtio_ccw_reset_virtio
block: add more commands to preconfig mode
hmp: add filtering of statistics by name
qmp: add filtering of statistics by name
hmp: add filtering of statistics by provider
qmp: add filtering of statistics by provider
hmp: add basic "info stats" implementation
cutils: add functions for IEC and SI prefixes
qmp: add filtering of statistics by target vCPU
kvm: Support for querying fd-based stats
...
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
-rw-r--r-- | Makefile | 12 | ||||
-rw-r--r-- | accel/kvm/kvm-all.c | 403 | ||||
-rwxr-xr-x | configure | 22 | ||||
-rw-r--r-- | hmp-commands-info.hx | 14 | ||||
-rw-r--r-- | hmp-commands.hx | 14 | ||||
-rw-r--r-- | hw/pci-host/q35.c | 3 | ||||
-rw-r--r-- | hw/s390x/virtio-ccw.c | 12 | ||||
-rw-r--r-- | hw/virtio/virtio-bus.c | 1 | ||||
-rw-r--r-- | hw/virtio/virtio-mmio.c | 18 | ||||
-rw-r--r-- | hw/virtio/virtio-pci.c | 1 | ||||
-rw-r--r-- | include/monitor/hmp.h | 1 | ||||
-rw-r--r-- | include/monitor/stats.h | 45 | ||||
-rw-r--r-- | include/qemu/cutils.h | 18 | ||||
-rw-r--r-- | meson.build | 25 | ||||
-rw-r--r-- | monitor/hmp-cmds.c | 232 | ||||
-rw-r--r-- | monitor/qmp-cmds.c | 155 | ||||
-rw-r--r-- | qapi/block-core.json | 117 | ||||
-rw-r--r-- | qapi/block-export.json | 21 | ||||
-rw-r--r-- | qapi/block.json | 6 | ||||
-rw-r--r-- | qapi/meson.build | 1 | ||||
-rw-r--r-- | qapi/qapi-schema.json | 1 | ||||
-rw-r--r-- | qapi/stats.json | 249 | ||||
-rw-r--r-- | tests/unit/test-cutils.c | 52 | ||||
-rw-r--r-- | tests/vm/Makefile.include | 26 | ||||
-rw-r--r-- | util/cutils.c | 34 |
25 files changed, 1368 insertions, 115 deletions
@@ -186,16 +186,14 @@ include $(SRC_PATH)/tests/Makefile.include all: recurse-all -ROM_DIRS = $(addprefix pc-bios/, $(ROMS)) -ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS))) -# Only keep -O and -g cflags -.PHONY: $(ROM_DIRS_RULES) -$(ROM_DIRS_RULES): +ROMS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROMS))) +.PHONY: $(ROMS_RULES) +$(ROMS_RULES): $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),) .PHONY: recurse-all recurse-clean -recurse-all: $(addsuffix /all, $(ROM_DIRS)) -recurse-clean: $(addsuffix /clean, $(ROM_DIRS)) +recurse-all: $(addsuffix /all, $(ROMS)) +recurse-clean: $(addsuffix /clean, $(ROMS)) ###################################################################### diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a4c4863f53..ba3210b1c1 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -47,6 +47,7 @@ #include "kvm-cpus.h" #include "hw/boards.h" +#include "monitor/stats.h" /* This check must be after config-host.h is included */ #ifdef CONFIG_EVENTFD @@ -2310,6 +2311,10 @@ bool kvm_dirty_ring_enabled(void) return kvm_state->kvm_dirty_ring_size ? true : false; } +static void query_stats_cb(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp); +static void query_stats_schemas_cb(StatsSchemaList **result, Error **errp); + static int kvm_init(MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -2638,6 +2643,11 @@ static int kvm_init(MachineState *ms) } } + if (kvm_check_extension(kvm_state, KVM_CAP_BINARY_STATS_FD)) { + add_stats_callbacks(STATS_PROVIDER_KVM, query_stats_cb, + query_stats_schemas_cb); + } + return 0; err: @@ -3697,3 +3707,396 @@ static void kvm_type_init(void) } type_init(kvm_type_init); + +typedef struct StatsArgs { + union StatsResultsType { + StatsResultList **stats; + StatsSchemaList **schema; + } result; + strList *names; + Error **errp; +} StatsArgs; + +static StatsList *add_kvmstat_entry(struct kvm_stats_desc *pdesc, + uint64_t *stats_data, + StatsList *stats_list, + Error **errp) +{ + + Stats *stats; + uint64List *val_list = NULL; + + /* Only add stats that we understand. */ + switch (pdesc->flags & KVM_STATS_TYPE_MASK) { + case KVM_STATS_TYPE_CUMULATIVE: + case KVM_STATS_TYPE_INSTANT: + case KVM_STATS_TYPE_PEAK: + case KVM_STATS_TYPE_LINEAR_HIST: + case KVM_STATS_TYPE_LOG_HIST: + break; + default: + return stats_list; + } + + switch (pdesc->flags & KVM_STATS_UNIT_MASK) { + case KVM_STATS_UNIT_NONE: + case KVM_STATS_UNIT_BYTES: + case KVM_STATS_UNIT_CYCLES: + case KVM_STATS_UNIT_SECONDS: + break; + default: + return stats_list; + } + + switch (pdesc->flags & KVM_STATS_BASE_MASK) { + case KVM_STATS_BASE_POW10: + case KVM_STATS_BASE_POW2: + break; + default: + return stats_list; + } + + /* Alloc and populate data list */ + stats = g_new0(Stats, 1); + stats->name = g_strdup(pdesc->name); + stats->value = g_new0(StatsValue, 1);; + + if (pdesc->size == 1) { + stats->value->u.scalar = *stats_data; + stats->value->type = QTYPE_QNUM; + } else { + int i; + for (i = 0; i < pdesc->size; i++) { + QAPI_LIST_PREPEND(val_list, stats_data[i]); + } + stats->value->u.list = val_list; + stats->value->type = QTYPE_QLIST; + } + + QAPI_LIST_PREPEND(stats_list, stats); + return stats_list; +} + +static StatsSchemaValueList *add_kvmschema_entry(struct kvm_stats_desc *pdesc, + StatsSchemaValueList *list, + Error **errp) +{ + StatsSchemaValueList *schema_entry = g_new0(StatsSchemaValueList, 1); + schema_entry->value = g_new0(StatsSchemaValue, 1); + + switch (pdesc->flags & KVM_STATS_TYPE_MASK) { + case KVM_STATS_TYPE_CUMULATIVE: + schema_entry->value->type = STATS_TYPE_CUMULATIVE; + break; + case KVM_STATS_TYPE_INSTANT: + schema_entry->value->type = STATS_TYPE_INSTANT; + break; + case KVM_STATS_TYPE_PEAK: + schema_entry->value->type = STATS_TYPE_PEAK; + break; + case KVM_STATS_TYPE_LINEAR_HIST: + schema_entry->value->type = STATS_TYPE_LINEAR_HISTOGRAM; + schema_entry->value->bucket_size = pdesc->bucket_size; + schema_entry->value->has_bucket_size = true; + break; + case KVM_STATS_TYPE_LOG_HIST: + schema_entry->value->type = STATS_TYPE_LOG2_HISTOGRAM; + break; + default: + goto exit; + } + + switch (pdesc->flags & KVM_STATS_UNIT_MASK) { + case KVM_STATS_UNIT_NONE: + break; + case KVM_STATS_UNIT_BYTES: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_BYTES; + break; + case KVM_STATS_UNIT_CYCLES: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_CYCLES; + break; + case KVM_STATS_UNIT_SECONDS: + schema_entry->value->has_unit = true; + schema_entry->value->unit = STATS_UNIT_SECONDS; + break; + default: + goto exit; + } + + schema_entry->value->exponent = pdesc->exponent; + if (pdesc->exponent) { + switch (pdesc->flags & KVM_STATS_BASE_MASK) { + case KVM_STATS_BASE_POW10: + schema_entry->value->has_base = true; + schema_entry->value->base = 10; + break; + case KVM_STATS_BASE_POW2: + schema_entry->value->has_base = true; + schema_entry->value->base = 2; + break; + default: + goto exit; + } + } + + schema_entry->value->name = g_strdup(pdesc->name); + schema_entry->next = list; + return schema_entry; +exit: + g_free(schema_entry->value); + g_free(schema_entry); + return list; +} + +/* Cached stats descriptors */ +typedef struct StatsDescriptors { + const char *ident; /* cache key, currently the StatsTarget */ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + QTAILQ_ENTRY(StatsDescriptors) next; +} StatsDescriptors; + +static QTAILQ_HEAD(, StatsDescriptors) stats_descriptors = + QTAILQ_HEAD_INITIALIZER(stats_descriptors); + +/* + * Return the descriptors for 'target', that either have already been read + * or are retrieved from 'stats_fd'. + */ +static StatsDescriptors *find_stats_descriptors(StatsTarget target, int stats_fd, + Error **errp) +{ + StatsDescriptors *descriptors; + const char *ident; + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + size_t size_desc; + ssize_t ret; + + ident = StatsTarget_str(target); + QTAILQ_FOREACH(descriptors, &stats_descriptors, next) { + if (g_str_equal(descriptors->ident, ident)) { + return descriptors; + } + } + + descriptors = g_new0(StatsDescriptors, 1); + + /* Read stats header */ + kvm_stats_header = g_malloc(sizeof(*kvm_stats_header)); + ret = read(stats_fd, kvm_stats_header, sizeof(*kvm_stats_header)); + if (ret != sizeof(*kvm_stats_header)) { + error_setg(errp, "KVM stats: failed to read stats header: " + "expected %zu actual %zu", + sizeof(*kvm_stats_header), ret); + return NULL; + } + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Read stats descriptors */ + kvm_stats_desc = g_malloc0_n(kvm_stats_header->num_desc, size_desc); + ret = pread(stats_fd, kvm_stats_desc, + size_desc * kvm_stats_header->num_desc, + kvm_stats_header->desc_offset); + + if (ret != size_desc * kvm_stats_header->num_desc) { + error_setg(errp, "KVM stats: failed to read stats descriptors: " + "expected %zu actual %zu", + size_desc * kvm_stats_header->num_desc, ret); + g_free(descriptors); + g_free(kvm_stats_desc); + return NULL; + } + descriptors->kvm_stats_header = kvm_stats_header; + descriptors->kvm_stats_desc = kvm_stats_desc; + descriptors->ident = ident; + QTAILQ_INSERT_TAIL(&stats_descriptors, descriptors, next); + return descriptors; +} + +static void query_stats(StatsResultList **result, StatsTarget target, + strList *names, int stats_fd, Error **errp) +{ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + StatsDescriptors *descriptors; + g_autofree uint64_t *stats_data = NULL; + struct kvm_stats_desc *pdesc; + StatsList *stats_list = NULL; + size_t size_desc, size_data = 0; + ssize_t ret; + int i; + + descriptors = find_stats_descriptors(target, stats_fd, errp); + if (!descriptors) { + return; + } + + kvm_stats_header = descriptors->kvm_stats_header; + kvm_stats_desc = descriptors->kvm_stats_desc; + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Tally the total data size; read schema data */ + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + pdesc = (void *)kvm_stats_desc + i * size_desc; + size_data += pdesc->size * sizeof(*stats_data); + } + + stats_data = g_malloc0(size_data); + ret = pread(stats_fd, stats_data, size_data, kvm_stats_header->data_offset); + + if (ret != size_data) { + error_setg(errp, "KVM stats: failed to read data: " + "expected %zu actual %zu", size_data, ret); + return; + } + + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + uint64_t *stats; + pdesc = (void *)kvm_stats_desc + i * size_desc; + + /* Add entry to the list */ + stats = (void *)stats_data + pdesc->offset; + if (!apply_str_list_filter(pdesc->name, names)) { + continue; + } + stats_list = add_kvmstat_entry(pdesc, stats, stats_list, errp); + } + + if (!stats_list) { + return; + } + + switch (target) { + case STATS_TARGET_VM: + add_stats_entry(result, STATS_PROVIDER_KVM, NULL, stats_list); + break; + case STATS_TARGET_VCPU: + add_stats_entry(result, STATS_PROVIDER_KVM, + current_cpu->parent_obj.canonical_path, + stats_list); + break; + default: + break; + } +} + +static void query_stats_schema(StatsSchemaList **result, StatsTarget target, + int stats_fd, Error **errp) +{ + struct kvm_stats_desc *kvm_stats_desc; + struct kvm_stats_header *kvm_stats_header; + StatsDescriptors *descriptors; + struct kvm_stats_desc *pdesc; + StatsSchemaValueList *stats_list = NULL; + size_t size_desc; + int i; + + descriptors = find_stats_descriptors(target, stats_fd, errp); + if (!descriptors) { + return; + } + + kvm_stats_header = descriptors->kvm_stats_header; + kvm_stats_desc = descriptors->kvm_stats_desc; + size_desc = sizeof(*kvm_stats_desc) + kvm_stats_header->name_size; + + /* Tally the total data size; read schema data */ + for (i = 0; i < kvm_stats_header->num_desc; ++i) { + pdesc = (void *)kvm_stats_desc + i * size_desc; + stats_list = add_kvmschema_entry(pdesc, stats_list, errp); + } + + add_stats_schema(result, STATS_PROVIDER_KVM, target, stats_list); +} + +static void query_stats_vcpu(CPUState *cpu, run_on_cpu_data data) +{ + StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr; + int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL); + Error *local_err = NULL; + + if (stats_fd == -1) { + error_setg_errno(&local_err, errno, "KVM stats: ioctl failed"); + error_propagate(kvm_stats_args->errp, local_err); + return; + } + query_stats(kvm_stats_args->result.stats, STATS_TARGET_VCPU, + kvm_stats_args->names, stats_fd, kvm_stats_args->errp); + close(stats_fd); +} + +static void query_stats_schema_vcpu(CPUState *cpu, run_on_cpu_data data) +{ + StatsArgs *kvm_stats_args = (StatsArgs *) data.host_ptr; + int stats_fd = kvm_vcpu_ioctl(cpu, KVM_GET_STATS_FD, NULL); + Error *local_err = NULL; + + if (stats_fd == -1) { + error_setg_errno(&local_err, errno, "KVM stats: ioctl failed"); + error_propagate(kvm_stats_args->errp, local_err); + return; + } + query_stats_schema(kvm_stats_args->result.schema, STATS_TARGET_VCPU, stats_fd, + kvm_stats_args->errp); + close(stats_fd); +} + +static void query_stats_cb(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp) +{ + KVMState *s = kvm_state; + CPUState *cpu; + int stats_fd; + + switch (target) { + case STATS_TARGET_VM: + { + stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL); + if (stats_fd == -1) { + error_setg_errno(errp, errno, "KVM stats: ioctl failed"); + return; + } + query_stats(result, target, names, stats_fd, errp); + close(stats_fd); + break; + } + case STATS_TARGET_VCPU: + { + StatsArgs stats_args; + stats_args.result.stats = result; + stats_args.names = names; + stats_args.errp = errp; + CPU_FOREACH(cpu) { + if (!apply_str_list_filter(cpu->parent_obj.canonical_path, targets)) { + continue; + } + run_on_cpu(cpu, query_stats_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args)); + } + break; + } + default: + break; + } +} + +void query_stats_schemas_cb(StatsSchemaList **result, Error **errp) +{ + StatsArgs stats_args; + KVMState *s = kvm_state; + int stats_fd; + + stats_fd = kvm_vm_ioctl(s, KVM_GET_STATS_FD, NULL); + if (stats_fd == -1) { + error_setg_errno(errp, errno, "KVM stats: ioctl failed"); + return; + } + query_stats_schema(result, STATS_TARGET_VM, stats_fd, errp); + close(stats_fd); + + stats_args.result.schema = result; + stats_args.errp = errp; + run_on_cpu(first_cpu, query_stats_schema_vcpu, RUN_ON_CPU_HOST_PTR(&stats_args)); +} @@ -1351,13 +1351,6 @@ static THREAD int tls_var; int main(void) { return tls_var; } EOF -# Check we support -fno-pie and -no-pie first; we will need the former for -# building ROMs, and both for everything if --disable-pie is passed. -if compile_prog "-Werror -fno-pie" "-no-pie"; then - CFLAGS_NOPIE="-fno-pie" - LDFLAGS_NOPIE="-no-pie" -fi - if test "$static" = "yes"; then if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" @@ -1370,8 +1363,10 @@ if test "$static" = "yes"; then pie="no" fi elif test "$pie" = "no"; then - CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS" - CONFIGURE_LDFLAGS="$LDFLAGS_NOPIE $CONFIGURE_LDFLAGS" + if compile_prog "-Werror -fno-pie" "-no-pie"; then + CONFIGURE_CFLAGS="-fno-pie $CONFIGURE_CFLAGS" + CONFIGURE_LDFLAGS="-no-pie $CONFIGURE_LDFLAGS" + fi elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS" @@ -2257,7 +2252,7 @@ if test -n "$target_cc" && fi done if test -n "$ld_i386_emulation"; then - roms="optionrom" + roms="pc-bios/optionrom" config_mak=pc-bios/optionrom/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "TOPSRC_DIR=$source_path" >> $config_mak @@ -2268,7 +2263,7 @@ fi probe_target_compilers ppc ppc64 if test -n "$target_cc" && test "$softmmu" = yes; then - roms="$roms vof" + roms="$roms pc-bios/vof" config_mak=pc-bios/vof/config.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_DIR=$source_path/pc-bios/vof" >> $config_mak @@ -2287,7 +2282,7 @@ if test -n "$target_cc" && test "$softmmu" = yes; then echo "WARNING: Your compiler does not support the z900!" echo " The s390-ccw bios will only work with guest CPUs >= z10." fi - roms="$roms s390-ccw" + roms="$roms pc-bios/s390-ccw" config_mak=pc-bios/s390-ccw/config-host.mak echo "# Automatically generated by configure - do not modify" > $config_mak echo "SRC_PATH=$source_path/pc-bios/s390-ccw" >> $config_mak @@ -2739,13 +2734,12 @@ preserve_env CC preserve_env CFLAGS preserve_env CXX preserve_env CXXFLAGS -preserve_env INSTALL preserve_env LD preserve_env LDFLAGS preserve_env LD_LIBRARY_PATH -preserve_env LIBTOOL preserve_env MAKE preserve_env NM +preserve_env OBJCFLAGS preserve_env OBJCOPY preserve_env PATH preserve_env PKG_CONFIG diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 834bed089e..3ffa24bd67 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -894,3 +894,17 @@ SRST ``info via`` Show guest mos6522 VIA devices. ERST + + { + .name = "stats", + .args_type = "target:s,names:s?,provider:s?", + .params = "target [names] [provider]", + .help = "show statistics for the given target (vm or vcpu); optionally filter by" + "name (comma-separated list, or * for all) and provider", + .cmd = hmp_info_stats, + }, + +SRST + ``stats`` + Show runtime-collected statistics +ERST diff --git a/hmp-commands.hx b/hmp-commands.hx index 564f1de364..c9d465735a 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -78,6 +78,7 @@ ERST .help = "resize a block image", .cmd = hmp_block_resize, .coroutine = true, + .flags = "p", }, SRST @@ -94,6 +95,7 @@ ERST .params = "device [speed [base]]", .help = "copy data from a backing file into a block device", .cmd = hmp_block_stream, + .flags = "p", }, SRST @@ -107,6 +109,7 @@ ERST .params = "device speed", .help = "set maximum speed for a background block operation", .cmd = hmp_block_job_set_speed, + .flags = "p", }, SRST @@ -122,6 +125,7 @@ ERST "\n\t\t\t if you want to abort the operation immediately" "\n\t\t\t instead of keep running until data is in sync)", .cmd = hmp_block_job_cancel, + .flags = "p", }, SRST @@ -135,6 +139,7 @@ ERST .params = "device", .help = "stop an active background block operation", .cmd = hmp_block_job_complete, + .flags = "p", }, SRST @@ -149,6 +154,7 @@ ERST .params = "device", .help = "pause an active background block operation", .cmd = hmp_block_job_pause, + .flags = "p", }, SRST @@ -162,6 +168,7 @@ ERST .params = "device", .help = "resume a paused background block operation", .cmd = hmp_block_job_resume, + .flags = "p", }, SRST @@ -1406,6 +1413,7 @@ ERST .params = "nbd_server_start [-a] [-w] host:port", .help = "serve block devices on the given host and port", .cmd = hmp_nbd_server_start, + .flags = "p", }, SRST ``nbd_server_start`` *host*:*port* @@ -1421,6 +1429,7 @@ ERST .params = "nbd_server_add [-w] device [name]", .help = "export a block device via NBD", .cmd = hmp_nbd_server_add, + .flags = "p", }, SRST ``nbd_server_add`` *device* [ *name* ] @@ -1436,6 +1445,7 @@ ERST .params = "nbd_server_remove [-f] name", .help = "remove an export previously exposed via NBD", .cmd = hmp_nbd_server_remove, + .flags = "p", }, SRST ``nbd_server_remove [-f]`` *name* @@ -1452,6 +1462,7 @@ ERST .params = "nbd_server_stop", .help = "stop serving block devices using the NBD protocol", .cmd = hmp_nbd_server_stop, + .flags = "p", }, SRST ``nbd_server_stop`` @@ -1481,6 +1492,7 @@ ERST .params = "getfd name", .help = "receive a file descriptor via SCM rights and assign it a name", .cmd = hmp_getfd, + .flags = "p", }, SRST @@ -1496,6 +1508,7 @@ ERST .params = "closefd name", .help = "close a file descriptor previously passed via SCM rights", .cmd = hmp_closefd, + .flags = "p", }, SRST @@ -1511,6 +1524,7 @@ ERST .params = "device bps bps_rd bps_wr iops iops_rd iops_wr", .help = "change I/O throttle limits for a block drive", .cmd = hmp_block_set_io_throttle, + .flags = "p", }, SRST diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index ab5a47aff5..20da121374 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -379,7 +379,8 @@ static void mch_update_smram(MCHPCIState *mch) memory_region_set_enabled(&mch->high_smram, false); } - if (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) { + if ((pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_T_EN) && + (pd->config[MCH_HOST_BRIDGE_SMRAM] & SMRAM_G_SMRAME)) { switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] & MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB: diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 15b458527e..e33e5207ab 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -249,12 +249,11 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, return 0; } -static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev, VirtIODevice *vdev) +static void virtio_ccw_reset_virtio(VirtioCcwDevice *dev) { CcwDevice *ccw_dev = CCW_DEVICE(dev); - virtio_ccw_stop_ioeventfd(dev); - virtio_reset(vdev); + virtio_bus_reset(&dev->bus); if (dev->indicators) { release_indicator(&dev->routes.adapter, dev->indicators); dev->indicators = NULL; @@ -359,7 +358,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); break; case CCW_CMD_VDEV_RESET: - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); ret = 0; break; case CCW_CMD_READ_FEAT: @@ -536,7 +535,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } if (virtio_set_status(vdev, status) == 0) { if (vdev->status == 0) { - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); } if (status & VIRTIO_CONFIG_S_DRIVER_OK) { virtio_ccw_start_ioeventfd(dev); @@ -921,10 +920,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); - virtio_ccw_reset_virtio(dev, vdev); + virtio_ccw_reset_virtio(dev); if (vdc->parent_reset) { vdc->parent_reset(d); } diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d7ec023adf..896feb37a1 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -104,6 +104,7 @@ void virtio_bus_reset(VirtioBusState *bus) VirtIODevice *vdev = virtio_bus_get_device(bus); DPRINTF("%s: reset device.\n", BUS(bus)->name); + virtio_bus_stop_ioeventfd(bus); if (vdev != NULL) { virtio_reset(vdev); } diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 688eccda94..d240efef97 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -72,12 +72,12 @@ static void virtio_mmio_soft_reset(VirtIOMMIOProxy *proxy) { int i; - if (proxy->legacy) { - return; - } + virtio_bus_reset(&proxy->bus); - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - proxy->vqs[i].enabled = 0; + if (!proxy->legacy) { + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + proxy->vqs[i].enabled = 0; + } } } @@ -376,7 +376,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, return; } if (value == 0) { - virtio_reset(vdev); + virtio_mmio_soft_reset(proxy); } else { virtio_queue_set_addr(vdev, vdev->queue_sel, value << proxy->guest_page_shift); @@ -432,7 +432,6 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, } if (vdev->status == 0) { - virtio_reset(vdev); virtio_mmio_soft_reset(proxy); } break; @@ -627,8 +626,8 @@ static void virtio_mmio_reset(DeviceState *d) VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); int i; - virtio_mmio_stop_ioeventfd(proxy); - virtio_bus_reset(&proxy->bus); + virtio_mmio_soft_reset(proxy); + proxy->host_features_sel = 0; proxy->guest_features_sel = 0; proxy->guest_page_shift = 0; @@ -637,7 +636,6 @@ static void virtio_mmio_reset(DeviceState *d) proxy->guest_features[0] = proxy->guest_features[1] = 0; for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - proxy->vqs[i].enabled = 0; proxy->vqs[i].num = 0; proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0; proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0; diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 0566ad7d00..45327f0b31 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1945,7 +1945,6 @@ static void virtio_pci_reset(DeviceState *qdev) PCIDevice *dev = PCI_DEVICE(qdev); int i; - virtio_pci_stop_ioeventfd(proxy); virtio_bus_reset(bus); msix_unuse_all_vectors(&proxy->pci_dev); diff --git a/include/monitor/hmp.h b/include/monitor/hmp.h index 96d014826a..2e89a97bd6 100644 --- a/include/monitor/hmp.h +++ b/include/monitor/hmp.h @@ -133,5 +133,6 @@ void hmp_info_dirty_rate(Monitor *mon, const QDict *qdict); void hmp_calc_dirty_rate(Monitor *mon, const QDict *qdict); void hmp_human_readable_text_helper(Monitor *mon, HumanReadableText *(*qmp_handler)(Error **)); +void hmp_info_stats(Monitor *mon, const QDict *qdict); #endif diff --git a/include/monitor/stats.h b/include/monitor/stats.h new file mode 100644 index 0000000000..fcf0983154 --- /dev/null +++ b/include/monitor/stats.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022 Oracle and/or its affiliates. + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + */ + +#ifndef STATS_H +#define STATS_H + +#include "qapi/qapi-types-stats.h" + +typedef void StatRetrieveFunc(StatsResultList **result, StatsTarget target, + strList *names, strList *targets, Error **errp); +typedef void SchemaRetrieveFunc(StatsSchemaList **result, Error **errp); + +/* + * Register callbacks for the QMP query-stats command. + * + * @provider: stats provider checked against QMP command arguments + * @stats_fn: routine to query stats: + * @schema_fn: routine to query stat schemas: + */ +void add_stats_callbacks(StatsProvider provider, + StatRetrieveFunc *stats_fn, + SchemaRetrieveFunc *schemas_fn); + +/* + * Helper routines for adding stats entries to the results lists. + */ +void add_stats_entry(StatsResultList **, StatsProvider, const char *id, + StatsList *stats_list); +void add_stats_schema(StatsSchemaList **, StatsProvider, StatsTarget, + StatsSchemaValueList *); + +/* + * True if a string matches the filter passed to the stats_fn callabck, + * false otherwise. + * + * Note that an empty list means no filtering, i.e. all strings will + * return true. + */ +bool apply_str_list_filter(const char *string, strList *list); + +#endif /* STATS_H */ diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 40e10e19a7..d3e532b64c 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -1,6 +1,24 @@ #ifndef QEMU_CUTILS_H #define QEMU_CUTILS_H +/* + * si_prefix: + * @exp10: exponent of 10, a multiple of 3 between -18 and 18 inclusive. + * + * Return a SI prefix (n, u, m, K, M, etc.) corresponding + * to the given exponent of 10. + */ +const char *si_prefix(unsigned int exp10); + +/* + * iec_binary_prefix: + * @exp2: exponent of 2, a multiple of 10 between 0 and 60 inclusive. + * + * Return an IEC binary prefix (Ki, Mi, etc.) corresponding + * to the given exponent of 2. + */ +const char *iec_binary_prefix(unsigned int exp2); + /** * pstrcpy: * @buf: buffer to copy string into diff --git a/meson.build b/meson.build index ca19ddc30c..9efcb175d1 100644 --- a/meson.build +++ b/meson.build @@ -209,9 +209,13 @@ if get_option('fuzzing') configure_file(output: 'instrumentation-filter', input: 'scripts/oss-fuzz/instrumentation-filter-template', copy: true) - add_global_arguments( - cc.get_supported_arguments('-fsanitize-coverage-allowlist=instrumentation-filter'), - native: false, language: ['c', 'cpp', 'objc']) + + if cc.compiles('int main () { return 0; }', + name: '-fsanitize-coverage-allowlist=/dev/null', + args: ['-fsanitize-coverage-allowlist=/dev/null'] ) + add_global_arguments('-fsanitize-coverage-allowlist=instrumentation-filter', + native: false, language: ['c', 'cpp', 'objc']) + endif if get_option('fuzzing_engine') == '' # Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the @@ -3767,21 +3771,24 @@ endif summary_info += {'strip binaries': get_option('strip')} summary_info += {'sparse': sparse} summary_info += {'mingw32 support': targetos == 'windows'} +summary(summary_info, bool_yn: true, section: 'Compilation') # snarf the cross-compilation information for tests +summary_info = {} +have_cross = false foreach target: target_dirs tcg_mak = meson.current_build_dir() / 'tests/tcg' / 'config-' + target + '.mak' if fs.exists(tcg_mak) config_cross_tcg = keyval.load(tcg_mak) - target = config_cross_tcg['TARGET_NAME'] - compiler = '' if 'CC' in config_cross_tcg - summary_info += {target + ' tests': config_cross_tcg['CC']} + summary_info += {config_cross_tcg['TARGET_NAME']: config_cross_tcg['CC']} + have_cross = true endif - endif + endif endforeach - -summary(summary_info, bool_yn: true, section: 'Compilation') +if have_cross + summary(summary_info, bool_yn: true, section: 'Cross compilers') +endif # Targets and accelerators summary_info = {} diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 622c783c32..47a27326ee 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -40,6 +40,7 @@ #include "qapi/qapi-commands-pci.h" #include "qapi/qapi-commands-rocker.h" #include "qapi/qapi-commands-run-state.h" +#include "qapi/qapi-commands-stats.h" #include "qapi/qapi-commands-tpm.h" #include "qapi/qapi-commands-ui.h" #include "qapi/qapi-visit-net.h" @@ -52,6 +53,7 @@ #include "ui/console.h" #include "qemu/cutils.h" #include "qemu/error-report.h" +#include "hw/core/cpu.h" #include "hw/intc/intc.h" #include "migration/snapshot.h" #include "migration/misc.h" @@ -2239,3 +2241,233 @@ void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict) } hmp_handle_error(mon, err); } + +static void print_stats_schema_value(Monitor *mon, StatsSchemaValue *value) +{ + const char *unit = NULL; + monitor_printf(mon, " %s (%s%s", value->name, StatsType_str(value->type), + value->has_unit || value->exponent ? ", " : ""); + + if (value->has_unit) { + if (value->unit == STATS_UNIT_SECONDS) { + unit = "s"; + } else if (value->unit == STATS_UNIT_BYTES) { + unit = "B"; + } + } + + if (unit && value->base == 10 && + value->exponent >= -18 && value->exponent <= 18 && + value->exponent % 3 == 0) { + monitor_printf(mon, "%s", si_prefix(value->exponent)); + } else if (unit && value->base == 2 && + value->exponent >= 0 && value->exponent <= 60 && + value->exponent % 10 == 0) { + + monitor_printf(mon, "%s", iec_binary_prefix(value->exponent)); + } else if (value->exponent) { + /* Use exponential notation and write the unit's English name */ + monitor_printf(mon, "* %d^%d%s", + value->base, value->exponent, + value->has_unit ? " " : ""); + unit = NULL; + } + + if (value->has_unit) { + monitor_printf(mon, "%s", unit ? unit : StatsUnit_str(value->unit)); + } + + /* Print bucket size for linear histograms */ + if (value->type == STATS_TYPE_LINEAR_HISTOGRAM && value->has_bucket_size) { + monitor_printf(mon, ", bucket size=%d", value->bucket_size); + } + monitor_printf(mon, ")"); +} + +static StatsSchemaValueList *find_schema_value_list( + StatsSchemaList *list, StatsProvider provider, + StatsTarget target) +{ + StatsSchemaList *node; + + for (node = list; node; node = node->next) { + if (node->value->provider == provider && + node->value->target == target) { + return node->value->stats; + } + } + return NULL; +} + +static void print_stats_results(Monitor *mon, StatsTarget target, + bool show_provider, + StatsResult *result, + StatsSchemaList *schema) +{ + /* Find provider schema */ + StatsSchemaValueList *schema_value_list = + find_schema_value_list(schema, result->provider, target); + StatsList *stats_list; + + if (!schema_value_list) { + monitor_printf(mon, "failed to find schema list for %s\n", + StatsProvider_str(result->provider)); + return; + } + + if (show_provider) { + monitor_printf(mon, "provider: %s\n", + StatsProvider_str(result->provider)); + } + + for (stats_list = result->stats; stats_list; + stats_list = stats_list->next, + schema_value_list = schema_value_list->next) { + + Stats *stats = stats_list->value; + StatsValue *stats_value = stats->value; + StatsSchemaValue *schema_value = schema_value_list->value; + + /* Find schema entry */ + while (!g_str_equal(stats->name, schema_value->name)) { + if (!schema_value_list->next) { + monitor_printf(mon, "failed to find schema entry for %s\n", + stats->name); + return; + } + schema_value_list = schema_value_list->next; + schema_value = schema_value_list->value; + } + + print_stats_schema_value(mon, schema_value); + + if (stats_value->type == QTYPE_QNUM) { + monitor_printf(mon, ": %" PRId64 "\n", stats_value->u.scalar); + } else if (stats_value->type == QTYPE_QLIST) { + uint64List *list; + int i; + + monitor_printf(mon, ": "); + for (list = stats_value->u.list, i = 1; + list; + list = list->next, i++) { + monitor_printf(mon, "[%d]=%" PRId64 " ", i, list->value); + } + monitor_printf(mon, "\n"); + } + } +} + +/* Create the StatsFilter that is needed for an "info stats" invocation. */ +static StatsFilter *stats_filter(StatsTarget target, const char *names, + int cpu_index, StatsProvider provider) +{ + StatsFilter *filter = g_malloc0(sizeof(*filter)); + StatsProvider provider_idx; + StatsRequestList *request_list = NULL; + + filter->target = target; + switch (target) { + case STATS_TARGET_VM: + break; + case STATS_TARGET_VCPU: + { + strList *vcpu_list = NULL; + CPUState *cpu = qemu_get_cpu(cpu_index); + char *canonical_path = object_get_canonical_path(OBJECT(cpu)); + + QAPI_LIST_PREPEND(vcpu_list, canonical_path); + filter->u.vcpu.has_vcpus = true; + filter->u.vcpu.vcpus = vcpu_list; + break; + } + default: + break; + } + + if (!names && provider == STATS_PROVIDER__MAX) { + return filter; + } + + /* + * "info stats" can only query either one or all the providers. Querying + * by name, but not by provider, requires the creation of one filter per + * provider. + */ + for (provider_idx = 0; provider_idx < STATS_PROVIDER__MAX; provider_idx++) { + if (provider == STATS_PROVIDER__MAX || provider == provider_idx) { + StatsRequest *request = g_new0(StatsRequest, 1); + request->provider = provider_idx; + if (names && !g_str_equal(names, "*")) { + request->has_names = true; + request->names = strList_from_comma_list(names); + } + QAPI_LIST_PREPEND(request_list, request); + } + } + + filter->has_providers = true; + filter->providers = request_list; + return filter; +} + +void hmp_info_stats(Monitor *mon, const QDict *qdict) +{ + const char *target_str = qdict_get_str(qdict, "target"); + const char *provider_str = qdict_get_try_str(qdict, "provider"); + const char *names = qdict_get_try_str(qdict, "names"); + + StatsProvider provider = STATS_PROVIDER__MAX; + StatsTarget target; + Error *err = NULL; + g_autoptr(StatsSchemaList) schema = NULL; + g_autoptr(StatsResultList) stats = NULL; + g_autoptr(StatsFilter) filter = NULL; + StatsResultList *entry; + + target = qapi_enum_parse(&StatsTarget_lookup, target_str, -1, &err); + if (err) { + monitor_printf(mon, "invalid stats target %s\n", target_str); + goto exit_no_print; + } + if (provider_str) { + provider = qapi_enum_parse(&StatsProvider_lookup, provider_str, -1, &err); + if (err) { + monitor_printf(mon, "invalid stats provider %s\n", provider_str); + goto exit_no_print; + } + } + + schema = qmp_query_stats_schemas(provider_str ? true : false, + provider, &err); + if (err) { + goto exit; + } + + switch (target) { + case STATS_TARGET_VM: + filter = stats_filter(target, names, -1, provider); + break; + case STATS_TARGET_VCPU: {} + int cpu_index = monitor_get_cpu_index(mon); + filter = stats_filter(target, names, cpu_index, provider); + break; + default: + abort(); + } + + stats = qmp_query_stats(filter, &err); + if (err) { + goto exit; + } + for (entry = stats; entry; entry = entry->next) { + print_stats_results(mon, target, provider_str == NULL, entry->value, schema); + } + +exit: + if (err) { + monitor_printf(mon, "%s\n", error_get_pretty(err)); + } +exit_no_print: + error_free(err); +} diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index 1ebb89f46c..7314cd813d 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -35,6 +35,7 @@ #include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-stats.h" #include "qapi/qapi-commands-ui.h" #include "qapi/type-helpers.h" #include "qapi/qmp/qerror.h" @@ -43,6 +44,7 @@ #include "hw/acpi/acpi_dev_interface.h" #include "hw/intc/intc.h" #include "hw/rdma/rdma.h" +#include "monitor/stats.h" NameInfo *qmp_query_name(Error **errp) { @@ -441,3 +443,156 @@ HumanReadableText *qmp_x_query_irq(Error **errp) return human_readable_text_from_str(buf); } + +typedef struct StatsCallbacks { + StatsProvider provider; + StatRetrieveFunc *stats_cb; + SchemaRetrieveFunc *schemas_cb; + QTAILQ_ENTRY(StatsCallbacks) next; +} StatsCallbacks; + +static QTAILQ_HEAD(, StatsCallbacks) stats_callbacks = + QTAILQ_HEAD_INITIALIZER(stats_callbacks); + +void add_stats_callbacks(StatsProvider provider, + StatRetrieveFunc *stats_fn, + SchemaRetrieveFunc *schemas_fn) +{ + StatsCallbacks *entry = g_new(StatsCallbacks, 1); + entry->provider = provider; + entry->stats_cb = stats_fn; + entry->schemas_cb = schemas_fn; + + QTAILQ_INSERT_TAIL(&stats_callbacks, entry, next); +} + +static bool invoke_stats_cb(StatsCallbacks *entry, + StatsResultList **stats_results, + StatsFilter *filter, StatsRequest *request, + Error **errp) +{ + strList *targets = NULL; + strList *names = NULL; + ERRP_GUARD(); + + if (request) { + if (request->provider != entry->provider) { + return true; + } + if (request->has_names && !request->names) { + return true; + } + names = request->has_names ? request->names : NULL; + } + + switch (filter->target) { + case STATS_TARGET_VM: + break; + case STATS_TARGET_VCPU: + if (filter->u.vcpu.has_vcpus) { + if (!filter->u.vcpu.vcpus) { + /* No targets allowed? Return no statistics. */ + return true; + } + targets = filter->u.vcpu.vcpus; + } + break; + default: + abort(); + } + + entry->stats_cb(stats_results, filter->target, names, targets, errp); + if (*errp) { + qapi_free_StatsResultList(*stats_results); + *stats_results = NULL; + return false; + } + return true; +} + +StatsResultList *qmp_query_stats(StatsFilter *filter, Error **errp) +{ + StatsResultList *stats_results = NULL; + StatsCallbacks *entry; + StatsRequestList *request; + + QTAILQ_FOREACH(entry, &stats_callbacks, next) { + if (filter->has_providers) { + for (request = filter->providers; request; request = request->next) { + if (!invoke_stats_cb(entry, &stats_results, filter, + request->value, errp)) { + break; + } + } + } else { + if (!invoke_stats_cb(entry, &stats_results, filter, NULL, errp)) { + break; + } + } + } + + return stats_results; +} + +StatsSchemaList *qmp_query_stats_schemas(bool has_provider, + StatsProvider provider, + Error **errp) +{ + StatsSchemaList *stats_results = NULL; + StatsCallbacks *entry; + ERRP_GUARD(); + + QTAILQ_FOREACH(entry, &stats_callbacks, next) { + if (!has_provider || provider == entry->provider) { + entry->schemas_cb(&stats_results, errp); + if (*errp) { + qapi_free_StatsSchemaList(stats_results); + return NULL; + } + } + } + + return stats_results; +} + +void add_stats_entry(StatsResultList **stats_results, StatsProvider provider, + const char *qom_path, StatsList *stats_list) +{ + StatsResult *entry = g_new0(StatsResult, 1); + + entry->provider = provider; + if (qom_path) { + entry->has_qom_path = true; + entry->qom_path = g_strdup(qom_path); + } + entry->stats = stats_list; + + QAPI_LIST_PREPEND(*stats_results, entry); +} + +void add_stats_schema(StatsSchemaList **schema_results, + StatsProvider provider, StatsTarget target, + StatsSchemaValueList *stats_list) +{ + StatsSchema *entry = g_new0(StatsSchema, 1); + + entry->provider = provider; + entry->target = target; + entry->stats = stats_list; + QAPI_LIST_PREPEND(*schema_results, entry); +} + +bool apply_str_list_filter(const char *string, strList *list) +{ + strList *str_list = NULL; + + if (!list) { + return true; + } + for (str_list = list; str_list; str_list = str_list->next) { + if (g_str_equal(string, str_list->value)) { + return true; + } + } + return false; +} diff --git a/qapi/block-core.json b/qapi/block-core.json index f0383c7925..457df16638 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -737,7 +737,8 @@ # } # ## -{ 'command': 'query-block', 'returns': ['BlockInfo'] } +{ 'command': 'query-block', 'returns': ['BlockInfo'], + 'allow-preconfig': true } ## # @BlockDeviceTimedStats: @@ -1113,7 +1114,8 @@ ## { 'command': 'query-blockstats', 'data': { '*query-nodes': 'bool' }, - 'returns': ['BlockStats'] } + 'returns': ['BlockStats'], + 'allow-preconfig': true } ## # @BlockdevOnError: @@ -1262,7 +1264,8 @@ # # Since: 1.1 ## -{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] } +{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'], + 'allow-preconfig': true } ## # @block_resize: @@ -1293,7 +1296,8 @@ 'data': { '*device': 'str', '*node-name': 'str', 'size': 'int' }, - 'coroutine': true } + 'coroutine': true, + 'allow-preconfig': true } ## # @NewImageMode: @@ -1509,7 +1513,8 @@ # ## { 'command': 'blockdev-snapshot-sync', - 'data': 'BlockdevSnapshotSync' } + 'data': 'BlockdevSnapshotSync', + 'allow-preconfig': true } ## # @blockdev-snapshot: @@ -1550,7 +1555,8 @@ ## { 'command': 'blockdev-snapshot', 'data': 'BlockdevSnapshot', - 'features': [ 'allow-write-only-overlay' ] } + 'features': [ 'allow-write-only-overlay' ], + 'allow-preconfig': true } ## # @change-backing-file: @@ -1582,7 +1588,8 @@ ## { 'command': 'change-backing-file', 'data': { 'device': 'str', 'image-node-name': 'str', - 'backing-file': 'str' } } + 'backing-file': 'str' }, + 'allow-preconfig': true } ## # @block-commit: @@ -1692,7 +1699,8 @@ '*backing-file': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @drive-backup: @@ -1721,7 +1729,8 @@ # ## { 'command': 'drive-backup', 'boxed': true, - 'data': 'DriveBackup', 'features': ['deprecated'] } + 'data': 'DriveBackup', 'features': ['deprecated'], + 'allow-preconfig': true } ## # @blockdev-backup: @@ -1747,7 +1756,8 @@ # ## { 'command': 'blockdev-backup', 'boxed': true, - 'data': 'BlockdevBackup' } + 'data': 'BlockdevBackup', + 'allow-preconfig': true } ## # @query-named-block-nodes: @@ -1813,7 +1823,8 @@ ## { 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ], - 'data': { '*flat': 'bool' } } + 'data': { '*flat': 'bool' }, + 'allow-preconfig': true } ## # @XDbgBlockGraphNodeType: @@ -1922,7 +1933,8 @@ # Since: 4.0 ## { 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph', - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @drive-mirror: @@ -1950,7 +1962,8 @@ # ## { 'command': 'drive-mirror', 'boxed': true, - 'data': 'DriveMirror' } + 'data': 'DriveMirror', + 'allow-preconfig': true } ## # @DriveMirror: @@ -2123,7 +2136,8 @@ # ## { 'command': 'block-dirty-bitmap-add', - 'data': 'BlockDirtyBitmapAdd' } + 'data': 'BlockDirtyBitmapAdd', + 'allow-preconfig': true } ## # @block-dirty-bitmap-remove: @@ -2147,7 +2161,8 @@ # ## { 'command': 'block-dirty-bitmap-remove', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-clear: @@ -2170,7 +2185,8 @@ # ## { 'command': 'block-dirty-bitmap-clear', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-enable: @@ -2191,7 +2207,8 @@ # ## { 'command': 'block-dirty-bitmap-enable', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-disable: @@ -2212,7 +2229,8 @@ # ## { 'command': 'block-dirty-bitmap-disable', - 'data': 'BlockDirtyBitmap' } + 'data': 'BlockDirtyBitmap', + 'allow-preconfig': true } ## # @block-dirty-bitmap-merge: @@ -2244,7 +2262,8 @@ # ## { 'command': 'block-dirty-bitmap-merge', - 'data': 'BlockDirtyBitmapMerge' } + 'data': 'BlockDirtyBitmapMerge', + 'allow-preconfig': true } ## # @BlockDirtyBitmapSha256: @@ -2275,7 +2294,8 @@ ## { 'command': 'x-debug-block-dirty-bitmap-sha256', 'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256', - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @blockdev-mirror: @@ -2361,7 +2381,8 @@ '*on-target-error': 'BlockdevOnError', '*filter-node-name': 'str', '*copy-mode': 'MirrorCopyMode', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @BlockIOThrottle: @@ -2663,7 +2684,8 @@ '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str', '*speed': 'int', '*on-error': 'BlockdevOnError', '*filter-node-name': 'str', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + 'allow-preconfig': true } ## # @block-job-set-speed: @@ -2687,7 +2709,8 @@ # Since: 1.1 ## { 'command': 'block-job-set-speed', - 'data': { 'device': 'str', 'speed': 'int' } } + 'data': { 'device': 'str', 'speed': 'int' }, + 'allow-preconfig': true } ## # @block-job-cancel: @@ -2726,7 +2749,8 @@ # # Since: 1.1 ## -{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' } } +{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' }, + 'allow-preconfig': true } ## # @block-job-pause: @@ -2750,7 +2774,8 @@ # # Since: 1.3 ## -{ 'command': 'block-job-pause', 'data': { 'device': 'str' } } +{ 'command': 'block-job-pause', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-resume: @@ -2772,7 +2797,8 @@ # # Since: 1.3 ## -{ 'command': 'block-job-resume', 'data': { 'device': 'str' } } +{ 'command': 'block-job-resume', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-complete: @@ -2800,7 +2826,8 @@ # # Since: 1.3 ## -{ 'command': 'block-job-complete', 'data': { 'device': 'str' } } +{ 'command': 'block-job-complete', 'data': { 'device': 'str' }, + 'allow-preconfig': true } ## # @block-job-dismiss: @@ -2820,7 +2847,8 @@ # # Since: 2.12 ## -{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } } +{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' }, + 'allow-preconfig': true } ## # @block-job-finalize: @@ -2838,7 +2866,8 @@ # # Since: 2.12 ## -{ 'command': 'block-job-finalize', 'data': { 'id': 'str' } } +{ 'command': 'block-job-finalize', 'data': { 'id': 'str' }, + 'allow-preconfig': true } ## # @BlockdevDiscardOptions: @@ -4354,7 +4383,8 @@ # <- { "return": {} } # ## -{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } +{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true, + 'allow-preconfig': true } ## # @blockdev-reopen: @@ -4398,7 +4428,8 @@ # Since: 6.1 ## { 'command': 'blockdev-reopen', - 'data': { 'options': ['BlockdevOptions'] } } + 'data': { 'options': ['BlockdevOptions'] }, + 'allow-preconfig': true } ## # @blockdev-del: @@ -4431,7 +4462,8 @@ # <- { "return": {} } # ## -{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } } +{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' }, + 'allow-preconfig': true } ## # @BlockdevCreateOptionsFile: @@ -4872,7 +4904,8 @@ ## { 'command': 'blockdev-create', 'data': { 'job-id': 'str', - 'options': 'BlockdevCreateOptions' } } + 'options': 'BlockdevCreateOptions' }, + 'allow-preconfig': true } ## # @BlockdevAmendOptionsLUKS: @@ -4944,7 +4977,8 @@ 'node-name': 'str', 'options': 'BlockdevAmendOptions', '*force': 'bool' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @BlockErrorAction: @@ -5294,7 +5328,8 @@ # ## { 'command': 'block-set-write-threshold', - 'data': { 'node-name': 'str', 'write-threshold': 'uint64' } } + 'data': { 'node-name': 'str', 'write-threshold': 'uint64' }, + 'allow-preconfig': true } ## # @x-blockdev-change: @@ -5355,7 +5390,8 @@ 'data' : { 'parent': 'str', '*child': 'str', '*node': 'str' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @x-blockdev-set-iothread: @@ -5397,7 +5433,8 @@ 'data' : { 'node-name': 'str', 'iothread': 'StrOrNull', '*force': 'bool' }, - 'features': [ 'unstable' ] } + 'features': [ 'unstable' ], + 'allow-preconfig': true } ## # @QuorumOpType: @@ -5529,7 +5566,8 @@ # ## { 'command': 'blockdev-snapshot-internal-sync', - 'data': 'BlockdevSnapshotInternal' } + 'data': 'BlockdevSnapshotInternal', + 'allow-preconfig': true } ## # @blockdev-snapshot-delete-internal-sync: @@ -5576,4 +5614,5 @@ ## { 'command': 'blockdev-snapshot-delete-internal-sync', 'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, - 'returns': 'SnapshotInfo' } + 'returns': 'SnapshotInfo', + 'allow-preconfig': true } diff --git a/qapi/block-export.json b/qapi/block-export.json index 0685cb8b9a..8afb1b65b3 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -65,7 +65,8 @@ 'data': { 'addr': 'SocketAddressLegacy', '*tls-creds': 'str', '*tls-authz': 'str', - '*max-connections': 'uint32' } } + '*max-connections': 'uint32' }, + 'allow-preconfig': true } ## # @BlockExportOptionsNbdBase: @@ -215,7 +216,8 @@ # Since: 1.3 ## { 'command': 'nbd-server-add', - 'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'] } + 'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'], + 'allow-preconfig': true } ## # @BlockExportRemoveMode: @@ -260,7 +262,8 @@ ## { 'command': 'nbd-server-remove', 'data': {'name': 'str', '*mode': 'BlockExportRemoveMode'}, - 'features': ['deprecated'] } + 'features': ['deprecated'], + 'allow-preconfig': true } ## # @nbd-server-stop: @@ -270,7 +273,8 @@ # # Since: 1.3 ## -{ 'command': 'nbd-server-stop' } +{ 'command': 'nbd-server-stop', + 'allow-preconfig': true } ## # @BlockExportType: @@ -342,7 +346,8 @@ # Since: 5.2 ## { 'command': 'block-export-add', - 'data': 'BlockExportOptions', 'boxed': true } + 'data': 'BlockExportOptions', 'boxed': true, + 'allow-preconfig': true } ## # @block-export-del: @@ -362,7 +367,8 @@ # Since: 5.2 ## { 'command': 'block-export-del', - 'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' } } + 'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' }, + 'allow-preconfig': true } ## # @BLOCK_EXPORT_DELETED: @@ -406,4 +412,5 @@ # # Since: 5.2 ## -{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'] } +{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'], + 'allow-preconfig': true } diff --git a/qapi/block.json b/qapi/block.json index 19326641ac..5fe068f903 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -496,7 +496,8 @@ # <- { "return": {} } ## { 'command': 'block_set_io_throttle', 'boxed': true, - 'data': 'BlockIOThrottle' } + 'data': 'BlockIOThrottle', + 'allow-preconfig': true } ## # @block-latency-histogram-set: @@ -572,4 +573,5 @@ '*boundaries': ['uint64'], '*boundaries-read': ['uint64'], '*boundaries-write': ['uint64'], - '*boundaries-flush': ['uint64'] } } + '*boundaries-flush': ['uint64'] }, + 'allow-preconfig': true } diff --git a/qapi/meson.build b/qapi/meson.build index 656ef0e039..fd5c93d643 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -46,6 +46,7 @@ qapi_all_modules = [ 'replay', 'run-state', 'sockets', + 'stats', 'trace', 'transaction', 'yank', diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 4912b9744e..92d7ecc52c 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -93,3 +93,4 @@ { 'include': 'audio.json' } { 'include': 'acpi.json' } { 'include': 'pci.json' } +{ 'include': 'stats.json' } diff --git a/qapi/stats.json b/qapi/stats.json new file mode 100644 index 0000000000..2f8bfe8fdb --- /dev/null +++ b/qapi/stats.json @@ -0,0 +1,249 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# Copyright (c) 2022 Oracle and/or its affiliates. +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +## +# = Statistics +## + +## +# @StatsType: +# +# Enumeration of statistics types +# +# @cumulative: stat is cumulative; value can only increase. +# @instant: stat is instantaneous; value can increase or decrease. +# @peak: stat is the peak value; value can only increase. +# @linear-histogram: stat is a linear histogram. +# @log2-histogram: stat is a logarithmic histogram, with one bucket +# for each power of two. +# +# Since: 7.1 +## +{ 'enum' : 'StatsType', + 'data' : [ 'cumulative', 'instant', 'peak', 'linear-histogram', + 'log2-histogram' ] } + +## +# @StatsUnit: +# +# Enumeration of unit of measurement for statistics +# +# @bytes: stat reported in bytes. +# @seconds: stat reported in seconds. +# @cycles: stat reported in clock cycles. +# +# Since: 7.1 +## +{ 'enum' : 'StatsUnit', + 'data' : [ 'bytes', 'seconds', 'cycles' ] } + +## +# @StatsProvider: +# +# Enumeration of statistics providers. +# +# Since: 7.1 +## +{ 'enum': 'StatsProvider', + 'data': [ 'kvm' ] } + +## +# @StatsTarget: +# +# The kinds of objects on which one can request statistics. +# +# @vm: statistics that apply to the entire virtual machine or +# the entire QEMU process. +# +# @vcpu: statistics that apply to a single virtual CPU. +# +# Since: 7.1 +## +{ 'enum': 'StatsTarget', + 'data': [ 'vm', 'vcpu' ] } + +## +# @StatsRequest: +# +# Indicates a set of statistics that should be returned by query-stats. +# +# @provider: provider for which to return statistics. + +# @names: statistics to be returned (all if omitted). +# +# Since: 7.1 +## +{ 'struct': 'StatsRequest', + 'data': { 'provider': 'StatsProvider', + '*names': [ 'str' ] } } + +## +# @StatsVCPUFilter: +# +# @vcpus: list of QOM paths for the desired vCPU objects. +# +# Since: 7.1 +## +{ 'struct': 'StatsVCPUFilter', + 'data': { '*vcpus': [ 'str' ] } } + +## +# @StatsFilter: +# +# The arguments to the query-stats command; specifies a target for which to +# request statistics and optionally the required subset of information for +# that target: +# - which vCPUs to request statistics for +# - which providers to request statistics from +# - which named values to return within each provider +# +# Since: 7.1 +## +{ 'union': 'StatsFilter', + 'base': { + 'target': 'StatsTarget', + '*providers': [ 'StatsRequest' ] }, + 'discriminator': 'target', + 'data': { 'vcpu': 'StatsVCPUFilter' } } + +## +# @StatsValue: +# +# @scalar: single unsigned 64-bit integers. +# @list: list of unsigned 64-bit integers (used for histograms). +# +# Since: 7.1 +## +{ 'alternate': 'StatsValue', + 'data': { 'scalar': 'uint64', + 'list': [ 'uint64' ] } } + +## +# @Stats: +# +# @name: name of stat. +# @value: stat value. +# +# Since: 7.1 +## +{ 'struct': 'Stats', + 'data': { 'name': 'str', + 'value' : 'StatsValue' } } + +## +# @StatsResult: +# +# @provider: provider for this set of statistics. +# +# @qom-path: Path to the object for which the statistics are returned, +# if the object is exposed in the QOM tree +# +# @stats: list of statistics. +# +# Since: 7.1 +## +{ 'struct': 'StatsResult', + 'data': { 'provider': 'StatsProvider', + '*qom-path': 'str', + 'stats': [ 'Stats' ] } } + +## +# @query-stats: +# +# Return runtime-collected statistics for objects such as the +# VM or its vCPUs. +# +# The arguments are a StatsFilter and specify the provider and objects +# to return statistics about. +# +# Returns: a list of StatsResult, one for each provider and object +# (e.g., for each vCPU). +# +# Since: 7.1 +## +{ 'command': 'query-stats', + 'data': 'StatsFilter', + 'boxed': true, + 'returns': [ 'StatsResult' ] } + +## +# @StatsSchemaValue: +# +# Schema for a single statistic. +# +# @name: name of the statistic; each element of the schema is uniquely +# identified by a target, a provider (both available in @StatsSchema) +# and the name. +# +# @type: kind of statistic. +# +# @unit: basic unit of measure for the statistic; if missing, the statistic +# is a simple number or counter. +# +# @base: base for the multiple of @unit in which the statistic is measured. +# Only present if @exponent is non-zero; @base and @exponent together +# form a SI prefix (e.g., _nano-_ for ``base=10`` and ``exponent=-9``) +# or IEC binary prefix (e.g. _kibi-_ for ``base=2`` and ``exponent=10``) +# +# @exponent: exponent for the multiple of @unit in which the statistic is +# expressed, or 0 for the basic unit +# +# @bucket-size: Present when @type is "linear-histogram", contains the width +# of each bucket of the histogram. +# +# Since: 7.1 +## +{ 'struct': 'StatsSchemaValue', + 'data': { 'name': 'str', + 'type': 'StatsType', + '*unit': 'StatsUnit', + '*base': 'int8', + 'exponent': 'int16', + '*bucket-size': 'uint32' } } + +## +# @StatsSchema: +# +# Schema for all available statistics for a provider and target. +# +# @provider: provider for this set of statistics. +# +# @target: the kind of object that can be queried through the provider. +# +# @stats: list of statistics. +# +# Since: 7.1 +## +{ 'struct': 'StatsSchema', + 'data': { 'provider': 'StatsProvider', + 'target': 'StatsTarget', + 'stats': [ 'StatsSchemaValue' ] } } + +## +# @query-stats-schemas: +# +# Return the schema for all available runtime-collected statistics. +# +# Note: runtime-collected statistics and their names fall outside QEMU's usual +# deprecation policies. QEMU will try to keep the set of available data +# stable, together with their names, but will not guarantee stability +# at all costs; the same is true of providers that source statistics +# externally, e.g. from Linux. For example, if the same value is being +# tracked with different names on different architectures or by different +# providers, one of them might be renamed. A statistic might go away if +# an algorithm is changed or some code is removed; changing a default +# might cause previously useful statistics to always report 0. Such +# changes, however, are expected to be rare. +# +# Since: 7.1 +## +{ 'command': 'query-stats-schemas', + 'data': { '*provider': 'StatsProvider' }, + 'returns': [ 'StatsSchema' ] } diff --git a/tests/unit/test-cutils.c b/tests/unit/test-cutils.c index 98671f1ac3..f5b780f012 100644 --- a/tests/unit/test-cutils.c +++ b/tests/unit/test-cutils.c @@ -2450,6 +2450,50 @@ static void test_qemu_strtosz_metric(void) g_assert(endptr == str + 7); } +static void test_freq_to_str(void) +{ + g_assert_cmpstr(freq_to_str(999), ==, "999 Hz"); + g_assert_cmpstr(freq_to_str(1000), ==, "1 KHz"); + g_assert_cmpstr(freq_to_str(1010), ==, "1.01 KHz"); +} + +static void test_size_to_str(void) +{ + g_assert_cmpstr(size_to_str(0), ==, "0 B"); + g_assert_cmpstr(size_to_str(1), ==, "1 B"); + g_assert_cmpstr(size_to_str(1016), ==, "0.992 KiB"); + g_assert_cmpstr(size_to_str(1024), ==, "1 KiB"); + g_assert_cmpstr(size_to_str(512ull << 20), ==, "512 MiB"); +} + +static void test_iec_binary_prefix(void) +{ + g_assert_cmpstr(iec_binary_prefix(0), ==, ""); + g_assert_cmpstr(iec_binary_prefix(10), ==, "Ki"); + g_assert_cmpstr(iec_binary_prefix(20), ==, "Mi"); + g_assert_cmpstr(iec_binary_prefix(30), ==, "Gi"); + g_assert_cmpstr(iec_binary_prefix(40), ==, "Ti"); + g_assert_cmpstr(iec_binary_prefix(50), ==, "Pi"); + g_assert_cmpstr(iec_binary_prefix(60), ==, "Ei"); +} + +static void test_si_prefix(void) +{ + g_assert_cmpstr(si_prefix(-18), ==, "a"); + g_assert_cmpstr(si_prefix(-15), ==, "f"); + g_assert_cmpstr(si_prefix(-12), ==, "p"); + g_assert_cmpstr(si_prefix(-9), ==, "n"); + g_assert_cmpstr(si_prefix(-6), ==, "u"); + g_assert_cmpstr(si_prefix(-3), ==, "m"); + g_assert_cmpstr(si_prefix(0), ==, ""); + g_assert_cmpstr(si_prefix(3), ==, "K"); + g_assert_cmpstr(si_prefix(6), ==, "M"); + g_assert_cmpstr(si_prefix(9), ==, "G"); + g_assert_cmpstr(si_prefix(12), ==, "T"); + g_assert_cmpstr(si_prefix(15), ==, "P"); + g_assert_cmpstr(si_prefix(18), ==, "E"); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -2729,5 +2773,13 @@ int main(int argc, char **argv) g_test_add_func("/cutils/strtosz/metric", test_qemu_strtosz_metric); + g_test_add_func("/cutils/size_to_str", + test_size_to_str); + g_test_add_func("/cutils/freq_to_str", + test_freq_to_str); + g_test_add_func("/cutils/iec_binary_prefix", + test_iec_binary_prefix); + g_test_add_func("/cutils/si_prefix", + test_si_prefix); return g_test_run(); } diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 588bc999cc..5f5b1fbfe6 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -1,8 +1,17 @@ # Makefile for VM tests -.PHONY: vm-build-all vm-clean-all +# Hack to allow running in an unconfigured build tree +ifeq ($(wildcard $(SRC_PATH)/config-host.mak),) +VM_PYTHON = PYTHONPATH=$(SRC_PATH)/python /usr/bin/env python3 +VM_VENV = +HOST_ARCH := $(shell uname -m) +else +VM_PYTHON = $(TESTS_PYTHON) +VM_VENV = check-venv +HOST_ARCH = $(ARCH) +endif -HOST_ARCH = $(if $(ARCH),$(ARCH),$(shell uname -m)) +.PHONY: vm-build-all vm-clean-all EFI_AARCH64 = $(wildcard $(BUILD_DIR)/pc-bios/edk2-aarch64-code.fd) @@ -85,10 +94,10 @@ vm-clean-all: $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ $(SRC_PATH)/tests/vm/basevm.py \ $(SRC_PATH)/tests/vm/Makefile.include \ - check-venv + $(VM_VENV) @mkdir -p $(IMAGES_DIR) $(call quiet-command, \ - $(TESTS_PYTHON) $< \ + $(VM_PYTHON) $< \ $(if $(V)$(DEBUG), --debug) \ $(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ @@ -100,11 +109,10 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ --build-image $@, \ " VM-IMAGE $*") - # Build in VM $(IMAGE) -vm-build-%: $(IMAGES_DIR)/%.img check-venv +vm-build-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(call quiet-command, \ - $(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \ + $(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(V)$(DEBUG), --debug) \ $(if $(DEBUG), --interactive) \ $(if $(J),--jobs $(J)) \ @@ -128,9 +136,9 @@ vm-boot-serial-%: $(IMAGES_DIR)/%.img -device virtio-net-pci,netdev=vnet \ || true -vm-boot-ssh-%: $(IMAGES_DIR)/%.img check-venv +vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(call quiet-command, \ - $(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \ + $(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \ $(if $(J),--jobs $(J)) \ $(if $(V)$(DEBUG), --debug) \ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ diff --git a/util/cutils.c b/util/cutils.c index a58bcfd80e..6d04e52907 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -872,6 +872,25 @@ int parse_debug_env(const char *name, int max, int initial) return debug; } +const char *si_prefix(unsigned int exp10) +{ + static const char *prefixes[] = { + "a", "f", "p", "n", "u", "m", "", "K", "M", "G", "T", "P", "E" + }; + + exp10 += 18; + assert(exp10 % 3 == 0 && exp10 / 3 < ARRAY_SIZE(prefixes)); + return prefixes[exp10 / 3]; +} + +const char *iec_binary_prefix(unsigned int exp2) +{ + static const char *prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; + + assert(exp2 % 10 == 0 && exp2 / 10 < ARRAY_SIZE(prefixes)); + return prefixes[exp2 / 10]; +} + /* * Return human readable string for size @val. * @val can be anything that uint64_t allows (no more than "16 EiB"). @@ -880,7 +899,6 @@ int parse_debug_env(const char *name, int max, int initial) */ char *size_to_str(uint64_t val) { - static const char *suffixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; uint64_t div; int i; @@ -891,25 +909,23 @@ char *size_to_str(uint64_t val) * (see e41b509d68afb1f for more info) */ frexp(val / (1000.0 / 1024.0), &i); - i = (i - 1) / 10; - div = 1ULL << (i * 10); + i = (i - 1) / 10 * 10; + div = 1ULL << i; - return g_strdup_printf("%0.3g %sB", (double)val / div, suffixes[i]); + return g_strdup_printf("%0.3g %sB", (double)val / div, iec_binary_prefix(i)); } char *freq_to_str(uint64_t freq_hz) { - static const char *const suffixes[] = { "", "K", "M", "G", "T", "P", "E" }; double freq = freq_hz; - size_t idx = 0; + size_t exp10 = 0; while (freq >= 1000.0) { freq /= 1000.0; - idx++; + exp10 += 3; } - assert(idx < ARRAY_SIZE(suffixes)); - return g_strdup_printf("%0.3g %sHz", freq, suffixes[idx]); + return g_strdup_printf("%0.3g %sHz", freq, si_prefix(exp10)); } int qemu_pstrcmp0(const char **str1, const char **str2) |