* 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-----

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>
This commit is contained in:
Richard Henderson 2022-06-16 07:13:04 -07:00
commit def6fd6c9c
25 changed files with 1368 additions and 115 deletions

View file

@ -186,16 +186,14 @@ include $(SRC_PATH)/tests/Makefile.include
all: recurse-all all: recurse-all
ROM_DIRS = $(addprefix pc-bios/, $(ROMS)) ROMS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROMS)))
ROM_DIRS_RULES=$(foreach t, all clean, $(addsuffix /$(t), $(ROM_DIRS))) .PHONY: $(ROMS_RULES)
# Only keep -O and -g cflags $(ROMS_RULES):
.PHONY: $(ROM_DIRS_RULES)
$(ROM_DIRS_RULES):
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),) $(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" TARGET_DIR="$(dir $@)" $(notdir $@),)
.PHONY: recurse-all recurse-clean .PHONY: recurse-all recurse-clean
recurse-all: $(addsuffix /all, $(ROM_DIRS)) recurse-all: $(addsuffix /all, $(ROMS))
recurse-clean: $(addsuffix /clean, $(ROM_DIRS)) recurse-clean: $(addsuffix /clean, $(ROMS))
###################################################################### ######################################################################

View file

@ -47,6 +47,7 @@
#include "kvm-cpus.h" #include "kvm-cpus.h"
#include "hw/boards.h" #include "hw/boards.h"
#include "monitor/stats.h"
/* This check must be after config-host.h is included */ /* This check must be after config-host.h is included */
#ifdef CONFIG_EVENTFD #ifdef CONFIG_EVENTFD
@ -2310,6 +2311,10 @@ bool kvm_dirty_ring_enabled(void)
return kvm_state->kvm_dirty_ring_size ? true : false; 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) static int kvm_init(MachineState *ms)
{ {
MachineClass *mc = MACHINE_GET_CLASS(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; return 0;
err: err:
@ -3697,3 +3707,396 @@ static void kvm_type_init(void)
} }
type_init(kvm_type_init); 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));
}

22
configure vendored
View file

@ -1351,13 +1351,6 @@ static THREAD int tls_var;
int main(void) { return tls_var; } int main(void) { return tls_var; }
EOF 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 "$static" = "yes"; then
if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then if test "$pie" != "no" && compile_prog "-Werror -fPIE -DPIE" "-static-pie"; then
CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS"
@ -1370,8 +1363,10 @@ if test "$static" = "yes"; then
pie="no" pie="no"
fi fi
elif test "$pie" = "no"; then elif test "$pie" = "no"; then
CONFIGURE_CFLAGS="$CFLAGS_NOPIE $CONFIGURE_CFLAGS" if compile_prog "-Werror -fno-pie" "-no-pie"; then
CONFIGURE_LDFLAGS="$LDFLAGS_NOPIE $CONFIGURE_LDFLAGS" CONFIGURE_CFLAGS="-fno-pie $CONFIGURE_CFLAGS"
CONFIGURE_LDFLAGS="-no-pie $CONFIGURE_LDFLAGS"
fi
elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then elif compile_prog "-Werror -fPIE -DPIE" "-pie"; then
CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS" CONFIGURE_CFLAGS="-fPIE -DPIE $CONFIGURE_CFLAGS"
CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS" CONFIGURE_LDFLAGS="-pie $CONFIGURE_LDFLAGS"
@ -2257,7 +2252,7 @@ if test -n "$target_cc" &&
fi fi
done done
if test -n "$ld_i386_emulation"; then if test -n "$ld_i386_emulation"; then
roms="optionrom" roms="pc-bios/optionrom"
config_mak=pc-bios/optionrom/config.mak config_mak=pc-bios/optionrom/config.mak
echo "# Automatically generated by configure - do not modify" > $config_mak echo "# Automatically generated by configure - do not modify" > $config_mak
echo "TOPSRC_DIR=$source_path" >> $config_mak echo "TOPSRC_DIR=$source_path" >> $config_mak
@ -2268,7 +2263,7 @@ fi
probe_target_compilers ppc ppc64 probe_target_compilers ppc ppc64
if test -n "$target_cc" && test "$softmmu" = yes; then if test -n "$target_cc" && test "$softmmu" = yes; then
roms="$roms vof" roms="$roms pc-bios/vof"
config_mak=pc-bios/vof/config.mak config_mak=pc-bios/vof/config.mak
echo "# Automatically generated by configure - do not modify" > $config_mak echo "# Automatically generated by configure - do not modify" > $config_mak
echo "SRC_DIR=$source_path/pc-bios/vof" >> $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 "WARNING: Your compiler does not support the z900!"
echo " The s390-ccw bios will only work with guest CPUs >= z10." echo " The s390-ccw bios will only work with guest CPUs >= z10."
fi fi
roms="$roms s390-ccw" roms="$roms pc-bios/s390-ccw"
config_mak=pc-bios/s390-ccw/config-host.mak config_mak=pc-bios/s390-ccw/config-host.mak
echo "# Automatically generated by configure - do not modify" > $config_mak echo "# Automatically generated by configure - do not modify" > $config_mak
echo "SRC_PATH=$source_path/pc-bios/s390-ccw" >> $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 CFLAGS
preserve_env CXX preserve_env CXX
preserve_env CXXFLAGS preserve_env CXXFLAGS
preserve_env INSTALL
preserve_env LD preserve_env LD
preserve_env LDFLAGS preserve_env LDFLAGS
preserve_env LD_LIBRARY_PATH preserve_env LD_LIBRARY_PATH
preserve_env LIBTOOL
preserve_env MAKE preserve_env MAKE
preserve_env NM preserve_env NM
preserve_env OBJCFLAGS
preserve_env OBJCOPY preserve_env OBJCOPY
preserve_env PATH preserve_env PATH
preserve_env PKG_CONFIG preserve_env PKG_CONFIG

View file

@ -894,3 +894,17 @@ SRST
``info via`` ``info via``
Show guest mos6522 VIA devices. Show guest mos6522 VIA devices.
ERST 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

View file

@ -78,6 +78,7 @@ ERST
.help = "resize a block image", .help = "resize a block image",
.cmd = hmp_block_resize, .cmd = hmp_block_resize,
.coroutine = true, .coroutine = true,
.flags = "p",
}, },
SRST SRST
@ -94,6 +95,7 @@ ERST
.params = "device [speed [base]]", .params = "device [speed [base]]",
.help = "copy data from a backing file into a block device", .help = "copy data from a backing file into a block device",
.cmd = hmp_block_stream, .cmd = hmp_block_stream,
.flags = "p",
}, },
SRST SRST
@ -107,6 +109,7 @@ ERST
.params = "device speed", .params = "device speed",
.help = "set maximum speed for a background block operation", .help = "set maximum speed for a background block operation",
.cmd = hmp_block_job_set_speed, .cmd = hmp_block_job_set_speed,
.flags = "p",
}, },
SRST SRST
@ -122,6 +125,7 @@ ERST
"\n\t\t\t if you want to abort the operation immediately" "\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)", "\n\t\t\t instead of keep running until data is in sync)",
.cmd = hmp_block_job_cancel, .cmd = hmp_block_job_cancel,
.flags = "p",
}, },
SRST SRST
@ -135,6 +139,7 @@ ERST
.params = "device", .params = "device",
.help = "stop an active background block operation", .help = "stop an active background block operation",
.cmd = hmp_block_job_complete, .cmd = hmp_block_job_complete,
.flags = "p",
}, },
SRST SRST
@ -149,6 +154,7 @@ ERST
.params = "device", .params = "device",
.help = "pause an active background block operation", .help = "pause an active background block operation",
.cmd = hmp_block_job_pause, .cmd = hmp_block_job_pause,
.flags = "p",
}, },
SRST SRST
@ -162,6 +168,7 @@ ERST
.params = "device", .params = "device",
.help = "resume a paused background block operation", .help = "resume a paused background block operation",
.cmd = hmp_block_job_resume, .cmd = hmp_block_job_resume,
.flags = "p",
}, },
SRST SRST
@ -1406,6 +1413,7 @@ ERST
.params = "nbd_server_start [-a] [-w] host:port", .params = "nbd_server_start [-a] [-w] host:port",
.help = "serve block devices on the given host and port", .help = "serve block devices on the given host and port",
.cmd = hmp_nbd_server_start, .cmd = hmp_nbd_server_start,
.flags = "p",
}, },
SRST SRST
``nbd_server_start`` *host*:*port* ``nbd_server_start`` *host*:*port*
@ -1421,6 +1429,7 @@ ERST
.params = "nbd_server_add [-w] device [name]", .params = "nbd_server_add [-w] device [name]",
.help = "export a block device via NBD", .help = "export a block device via NBD",
.cmd = hmp_nbd_server_add, .cmd = hmp_nbd_server_add,
.flags = "p",
}, },
SRST SRST
``nbd_server_add`` *device* [ *name* ] ``nbd_server_add`` *device* [ *name* ]
@ -1436,6 +1445,7 @@ ERST
.params = "nbd_server_remove [-f] name", .params = "nbd_server_remove [-f] name",
.help = "remove an export previously exposed via NBD", .help = "remove an export previously exposed via NBD",
.cmd = hmp_nbd_server_remove, .cmd = hmp_nbd_server_remove,
.flags = "p",
}, },
SRST SRST
``nbd_server_remove [-f]`` *name* ``nbd_server_remove [-f]`` *name*
@ -1452,6 +1462,7 @@ ERST
.params = "nbd_server_stop", .params = "nbd_server_stop",
.help = "stop serving block devices using the NBD protocol", .help = "stop serving block devices using the NBD protocol",
.cmd = hmp_nbd_server_stop, .cmd = hmp_nbd_server_stop,
.flags = "p",
}, },
SRST SRST
``nbd_server_stop`` ``nbd_server_stop``
@ -1481,6 +1492,7 @@ ERST
.params = "getfd name", .params = "getfd name",
.help = "receive a file descriptor via SCM rights and assign it a name", .help = "receive a file descriptor via SCM rights and assign it a name",
.cmd = hmp_getfd, .cmd = hmp_getfd,
.flags = "p",
}, },
SRST SRST
@ -1496,6 +1508,7 @@ ERST
.params = "closefd name", .params = "closefd name",
.help = "close a file descriptor previously passed via SCM rights", .help = "close a file descriptor previously passed via SCM rights",
.cmd = hmp_closefd, .cmd = hmp_closefd,
.flags = "p",
}, },
SRST SRST
@ -1511,6 +1524,7 @@ ERST
.params = "device bps bps_rd bps_wr iops iops_rd iops_wr", .params = "device bps bps_rd bps_wr iops iops_rd iops_wr",
.help = "change I/O throttle limits for a block drive", .help = "change I/O throttle limits for a block drive",
.cmd = hmp_block_set_io_throttle, .cmd = hmp_block_set_io_throttle,
.flags = "p",
}, },
SRST SRST

View file

@ -379,7 +379,8 @@ static void mch_update_smram(MCHPCIState *mch)
memory_region_set_enabled(&mch->high_smram, false); 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] & switch (pd->config[MCH_HOST_BRIDGE_ESMRAMC] &
MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) {
case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB: case MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB:

View file

@ -249,12 +249,11 @@ static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info,
return 0; 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); CcwDevice *ccw_dev = CCW_DEVICE(dev);
virtio_ccw_stop_ioeventfd(dev); virtio_bus_reset(&dev->bus);
virtio_reset(vdev);
if (dev->indicators) { if (dev->indicators) {
release_indicator(&dev->routes.adapter, dev->indicators); release_indicator(&dev->routes.adapter, dev->indicators);
dev->indicators = NULL; 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); ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1);
break; break;
case CCW_CMD_VDEV_RESET: case CCW_CMD_VDEV_RESET:
virtio_ccw_reset_virtio(dev, vdev); virtio_ccw_reset_virtio(dev);
ret = 0; ret = 0;
break; break;
case CCW_CMD_READ_FEAT: 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 (virtio_set_status(vdev, status) == 0) {
if (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) { if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
virtio_ccw_start_ioeventfd(dev); 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) static void virtio_ccw_reset(DeviceState *d)
{ {
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); VirtIOCCWDeviceClass *vdc = VIRTIO_CCW_DEVICE_GET_CLASS(dev);
virtio_ccw_reset_virtio(dev, vdev); virtio_ccw_reset_virtio(dev);
if (vdc->parent_reset) { if (vdc->parent_reset) {
vdc->parent_reset(d); vdc->parent_reset(d);
} }

View file

@ -104,6 +104,7 @@ void virtio_bus_reset(VirtioBusState *bus)
VirtIODevice *vdev = virtio_bus_get_device(bus); VirtIODevice *vdev = virtio_bus_get_device(bus);
DPRINTF("%s: reset device.\n", BUS(bus)->name); DPRINTF("%s: reset device.\n", BUS(bus)->name);
virtio_bus_stop_ioeventfd(bus);
if (vdev != NULL) { if (vdev != NULL) {
virtio_reset(vdev); virtio_reset(vdev);
} }

View file

@ -72,12 +72,12 @@ static void virtio_mmio_soft_reset(VirtIOMMIOProxy *proxy)
{ {
int i; int i;
if (proxy->legacy) { virtio_bus_reset(&proxy->bus);
return;
}
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { if (!proxy->legacy) {
proxy->vqs[i].enabled = 0; 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; return;
} }
if (value == 0) { if (value == 0) {
virtio_reset(vdev); virtio_mmio_soft_reset(proxy);
} else { } else {
virtio_queue_set_addr(vdev, vdev->queue_sel, virtio_queue_set_addr(vdev, vdev->queue_sel,
value << proxy->guest_page_shift); 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) { if (vdev->status == 0) {
virtio_reset(vdev);
virtio_mmio_soft_reset(proxy); virtio_mmio_soft_reset(proxy);
} }
break; break;
@ -627,8 +626,8 @@ static void virtio_mmio_reset(DeviceState *d)
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d); VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
int i; int i;
virtio_mmio_stop_ioeventfd(proxy); virtio_mmio_soft_reset(proxy);
virtio_bus_reset(&proxy->bus);
proxy->host_features_sel = 0; proxy->host_features_sel = 0;
proxy->guest_features_sel = 0; proxy->guest_features_sel = 0;
proxy->guest_page_shift = 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; proxy->guest_features[0] = proxy->guest_features[1] = 0;
for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
proxy->vqs[i].enabled = 0;
proxy->vqs[i].num = 0; proxy->vqs[i].num = 0;
proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0; proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0;
proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0; proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0;

View file

@ -1945,7 +1945,6 @@ static void virtio_pci_reset(DeviceState *qdev)
PCIDevice *dev = PCI_DEVICE(qdev); PCIDevice *dev = PCI_DEVICE(qdev);
int i; int i;
virtio_pci_stop_ioeventfd(proxy);
virtio_bus_reset(bus); virtio_bus_reset(bus);
msix_unuse_all_vectors(&proxy->pci_dev); msix_unuse_all_vectors(&proxy->pci_dev);

View file

@ -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_calc_dirty_rate(Monitor *mon, const QDict *qdict);
void hmp_human_readable_text_helper(Monitor *mon, void hmp_human_readable_text_helper(Monitor *mon,
HumanReadableText *(*qmp_handler)(Error **)); HumanReadableText *(*qmp_handler)(Error **));
void hmp_info_stats(Monitor *mon, const QDict *qdict);
#endif #endif

45
include/monitor/stats.h Normal file
View file

@ -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 */

View file

@ -1,6 +1,24 @@
#ifndef QEMU_CUTILS_H #ifndef QEMU_CUTILS_H
#define 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: * pstrcpy:
* @buf: buffer to copy string into * @buf: buffer to copy string into

View file

@ -209,9 +209,13 @@ if get_option('fuzzing')
configure_file(output: 'instrumentation-filter', configure_file(output: 'instrumentation-filter',
input: 'scripts/oss-fuzz/instrumentation-filter-template', input: 'scripts/oss-fuzz/instrumentation-filter-template',
copy: true) copy: true)
add_global_arguments(
cc.get_supported_arguments('-fsanitize-coverage-allowlist=instrumentation-filter'), if cc.compiles('int main () { return 0; }',
native: false, language: ['c', 'cpp', 'objc']) 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') == '' if get_option('fuzzing_engine') == ''
# Add CFLAGS to tell clang to add fuzzer-related instrumentation to all the # 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 += {'strip binaries': get_option('strip')}
summary_info += {'sparse': sparse} summary_info += {'sparse': sparse}
summary_info += {'mingw32 support': targetos == 'windows'} summary_info += {'mingw32 support': targetos == 'windows'}
summary(summary_info, bool_yn: true, section: 'Compilation')
# snarf the cross-compilation information for tests # snarf the cross-compilation information for tests
summary_info = {}
have_cross = false
foreach target: target_dirs foreach target: target_dirs
tcg_mak = meson.current_build_dir() / 'tests/tcg' / 'config-' + target + '.mak' tcg_mak = meson.current_build_dir() / 'tests/tcg' / 'config-' + target + '.mak'
if fs.exists(tcg_mak) if fs.exists(tcg_mak)
config_cross_tcg = keyval.load(tcg_mak) config_cross_tcg = keyval.load(tcg_mak)
target = config_cross_tcg['TARGET_NAME']
compiler = ''
if 'CC' in config_cross_tcg 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 endif
endforeach endforeach
if have_cross
summary(summary_info, bool_yn: true, section: 'Compilation') summary(summary_info, bool_yn: true, section: 'Cross compilers')
endif
# Targets and accelerators # Targets and accelerators
summary_info = {} summary_info = {}

View file

@ -40,6 +40,7 @@
#include "qapi/qapi-commands-pci.h" #include "qapi/qapi-commands-pci.h"
#include "qapi/qapi-commands-rocker.h" #include "qapi/qapi-commands-rocker.h"
#include "qapi/qapi-commands-run-state.h" #include "qapi/qapi-commands-run-state.h"
#include "qapi/qapi-commands-stats.h"
#include "qapi/qapi-commands-tpm.h" #include "qapi/qapi-commands-tpm.h"
#include "qapi/qapi-commands-ui.h" #include "qapi/qapi-commands-ui.h"
#include "qapi/qapi-visit-net.h" #include "qapi/qapi-visit-net.h"
@ -52,6 +53,7 @@
#include "ui/console.h" #include "ui/console.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/core/cpu.h"
#include "hw/intc/intc.h" #include "hw/intc/intc.h"
#include "migration/snapshot.h" #include "migration/snapshot.h"
#include "migration/misc.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); 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);
}

View file

@ -35,6 +35,7 @@
#include "qapi/qapi-commands-control.h" #include "qapi/qapi-commands-control.h"
#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-machine.h"
#include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-misc.h"
#include "qapi/qapi-commands-stats.h"
#include "qapi/qapi-commands-ui.h" #include "qapi/qapi-commands-ui.h"
#include "qapi/type-helpers.h" #include "qapi/type-helpers.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
@ -43,6 +44,7 @@
#include "hw/acpi/acpi_dev_interface.h" #include "hw/acpi/acpi_dev_interface.h"
#include "hw/intc/intc.h" #include "hw/intc/intc.h"
#include "hw/rdma/rdma.h" #include "hw/rdma/rdma.h"
#include "monitor/stats.h"
NameInfo *qmp_query_name(Error **errp) NameInfo *qmp_query_name(Error **errp)
{ {
@ -441,3 +443,156 @@ HumanReadableText *qmp_x_query_irq(Error **errp)
return human_readable_text_from_str(buf); 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;
}

View file

@ -737,7 +737,8 @@
# } # }
# #
## ##
{ 'command': 'query-block', 'returns': ['BlockInfo'] } { 'command': 'query-block', 'returns': ['BlockInfo'],
'allow-preconfig': true }
## ##
# @BlockDeviceTimedStats: # @BlockDeviceTimedStats:
@ -1113,7 +1114,8 @@
## ##
{ 'command': 'query-blockstats', { 'command': 'query-blockstats',
'data': { '*query-nodes': 'bool' }, 'data': { '*query-nodes': 'bool' },
'returns': ['BlockStats'] } 'returns': ['BlockStats'],
'allow-preconfig': true }
## ##
# @BlockdevOnError: # @BlockdevOnError:
@ -1262,7 +1264,8 @@
# #
# Since: 1.1 # Since: 1.1
## ##
{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] } { 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'],
'allow-preconfig': true }
## ##
# @block_resize: # @block_resize:
@ -1293,7 +1296,8 @@
'data': { '*device': 'str', 'data': { '*device': 'str',
'*node-name': 'str', '*node-name': 'str',
'size': 'int' }, 'size': 'int' },
'coroutine': true } 'coroutine': true,
'allow-preconfig': true }
## ##
# @NewImageMode: # @NewImageMode:
@ -1509,7 +1513,8 @@
# #
## ##
{ 'command': 'blockdev-snapshot-sync', { 'command': 'blockdev-snapshot-sync',
'data': 'BlockdevSnapshotSync' } 'data': 'BlockdevSnapshotSync',
'allow-preconfig': true }
## ##
# @blockdev-snapshot: # @blockdev-snapshot:
@ -1550,7 +1555,8 @@
## ##
{ 'command': 'blockdev-snapshot', { 'command': 'blockdev-snapshot',
'data': 'BlockdevSnapshot', 'data': 'BlockdevSnapshot',
'features': [ 'allow-write-only-overlay' ] } 'features': [ 'allow-write-only-overlay' ],
'allow-preconfig': true }
## ##
# @change-backing-file: # @change-backing-file:
@ -1582,7 +1588,8 @@
## ##
{ 'command': 'change-backing-file', { 'command': 'change-backing-file',
'data': { 'device': 'str', 'image-node-name': 'str', 'data': { 'device': 'str', 'image-node-name': 'str',
'backing-file': 'str' } } 'backing-file': 'str' },
'allow-preconfig': true }
## ##
# @block-commit: # @block-commit:
@ -1692,7 +1699,8 @@
'*backing-file': 'str', '*speed': 'int', '*backing-file': 'str', '*speed': 'int',
'*on-error': 'BlockdevOnError', '*on-error': 'BlockdevOnError',
'*filter-node-name': 'str', '*filter-node-name': 'str',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } '*auto-finalize': 'bool', '*auto-dismiss': 'bool' },
'allow-preconfig': true }
## ##
# @drive-backup: # @drive-backup:
@ -1721,7 +1729,8 @@
# #
## ##
{ 'command': 'drive-backup', 'boxed': true, { 'command': 'drive-backup', 'boxed': true,
'data': 'DriveBackup', 'features': ['deprecated'] } 'data': 'DriveBackup', 'features': ['deprecated'],
'allow-preconfig': true }
## ##
# @blockdev-backup: # @blockdev-backup:
@ -1747,7 +1756,8 @@
# #
## ##
{ 'command': 'blockdev-backup', 'boxed': true, { 'command': 'blockdev-backup', 'boxed': true,
'data': 'BlockdevBackup' } 'data': 'BlockdevBackup',
'allow-preconfig': true }
## ##
# @query-named-block-nodes: # @query-named-block-nodes:
@ -1813,7 +1823,8 @@
## ##
{ 'command': 'query-named-block-nodes', { 'command': 'query-named-block-nodes',
'returns': [ 'BlockDeviceInfo' ], 'returns': [ 'BlockDeviceInfo' ],
'data': { '*flat': 'bool' } } 'data': { '*flat': 'bool' },
'allow-preconfig': true }
## ##
# @XDbgBlockGraphNodeType: # @XDbgBlockGraphNodeType:
@ -1922,7 +1933,8 @@
# Since: 4.0 # Since: 4.0
## ##
{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph', { 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph',
'features': [ 'unstable' ] } 'features': [ 'unstable' ],
'allow-preconfig': true }
## ##
# @drive-mirror: # @drive-mirror:
@ -1950,7 +1962,8 @@
# #
## ##
{ 'command': 'drive-mirror', 'boxed': true, { 'command': 'drive-mirror', 'boxed': true,
'data': 'DriveMirror' } 'data': 'DriveMirror',
'allow-preconfig': true }
## ##
# @DriveMirror: # @DriveMirror:
@ -2123,7 +2136,8 @@
# #
## ##
{ 'command': 'block-dirty-bitmap-add', { 'command': 'block-dirty-bitmap-add',
'data': 'BlockDirtyBitmapAdd' } 'data': 'BlockDirtyBitmapAdd',
'allow-preconfig': true }
## ##
# @block-dirty-bitmap-remove: # @block-dirty-bitmap-remove:
@ -2147,7 +2161,8 @@
# #
## ##
{ 'command': 'block-dirty-bitmap-remove', { 'command': 'block-dirty-bitmap-remove',
'data': 'BlockDirtyBitmap' } 'data': 'BlockDirtyBitmap',
'allow-preconfig': true }
## ##
# @block-dirty-bitmap-clear: # @block-dirty-bitmap-clear:
@ -2170,7 +2185,8 @@
# #
## ##
{ 'command': 'block-dirty-bitmap-clear', { 'command': 'block-dirty-bitmap-clear',
'data': 'BlockDirtyBitmap' } 'data': 'BlockDirtyBitmap',
'allow-preconfig': true }
## ##
# @block-dirty-bitmap-enable: # @block-dirty-bitmap-enable:
@ -2191,7 +2207,8 @@
# #
## ##
{ 'command': 'block-dirty-bitmap-enable', { 'command': 'block-dirty-bitmap-enable',
'data': 'BlockDirtyBitmap' } 'data': 'BlockDirtyBitmap',
'allow-preconfig': true }
## ##
# @block-dirty-bitmap-disable: # @block-dirty-bitmap-disable:
@ -2212,7 +2229,8 @@
# #
## ##
{ 'command': 'block-dirty-bitmap-disable', { 'command': 'block-dirty-bitmap-disable',
'data': 'BlockDirtyBitmap' } 'data': 'BlockDirtyBitmap',
'allow-preconfig': true }
## ##
# @block-dirty-bitmap-merge: # @block-dirty-bitmap-merge:
@ -2244,7 +2262,8 @@
# #
## ##
{ 'command': 'block-dirty-bitmap-merge', { 'command': 'block-dirty-bitmap-merge',
'data': 'BlockDirtyBitmapMerge' } 'data': 'BlockDirtyBitmapMerge',
'allow-preconfig': true }
## ##
# @BlockDirtyBitmapSha256: # @BlockDirtyBitmapSha256:
@ -2275,7 +2294,8 @@
## ##
{ 'command': 'x-debug-block-dirty-bitmap-sha256', { 'command': 'x-debug-block-dirty-bitmap-sha256',
'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256', 'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256',
'features': [ 'unstable' ] } 'features': [ 'unstable' ],
'allow-preconfig': true }
## ##
# @blockdev-mirror: # @blockdev-mirror:
@ -2361,7 +2381,8 @@
'*on-target-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError',
'*filter-node-name': 'str', '*filter-node-name': 'str',
'*copy-mode': 'MirrorCopyMode', '*copy-mode': 'MirrorCopyMode',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } '*auto-finalize': 'bool', '*auto-dismiss': 'bool' },
'allow-preconfig': true }
## ##
# @BlockIOThrottle: # @BlockIOThrottle:
@ -2663,7 +2684,8 @@
'*base-node': 'str', '*backing-file': 'str', '*bottom': 'str', '*base-node': 'str', '*backing-file': 'str', '*bottom': 'str',
'*speed': 'int', '*on-error': 'BlockdevOnError', '*speed': 'int', '*on-error': 'BlockdevOnError',
'*filter-node-name': 'str', '*filter-node-name': 'str',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } '*auto-finalize': 'bool', '*auto-dismiss': 'bool' },
'allow-preconfig': true }
## ##
# @block-job-set-speed: # @block-job-set-speed:
@ -2687,7 +2709,8 @@
# Since: 1.1 # Since: 1.1
## ##
{ 'command': 'block-job-set-speed', { 'command': 'block-job-set-speed',
'data': { 'device': 'str', 'speed': 'int' } } 'data': { 'device': 'str', 'speed': 'int' },
'allow-preconfig': true }
## ##
# @block-job-cancel: # @block-job-cancel:
@ -2726,7 +2749,8 @@
# #
# Since: 1.1 # 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: # @block-job-pause:
@ -2750,7 +2774,8 @@
# #
# Since: 1.3 # Since: 1.3
## ##
{ 'command': 'block-job-pause', 'data': { 'device': 'str' } } { 'command': 'block-job-pause', 'data': { 'device': 'str' },
'allow-preconfig': true }
## ##
# @block-job-resume: # @block-job-resume:
@ -2772,7 +2797,8 @@
# #
# Since: 1.3 # Since: 1.3
## ##
{ 'command': 'block-job-resume', 'data': { 'device': 'str' } } { 'command': 'block-job-resume', 'data': { 'device': 'str' },
'allow-preconfig': true }
## ##
# @block-job-complete: # @block-job-complete:
@ -2800,7 +2826,8 @@
# #
# Since: 1.3 # Since: 1.3
## ##
{ 'command': 'block-job-complete', 'data': { 'device': 'str' } } { 'command': 'block-job-complete', 'data': { 'device': 'str' },
'allow-preconfig': true }
## ##
# @block-job-dismiss: # @block-job-dismiss:
@ -2820,7 +2847,8 @@
# #
# Since: 2.12 # Since: 2.12
## ##
{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } } { 'command': 'block-job-dismiss', 'data': { 'id': 'str' },
'allow-preconfig': true }
## ##
# @block-job-finalize: # @block-job-finalize:
@ -2838,7 +2866,8 @@
# #
# Since: 2.12 # Since: 2.12
## ##
{ 'command': 'block-job-finalize', 'data': { 'id': 'str' } } { 'command': 'block-job-finalize', 'data': { 'id': 'str' },
'allow-preconfig': true }
## ##
# @BlockdevDiscardOptions: # @BlockdevDiscardOptions:
@ -4354,7 +4383,8 @@
# <- { "return": {} } # <- { "return": {} }
# #
## ##
{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } { 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true,
'allow-preconfig': true }
## ##
# @blockdev-reopen: # @blockdev-reopen:
@ -4398,7 +4428,8 @@
# Since: 6.1 # Since: 6.1
## ##
{ 'command': 'blockdev-reopen', { 'command': 'blockdev-reopen',
'data': { 'options': ['BlockdevOptions'] } } 'data': { 'options': ['BlockdevOptions'] },
'allow-preconfig': true }
## ##
# @blockdev-del: # @blockdev-del:
@ -4431,7 +4462,8 @@
# <- { "return": {} } # <- { "return": {} }
# #
## ##
{ 'command': 'blockdev-del', 'data': { 'node-name': 'str' } } { 'command': 'blockdev-del', 'data': { 'node-name': 'str' },
'allow-preconfig': true }
## ##
# @BlockdevCreateOptionsFile: # @BlockdevCreateOptionsFile:
@ -4872,7 +4904,8 @@
## ##
{ 'command': 'blockdev-create', { 'command': 'blockdev-create',
'data': { 'job-id': 'str', 'data': { 'job-id': 'str',
'options': 'BlockdevCreateOptions' } } 'options': 'BlockdevCreateOptions' },
'allow-preconfig': true }
## ##
# @BlockdevAmendOptionsLUKS: # @BlockdevAmendOptionsLUKS:
@ -4944,7 +4977,8 @@
'node-name': 'str', 'node-name': 'str',
'options': 'BlockdevAmendOptions', 'options': 'BlockdevAmendOptions',
'*force': 'bool' }, '*force': 'bool' },
'features': [ 'unstable' ] } 'features': [ 'unstable' ],
'allow-preconfig': true }
## ##
# @BlockErrorAction: # @BlockErrorAction:
@ -5294,7 +5328,8 @@
# #
## ##
{ 'command': 'block-set-write-threshold', { '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: # @x-blockdev-change:
@ -5355,7 +5390,8 @@
'data' : { 'parent': 'str', 'data' : { 'parent': 'str',
'*child': 'str', '*child': 'str',
'*node': 'str' }, '*node': 'str' },
'features': [ 'unstable' ] } 'features': [ 'unstable' ],
'allow-preconfig': true }
## ##
# @x-blockdev-set-iothread: # @x-blockdev-set-iothread:
@ -5397,7 +5433,8 @@
'data' : { 'node-name': 'str', 'data' : { 'node-name': 'str',
'iothread': 'StrOrNull', 'iothread': 'StrOrNull',
'*force': 'bool' }, '*force': 'bool' },
'features': [ 'unstable' ] } 'features': [ 'unstable' ],
'allow-preconfig': true }
## ##
# @QuorumOpType: # @QuorumOpType:
@ -5529,7 +5566,8 @@
# #
## ##
{ 'command': 'blockdev-snapshot-internal-sync', { 'command': 'blockdev-snapshot-internal-sync',
'data': 'BlockdevSnapshotInternal' } 'data': 'BlockdevSnapshotInternal',
'allow-preconfig': true }
## ##
# @blockdev-snapshot-delete-internal-sync: # @blockdev-snapshot-delete-internal-sync:
@ -5576,4 +5614,5 @@
## ##
{ 'command': 'blockdev-snapshot-delete-internal-sync', { 'command': 'blockdev-snapshot-delete-internal-sync',
'data': { 'device': 'str', '*id': 'str', '*name': 'str'}, 'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
'returns': 'SnapshotInfo' } 'returns': 'SnapshotInfo',
'allow-preconfig': true }

View file

@ -65,7 +65,8 @@
'data': { 'addr': 'SocketAddressLegacy', 'data': { 'addr': 'SocketAddressLegacy',
'*tls-creds': 'str', '*tls-creds': 'str',
'*tls-authz': 'str', '*tls-authz': 'str',
'*max-connections': 'uint32' } } '*max-connections': 'uint32' },
'allow-preconfig': true }
## ##
# @BlockExportOptionsNbdBase: # @BlockExportOptionsNbdBase:
@ -215,7 +216,8 @@
# Since: 1.3 # Since: 1.3
## ##
{ 'command': 'nbd-server-add', { 'command': 'nbd-server-add',
'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'] } 'data': 'NbdServerAddOptions', 'boxed': true, 'features': ['deprecated'],
'allow-preconfig': true }
## ##
# @BlockExportRemoveMode: # @BlockExportRemoveMode:
@ -260,7 +262,8 @@
## ##
{ 'command': 'nbd-server-remove', { 'command': 'nbd-server-remove',
'data': {'name': 'str', '*mode': 'BlockExportRemoveMode'}, 'data': {'name': 'str', '*mode': 'BlockExportRemoveMode'},
'features': ['deprecated'] } 'features': ['deprecated'],
'allow-preconfig': true }
## ##
# @nbd-server-stop: # @nbd-server-stop:
@ -270,7 +273,8 @@
# #
# Since: 1.3 # Since: 1.3
## ##
{ 'command': 'nbd-server-stop' } { 'command': 'nbd-server-stop',
'allow-preconfig': true }
## ##
# @BlockExportType: # @BlockExportType:
@ -342,7 +346,8 @@
# Since: 5.2 # Since: 5.2
## ##
{ 'command': 'block-export-add', { 'command': 'block-export-add',
'data': 'BlockExportOptions', 'boxed': true } 'data': 'BlockExportOptions', 'boxed': true,
'allow-preconfig': true }
## ##
# @block-export-del: # @block-export-del:
@ -362,7 +367,8 @@
# Since: 5.2 # Since: 5.2
## ##
{ 'command': 'block-export-del', { 'command': 'block-export-del',
'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' } } 'data': { 'id': 'str', '*mode': 'BlockExportRemoveMode' },
'allow-preconfig': true }
## ##
# @BLOCK_EXPORT_DELETED: # @BLOCK_EXPORT_DELETED:
@ -406,4 +412,5 @@
# #
# Since: 5.2 # Since: 5.2
## ##
{ 'command': 'query-block-exports', 'returns': ['BlockExportInfo'] } { 'command': 'query-block-exports', 'returns': ['BlockExportInfo'],
'allow-preconfig': true }

View file

@ -496,7 +496,8 @@
# <- { "return": {} } # <- { "return": {} }
## ##
{ 'command': 'block_set_io_throttle', 'boxed': true, { 'command': 'block_set_io_throttle', 'boxed': true,
'data': 'BlockIOThrottle' } 'data': 'BlockIOThrottle',
'allow-preconfig': true }
## ##
# @block-latency-histogram-set: # @block-latency-histogram-set:
@ -572,4 +573,5 @@
'*boundaries': ['uint64'], '*boundaries': ['uint64'],
'*boundaries-read': ['uint64'], '*boundaries-read': ['uint64'],
'*boundaries-write': ['uint64'], '*boundaries-write': ['uint64'],
'*boundaries-flush': ['uint64'] } } '*boundaries-flush': ['uint64'] },
'allow-preconfig': true }

View file

@ -46,6 +46,7 @@ qapi_all_modules = [
'replay', 'replay',
'run-state', 'run-state',
'sockets', 'sockets',
'stats',
'trace', 'trace',
'transaction', 'transaction',
'yank', 'yank',

View file

@ -93,3 +93,4 @@
{ 'include': 'audio.json' } { 'include': 'audio.json' }
{ 'include': 'acpi.json' } { 'include': 'acpi.json' }
{ 'include': 'pci.json' } { 'include': 'pci.json' }
{ 'include': 'stats.json' }

249
qapi/stats.json Normal file
View file

@ -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' ] }

View file

@ -2450,6 +2450,50 @@ static void test_qemu_strtosz_metric(void)
g_assert(endptr == str + 7); 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) int main(int argc, char **argv)
{ {
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
@ -2729,5 +2773,13 @@ int main(int argc, char **argv)
g_test_add_func("/cutils/strtosz/metric", g_test_add_func("/cutils/strtosz/metric",
test_qemu_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(); return g_test_run();
} }

View file

@ -1,8 +1,17 @@
# Makefile for VM tests # 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) 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/% \ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
$(SRC_PATH)/tests/vm/basevm.py \ $(SRC_PATH)/tests/vm/basevm.py \
$(SRC_PATH)/tests/vm/Makefile.include \ $(SRC_PATH)/tests/vm/Makefile.include \
check-venv $(VM_VENV)
@mkdir -p $(IMAGES_DIR) @mkdir -p $(IMAGES_DIR)
$(call quiet-command, \ $(call quiet-command, \
$(TESTS_PYTHON) $< \ $(VM_PYTHON) $< \
$(if $(V)$(DEBUG), --debug) \ $(if $(V)$(DEBUG), --debug) \
$(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \ $(if $(GENISOIMAGE),--genisoimage $(GENISOIMAGE)) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \
@ -100,11 +109,10 @@ $(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \
--build-image $@, \ --build-image $@, \
" VM-IMAGE $*") " VM-IMAGE $*")
# Build in VM $(IMAGE) # Build in VM $(IMAGE)
vm-build-%: $(IMAGES_DIR)/%.img check-venv vm-build-%: $(IMAGES_DIR)/%.img $(VM_VENV)
$(call quiet-command, \ $(call quiet-command, \
$(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \ $(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \
$(if $(V)$(DEBUG), --debug) \ $(if $(V)$(DEBUG), --debug) \
$(if $(DEBUG), --interactive) \ $(if $(DEBUG), --interactive) \
$(if $(J),--jobs $(J)) \ $(if $(J),--jobs $(J)) \
@ -128,9 +136,9 @@ vm-boot-serial-%: $(IMAGES_DIR)/%.img
-device virtio-net-pci,netdev=vnet \ -device virtio-net-pci,netdev=vnet \
|| true || true
vm-boot-ssh-%: $(IMAGES_DIR)/%.img check-venv vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(VM_VENV)
$(call quiet-command, \ $(call quiet-command, \
$(TESTS_PYTHON) $(SRC_PATH)/tests/vm/$* \ $(VM_PYTHON) $(SRC_PATH)/tests/vm/$* \
$(if $(J),--jobs $(J)) \ $(if $(J),--jobs $(J)) \
$(if $(V)$(DEBUG), --debug) \ $(if $(V)$(DEBUG), --debug) \
$(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \ $(if $(QEMU_LOCAL),--build-path $(BUILD_DIR)) \

View file

@ -872,6 +872,25 @@ int parse_debug_env(const char *name, int max, int initial)
return debug; 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. * Return human readable string for size @val.
* @val can be anything that uint64_t allows (no more than "16 EiB"). * @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) char *size_to_str(uint64_t val)
{ {
static const char *suffixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
uint64_t div; uint64_t div;
int i; int i;
@ -891,25 +909,23 @@ char *size_to_str(uint64_t val)
* (see e41b509d68afb1f for more info) * (see e41b509d68afb1f for more info)
*/ */
frexp(val / (1000.0 / 1024.0), &i); frexp(val / (1000.0 / 1024.0), &i);
i = (i - 1) / 10; i = (i - 1) / 10 * 10;
div = 1ULL << (i * 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) char *freq_to_str(uint64_t freq_hz)
{ {
static const char *const suffixes[] = { "", "K", "M", "G", "T", "P", "E" };
double freq = freq_hz; double freq = freq_hz;
size_t idx = 0; size_t exp10 = 0;
while (freq >= 1000.0) { while (freq >= 1000.0) {
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) int qemu_pstrcmp0(const char **str1, const char **str2)