diff options
46 files changed, 587 insertions, 393 deletions
diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index e74998efb9..be4d90cd9c 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -202,85 +202,6 @@ acceptance-system-opensuse: MAKE_CHECK_ARGS: check-acceptance -build-disabled: - extends: .native_build_job_template - needs: - job: amd64-fedora-container - variables: - IMAGE: fedora - CONFIGURE_ARGS: - --disable-attr - --disable-auth-pam - --disable-avx2 - --disable-bochs - --disable-brlapi - --disable-bzip2 - --disable-cap-ng - --disable-capstone - --disable-cloop - --disable-coroutine-pool - --disable-curl - --disable-curses - --disable-dmg - --disable-docs - --disable-gcrypt - --disable-glusterfs - --disable-gnutls - --disable-gtk - --disable-guest-agent - --disable-iconv - --disable-keyring - --disable-kvm - --disable-libiscsi - --disable-libpmem - --disable-libssh - --disable-libudev - --disable-libusb - --disable-libxml2 - --disable-linux-aio - --disable-live-block-migration - --disable-lzo - --disable-malloc-trim - --disable-mpath - --disable-nettle - --disable-numa - --disable-opengl - --disable-parallels - --disable-pie - --disable-qcow1 - --disable-qed - --disable-qom-cast-debug - --disable-rbd - --disable-rdma - --disable-replication - --disable-sdl - --disable-seccomp - --disable-slirp - --disable-smartcard - --disable-snappy - --disable-sparse - --disable-spice - --disable-strip - --disable-tpm - --disable-usb-redir - --disable-vdi - --disable-vhost-crypto - --disable-vhost-net - --disable-vhost-scsi - --disable-vhost-kernel - --disable-vhost-user - --disable-vhost-vdpa - --disable-vhost-vsock - --disable-virglrenderer - --disable-vnc - --disable-vte - --disable-vvfat - --disable-xen - --disable-zstd - TARGETS: arm-softmmu i386-softmmu ppc64-softmmu mips64-softmmu - s390x-softmmu i386-linux-user - MAKE_CHECK_ARGS: check-qtest SPEED=slow - # This jobs explicitly disable TCG (--disable-tcg), KVM is detected by # the configure script. The container doesn't contain Xen headers so # Xen accelerator is not detected / selected. As result it build the @@ -649,20 +570,26 @@ build-without-default-devices: build-without-default-features: extends: .native_build_job_template needs: - job: amd64-debian-container + job: amd64-fedora-container variables: - IMAGE: debian-amd64 - CONFIGURE_ARGS: --without-default-features --disable-user - --target-list-exclude=arm-softmmu,i386-softmmu,mipsel-softmmu,mips64-softmmu,ppc-softmmu - MAKE_CHECK_ARGS: check-unit + IMAGE: fedora + CONFIGURE_ARGS: + --without-default-features + --disable-capstone + --disable-fdt + --disable-pie + --disable-qom-cast-debug + --disable-slirp + --disable-strip + TARGETS: avr-softmmu i386-softmmu mips64-softmmu s390x-softmmu sh4-softmmu + sparc64-softmmu hexagon-linux-user i386-linux-user s390x-linux-user + MAKE_CHECK_ARGS: check-unit check-qtest SPEED=slow build-libvhost-user: stage: build image: $CI_REGISTRY_IMAGE/qemu/fedora:latest needs: job: amd64-fedora-container - before_script: - - dnf install -y meson ninja-build script: - mkdir subprojects/libvhost-user/build - cd subprojects/libvhost-user/build diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 564b94565d..0d3e4a7b4b 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -137,7 +137,7 @@ ubuntu-20.04-aarch64-all-linux-static: - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$S390X_RUNNER_AVAILABLE" + - if: "$AARCH64_RUNNER_AVAILABLE" script: # --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763 # --disable-glusterfs is needed because there's no static version of those libs in distro supplied packages @@ -157,7 +157,7 @@ ubuntu-20.04-aarch64-all: - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$S390X_RUNNER_AVAILABLE" + - if: "$AARCH64_RUNNER_AVAILABLE" script: - mkdir build - cd build @@ -174,7 +174,7 @@ ubuntu-20.04-aarch64-alldbg: - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$S390X_RUNNER_AVAILABLE" + - if: "$AARCH64_RUNNER_AVAILABLE" script: - mkdir build - cd build @@ -193,7 +193,7 @@ ubuntu-20.04-aarch64-clang: rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual - - if: "$S390X_RUNNER_AVAILABLE" + - if: "$AARCH64_RUNNER_AVAILABLE" when: manual script: - mkdir build @@ -211,7 +211,7 @@ ubuntu-20.04-aarch64-tci: - aarch64 rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - - if: "$S390X_RUNNER_AVAILABLE" + - if: "$AARCH64_RUNNER_AVAILABLE" script: - mkdir build - cd build @@ -228,7 +228,7 @@ ubuntu-20.04-aarch64-notcg: rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' when: manual - - if: "$S390X_RUNNER_AVAILABLE" + - if: "$AARCH64_RUNNER_AVAILABLE" when: manual script: - mkdir build @@ -3187,9 +3187,8 @@ glib_req_ver=2.56 glib_modules=gthread-2.0 if test "$modules" = yes; then glib_modules="$glib_modules gmodule-export-2.0" -fi -if test "$plugins" = "yes"; then - glib_modules="$glib_modules gmodule-2.0" +elif test "$plugins" = "yes"; then + glib_modules="$glib_modules gmodule-no-export-2.0" fi for i in $glib_modules; do diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index 066ea6d8ec..a1e03ca882 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -17,18 +17,12 @@ static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; static GHashTable *miss_ht; -static GMutex mtx; +static GMutex hashtable_lock; static GRand *rng; static int limit; static bool sys; -static uint64_t dmem_accesses; -static uint64_t dmisses; - -static uint64_t imem_accesses; -static uint64_t imisses; - enum EvictionPolicy { LRU, FIFO, @@ -80,6 +74,8 @@ typedef struct { int blksize_shift; uint64_t set_mask; uint64_t tag_mask; + uint64_t accesses; + uint64_t misses; } Cache; typedef struct { @@ -96,7 +92,16 @@ void (*update_miss)(Cache *cache, int set, int blk); void (*metadata_init)(Cache *cache); void (*metadata_destroy)(Cache *cache); -Cache *dcache, *icache; +static int cores; +static Cache **dcaches, **icaches; + +static GMutex *dcache_locks; +static GMutex *icache_locks; + +static uint64_t all_dmem_accesses; +static uint64_t all_imem_accesses; +static uint64_t all_imisses; +static uint64_t all_dmisses; static int pow_of_two(int num) { @@ -233,20 +238,24 @@ static bool bad_cache_params(int blksize, int assoc, int cachesize) static Cache *cache_init(int blksize, int assoc, int cachesize) { - if (bad_cache_params(blksize, assoc, cachesize)) { - return NULL; - } - Cache *cache; int i; uint64_t blk_mask; + /* + * This function shall not be called directly, and hence expects suitable + * parameters. + */ + g_assert(!bad_cache_params(blksize, assoc, cachesize)); + cache = g_new(Cache, 1); cache->assoc = assoc; cache->cachesize = cachesize; cache->num_sets = cachesize / (blksize * assoc); cache->sets = g_new(CacheSet, cache->num_sets); cache->blksize_shift = pow_of_two(blksize); + cache->accesses = 0; + cache->misses = 0; for (i = 0; i < cache->num_sets; i++) { cache->sets[i].blocks = g_new0(CacheBlock, assoc); @@ -263,6 +272,24 @@ static Cache *cache_init(int blksize, int assoc, int cachesize) return cache; } +static Cache **caches_init(int blksize, int assoc, int cachesize) +{ + Cache **caches; + int i; + + if (bad_cache_params(blksize, assoc, cachesize)) { + return NULL; + } + + caches = g_new(Cache *, cores); + + for (i = 0; i < cores; i++) { + caches[i] = cache_init(blksize, assoc, cachesize); + } + + return caches; +} + static int get_invalid_block(Cache *cache, uint64_t set) { int i; @@ -353,6 +380,7 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, { uint64_t effective_addr; struct qemu_plugin_hwaddr *hwaddr; + int cache_idx; InsnData *insn; hwaddr = qemu_plugin_get_hwaddr(info, vaddr); @@ -361,32 +389,35 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info, } effective_addr = hwaddr ? qemu_plugin_hwaddr_phys_addr(hwaddr) : vaddr; + cache_idx = vcpu_index % cores; - g_mutex_lock(&mtx); - if (!access_cache(dcache, effective_addr)) { + g_mutex_lock(&dcache_locks[cache_idx]); + if (!access_cache(dcaches[cache_idx], effective_addr)) { insn = (InsnData *) userdata; - insn->dmisses++; - dmisses++; + __atomic_fetch_add(&insn->dmisses, 1, __ATOMIC_SEQ_CST); + dcaches[cache_idx]->misses++; } - dmem_accesses++; - g_mutex_unlock(&mtx); + dcaches[cache_idx]->accesses++; + g_mutex_unlock(&dcache_locks[cache_idx]); } static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) { uint64_t insn_addr; InsnData *insn; + int cache_idx; - g_mutex_lock(&mtx); insn_addr = ((InsnData *) userdata)->addr; - if (!access_cache(icache, insn_addr)) { + cache_idx = vcpu_index % cores; + g_mutex_lock(&icache_locks[cache_idx]); + if (!access_cache(icaches[cache_idx], insn_addr)) { insn = (InsnData *) userdata; - insn->imisses++; - imisses++; + __atomic_fetch_add(&insn->imisses, 1, __ATOMIC_SEQ_CST); + icaches[cache_idx]->misses++; } - imem_accesses++; - g_mutex_unlock(&mtx); + icaches[cache_idx]->accesses++; + g_mutex_unlock(&icache_locks[cache_idx]); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -411,7 +442,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) * new entries for those instructions. Instead, we fetch the same * entry from the hash table and register it for the callback again. */ - g_mutex_lock(&mtx); + g_mutex_lock(&hashtable_lock); data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr)); if (data == NULL) { data = g_new0(InsnData, 1); @@ -421,7 +452,7 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr), (gpointer) data); } - g_mutex_unlock(&mtx); + g_mutex_unlock(&hashtable_lock); qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access, QEMU_PLUGIN_CB_NO_REGS, @@ -453,6 +484,15 @@ static void cache_free(Cache *cache) g_free(cache); } +static void caches_free(Cache **caches) +{ + int i; + + for (i = 0; i < cores; i++) { + cache_free(caches[i]); + } +} + static int dcmp(gconstpointer a, gconstpointer b) { InsnData *insn_a = (InsnData *) a; @@ -461,6 +501,37 @@ static int dcmp(gconstpointer a, gconstpointer b) return insn_a->dmisses < insn_b->dmisses ? 1 : -1; } +static void append_stats_line(GString *line, uint64_t daccess, uint64_t dmisses, + uint64_t iaccess, uint64_t imisses) +{ + double dmiss_rate, imiss_rate; + + dmiss_rate = ((double) dmisses) / (daccess) * 100.0; + imiss_rate = ((double) imisses) / (iaccess) * 100.0; + + g_string_append_printf(line, "%-14lu %-12lu %9.4lf%% %-14lu %-12lu" + " %9.4lf%%\n", + daccess, + dmisses, + daccess ? dmiss_rate : 0.0, + iaccess, + imisses, + iaccess ? imiss_rate : 0.0); +} + +static void sum_stats(void) +{ + int i; + + g_assert(cores > 1); + for (i = 0; i < cores; i++) { + all_imisses += icaches[i]->misses; + all_dmisses += dcaches[i]->misses; + all_imem_accesses += icaches[i]->accesses; + all_dmem_accesses += dcaches[i]->accesses; + } +} + static int icmp(gconstpointer a, gconstpointer b) { InsnData *insn_a = (InsnData *) a; @@ -471,19 +542,29 @@ static int icmp(gconstpointer a, gconstpointer b) static void log_stats(void) { - g_autoptr(GString) rep = g_string_new(""); - g_string_append_printf(rep, - "Data accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n", - dmem_accesses, - dmisses, - ((double) dmisses / (double) dmem_accesses) * 100.0); - - g_string_append_printf(rep, - "Instruction accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n", - imem_accesses, - imisses, - ((double) imisses / (double) imem_accesses) * 100.0); + int i; + Cache *icache, *dcache; + + g_autoptr(GString) rep = g_string_new("core #, data accesses, data misses," + " dmiss rate, insn accesses," + " insn misses, imiss rate\n"); + + for (i = 0; i < cores; i++) { + g_string_append_printf(rep, "%-8d", i); + dcache = dcaches[i]; + icache = icaches[i]; + append_stats_line(rep, dcache->accesses, dcache->misses, + icache->accesses, icache->misses); + } + + if (cores > 1) { + sum_stats(); + g_string_append_printf(rep, "%-8s", "sum"); + append_stats_line(rep, all_dmem_accesses, all_dmisses, + all_imem_accesses, all_imisses); + } + g_string_append(rep, "\n"); qemu_plugin_outs(rep->str); } @@ -530,8 +611,8 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) log_stats(); log_top_insns(); - cache_free(dcache); - cache_free(icache); + caches_free(dcaches); + caches_free(icaches); g_hash_table_destroy(miss_ht); } @@ -579,6 +660,8 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, policy = LRU; + cores = sys ? qemu_plugin_n_vcpus() : 1; + for (i = 0; i < argc; i++) { char *opt = argv[i]; if (g_str_has_prefix(opt, "iblksize=")) { @@ -595,6 +678,8 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, dcachesize = g_ascii_strtoll(opt + 11, NULL, 10); } else if (g_str_has_prefix(opt, "limit=")) { limit = g_ascii_strtoll(opt + 6, NULL, 10); + } else if (g_str_has_prefix(opt, "cores=")) { + cores = g_ascii_strtoll(opt + 6, NULL, 10); } else if (g_str_has_prefix(opt, "evict=")) { gchar *p = opt + 6; if (g_strcmp0(p, "rand") == 0) { @@ -615,22 +700,25 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, policy_init(); - dcache = cache_init(dblksize, dassoc, dcachesize); - if (!dcache) { + dcaches = caches_init(dblksize, dassoc, dcachesize); + if (!dcaches) { const char *err = cache_config_error(dblksize, dassoc, dcachesize); fprintf(stderr, "dcache cannot be constructed from given parameters\n"); fprintf(stderr, "%s\n", err); return -1; } - icache = cache_init(iblksize, iassoc, icachesize); - if (!icache) { + icaches = caches_init(iblksize, iassoc, icachesize); + if (!icaches) { const char *err = cache_config_error(iblksize, iassoc, icachesize); fprintf(stderr, "icache cannot be constructed from given parameters\n"); fprintf(stderr, "%s\n", err); return -1; } + dcache_locks = g_new0(GMutex, cores); + icache_locks = g_new0(GMutex, cores); + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index 2de9f0d7d4..a5275dcc15 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -67,7 +67,7 @@ static void vcpu_insn_exec(unsigned int cpu_index, void *udata) /* Print previous instruction in cache */ if (s->len) { qemu_plugin_outs(s->str); - qemu_plugin_outs("s\n"); + qemu_plugin_outs("\n"); } /* Store new instruction in cache */ diff --git a/contrib/plugins/hotblocks.c b/contrib/plugins/hotblocks.c index 4b08340143..062200a7a4 100644 --- a/contrib/plugins/hotblocks.c +++ b/contrib/plugins/hotblocks.c @@ -133,8 +133,18 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { - if (argc && strcmp(argv[0], "inline") == 0) { - do_inline = true; + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_autofree char **tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "inline") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } } plugin_init(); diff --git a/contrib/plugins/hotpages.c b/contrib/plugins/hotpages.c index bf53267532..0d12910af6 100644 --- a/contrib/plugins/hotpages.c +++ b/contrib/plugins/hotpages.c @@ -169,16 +169,26 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, for (i = 0; i < argc; i++) { char *opt = argv[i]; - if (g_strcmp0(opt, "reads") == 0) { - sort_by = SORT_R; - } else if (g_strcmp0(opt, "writes") == 0) { - sort_by = SORT_W; - } else if (g_strcmp0(opt, "address") == 0) { - sort_by = SORT_A; - } else if (g_strcmp0(opt, "io") == 0) { - track_io = true; - } else if (g_str_has_prefix(opt, "pagesize=")) { - page_size = g_ascii_strtoull(opt + 9, NULL, 10); + g_autofree char **tokens = g_strsplit(opt, "=", -1); + + if (g_strcmp0(tokens[0], "sortby") == 0) { + if (g_strcmp0(tokens[1], "reads") == 0) { + sort_by = SORT_R; + } else if (g_strcmp0(tokens[1], "writes") == 0) { + sort_by = SORT_W; + } else if (g_strcmp0(tokens[1], "address") == 0) { + sort_by = SORT_A; + } else { + fprintf(stderr, "invalid value to sortby: %s\n", tokens[1]); + return -1; + } + } else if (g_strcmp0(tokens[0], "io") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &track_io)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "pagesize") == 0) { + page_size = g_ascii_strtoull(tokens[1], NULL, 10); } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c index 600f7facc1..4a5ec3d936 100644 --- a/contrib/plugins/howvec.c +++ b/contrib/plugins/howvec.c @@ -333,23 +333,34 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, for (i = 0; i < argc; i++) { char *p = argv[i]; - if (strcmp(p, "inline") == 0) { - do_inline = true; - } else if (strcmp(p, "verbose") == 0) { - verbose = true; - } else { + g_autofree char **tokens = g_strsplit(p, "=", -1); + if (g_strcmp0(tokens[0], "inline") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", p); + return -1; + } + } else if (g_strcmp0(tokens[0], "verbose") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &verbose)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", p); + return -1; + } + } else if (g_strcmp0(tokens[0], "count") == 0) { + char *value = tokens[1]; int j; CountType type = COUNT_INDIVIDUAL; - if (*p == '!') { + if (*value == '!') { type = COUNT_NONE; - p++; + value++; } for (j = 0; j < class_table_sz; j++) { - if (strcmp(p, class_table[j].opt) == 0) { + if (strcmp(value, class_table[j].opt) == 0) { class_table[j].what = type; break; } } + } else { + fprintf(stderr, "option parsing failed: %s\n", p); + return -1; } } diff --git a/contrib/plugins/hwprofile.c b/contrib/plugins/hwprofile.c index faf216ac00..691d4edb0c 100644 --- a/contrib/plugins/hwprofile.c +++ b/contrib/plugins/hwprofile.c @@ -259,27 +259,42 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { int i; + g_autoptr(GString) matches_raw = g_string_new(""); for (i = 0; i < argc; i++) { char *opt = argv[i]; - if (g_strcmp0(opt, "read") == 0) { - rw = QEMU_PLUGIN_MEM_R; - } else if (g_strcmp0(opt, "write") == 0) { - rw = QEMU_PLUGIN_MEM_W; - } else if (g_strcmp0(opt, "pattern") == 0) { - pattern = true; - } else if (g_strcmp0(opt, "source") == 0) { - source = true; - } else if (g_str_has_prefix(opt, "match")) { - gchar **parts = g_strsplit(opt, "=", 2); + g_autofree char **tokens = g_strsplit(opt, "=", 2); + + if (g_strcmp0(tokens[0], "track") == 0) { + if (g_strcmp0(tokens[1], "read") == 0) { + rw = QEMU_PLUGIN_MEM_R; + } else if (g_strcmp0(tokens[1], "write") == 0) { + rw = QEMU_PLUGIN_MEM_W; + } else { + fprintf(stderr, "invalid value for track: %s\n", tokens[1]); + return -1; + } + } else if (g_strcmp0(tokens[0], "pattern") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &pattern)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "source") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &source)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "match") == 0) { check_match = true; - matches = g_strsplit(parts[1], ",", -1); - g_strfreev(parts); + g_string_append_printf(matches_raw, "%s,", tokens[1]); } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; } } + if (check_match) { + matches = g_strsplit(matches_raw->str, ",", -1); + } if (source && pattern) { fprintf(stderr, "can only currently track either source or pattern.\n"); diff --git a/contrib/plugins/lockstep.c b/contrib/plugins/lockstep.c index 7fd35eb669..a41ffe83fa 100644 --- a/contrib/plugins/lockstep.c +++ b/contrib/plugins/lockstep.c @@ -319,22 +319,35 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, int argc, char **argv) { int i; - - if (!argc || !argv[0]) { - qemu_plugin_outs("Need a socket path to talk to other instance."); - return -1; - } + g_autofree char *sock_path = NULL; for (i = 0; i < argc; i++) { char *p = argv[i]; - if (strcmp(p, "verbose") == 0) { - verbose = true; - } else if (!setup_unix_socket(argv[0])) { - qemu_plugin_outs("Failed to setup socket for communications."); + g_autofree char **tokens = g_strsplit(p, "=", 2); + + if (g_strcmp0(tokens[0], "verbose") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &verbose)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", p); + return -1; + } + } else if (g_strcmp0(tokens[0], "sockpath") == 0) { + sock_path = tokens[1]; + } else { + fprintf(stderr, "option parsing failed: %s\n", p); return -1; } } + if (sock_path == NULL) { + fprintf(stderr, "Need a socket path to talk to other instance.\n"); + return -1; + } + + if (!setup_unix_socket(sock_path)) { + fprintf(stderr, "Failed to setup socket for communications.\n"); + return -1; + } + our_id = id; qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 1e1a5e96ad..6e88a84bba 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -139,6 +139,18 @@ The ``-no-quit`` is a synonym for ``-display ...,window-close=off`` which should be used instead. +Plugin argument passing through ``arg=<string>`` (since 6.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Passing TCG plugins arguments through ``arg=`` is redundant is makes the +command-line less readable, especially when the argument itself consist of a +name and a value, e.g. ``-plugin plugin_name,arg="arg_name=arg_value"``. +Therefore, the usage of ``arg`` is redundant. Single-word arguments are treated +as short-form boolean values, and passed to plugins as ``arg_name=on``. +However, short-form booleans are deprecated and full explicit ``arg_name=on`` +form is preferred. + + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/devel/ci-jobs.rst b/docs/devel/ci-jobs.rst index 9cd9819786..277975e4ad 100644 --- a/docs/devel/ci-jobs.rst +++ b/docs/devel/ci-jobs.rst @@ -38,3 +38,14 @@ these artifacts are not already cached, downloading them make the jobs reach the timeout limit). Set this variable to have the tests using the Avocado framework run automatically. +AARCH64_RUNNER_AVAILABLE +~~~~~~~~~~~~~~~~~~~~~~~~ +If you've got access to an aarch64 host that can be used as a gitlab-CI +runner, you can set this variable to enable the tests that require this +kind of host. The runner should be tagged with "aarch64". + +S390X_RUNNER_AVAILABLE +~~~~~~~~~~~~~~~~~~~~~~ +If you've got access to an IBM Z host that can be used as a gitlab-CI +runner, you can set this variable to enable the tests that require this +kind of host. The runner should be tagged with "s390x". diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst index 047bf4ada7..dac5101a3c 100644 --- a/docs/devel/tcg-plugins.rst +++ b/docs/devel/tcg-plugins.rst @@ -80,7 +80,7 @@ Once built a program can be run with multiple plugins loaded each with their own arguments:: $QEMU $OTHER_QEMU_ARGS \ - -plugin tests/plugin/libhowvec.so,arg=inline,arg=hint \ + -plugin tests/plugin/libhowvec.so,inline=on,count=hint \ -plugin tests/plugin/libhotblocks.so Arguments are plugin specific and can be used to modify their @@ -193,17 +193,32 @@ Similar to hotblocks but this time tracks memory accesses:: 0x0000000048b000, 0x0001, 130594, 0x0001, 355 0x0000000048a000, 0x0001, 1826, 0x0001, 11 +The hotpages plugin can be configured using the following arguments: + + * sortby=reads|writes|address + + Log the data sorted by either the number of reads, the number of writes, or + memory address. (Default: entries are sorted by the sum of reads and writes) + + * io=on + + Track IO addresses. Only relevant to full system emulation. (Default: off) + + * pagesize=N + + The page size used. (Default: N = 4096) + - contrib/plugins/howvec.c This is an instruction classifier so can be used to count different types of instructions. It has a number of options to refine which get -counted. You can give an argument for a class of instructions to break -it down fully, so for example to see all the system registers -accesses:: +counted. You can give a value to the `count` argument for a class of +instructions to break it down fully, so for example to see all the system +registers accesses:: ./aarch64-softmmu/qemu-system-aarch64 $(QEMU_ARGS) \ -append "root=/dev/sda2 systemd.unit=benchmark.service" \ - -smp 4 -plugin ./contrib/plugins/libhowvec.so,arg=sreg -d plugin + -smp 4 -plugin ./contrib/plugins/libhowvec.so,count=sreg -d plugin which will lead to a sorted list after the class breakdown:: @@ -271,7 +286,7 @@ communicate over:: ./sparc-softmmu/qemu-system-sparc -monitor none -parallel none \ -net none -M SS-20 -m 256 -kernel day11/zImage.elf \ - -plugin ./contrib/plugins/liblockstep.so,arg=lockstep-sparc.sock \ + -plugin ./contrib/plugins/liblockstep.so,sockpath=lockstep-sparc.sock \ -d plugin,nochain which will eventually report:: @@ -286,27 +301,27 @@ which will eventually report:: previously @ 0x000000ffd08098/5 (809900593 insns) previously @ 0x000000ffd080c0/1 (809900588 insns) -- contrib/plugins/hwprofile +- contrib/plugins/hwprofile.c The hwprofile tool can only be used with system emulation and allows the user to see what hardware is accessed how often. It has a number of options: - * arg=read or arg=write + * track=read or track=write By default the plugin tracks both reads and writes. You can use one of these options to limit the tracking to just one class of accesses. - * arg=source + * source Will include a detailed break down of what the guest PC that made the - access was. Not compatible with arg=pattern. Example output:: + access was. Not compatible with the pattern option. Example output:: cirrus-low-memory @ 0xfffffd00000a0000 pc:fffffc0000005cdc, 1, 256 pc:fffffc0000005ce8, 1, 256 pc:fffffc0000005cec, 1, 256 - * arg=pattern + * pattern Instead break down the accesses based on the offset into the HW region. This can be useful for seeing the most used registers of a @@ -345,7 +360,7 @@ which will output an execution trace following this structure:: 0, 0xd34, 0xf9c8f000, "bl #0x10c8" 0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM -- contrib/plugins/cache +- contrib/plugins/cache.c Cache modelling plugin that measures the performance of a given cache configuration when a given working set is run:: @@ -355,11 +370,8 @@ configuration when a given working set is run:: will report the following:: - Data accesses: 996479, Misses: 507 - Miss rate: 0.050879% - - Instruction accesses: 2641737, Misses: 18617 - Miss rate: 0.704726% + core #, data accesses, data misses, dmiss rate, insn accesses, insn misses, imiss rate + 0 996695 508 0.0510% 2642799 18617 0.7044% address, data misses, instruction 0x424f1e (_int_malloc), 109, movq %rax, 8(%rcx) @@ -377,29 +389,35 @@ will report the following:: The plugin has a number of arguments, all of them are optional: - * arg="limit=N" + * limit=N Print top N icache and dcache thrashing instructions along with their address, number of misses, and its disassembly. (default: 32) - * arg="icachesize=N" - * arg="iblksize=B" - * arg="iassoc=A" + * icachesize=N + * iblksize=B + * iassoc=A Instruction cache configuration arguments. They specify the cache size, block size, and associativity of the instruction cache, respectively. (default: N = 16384, B = 64, A = 8) - * arg="dcachesize=N" - * arg="dblksize=B" - * arg="dassoc=A" + * dcachesize=N + * dblksize=B + * dassoc=A Data cache configuration arguments. They specify the cache size, block size, and associativity of the data cache, respectively. (default: N = 16384, B = 64, A = 8) - * arg="evict=POLICY" + * evict=POLICY Sets the eviction policy to POLICY. Available policies are: :code:`lru`, :code:`fifo`, and :code:`rand`. The plugin will use the specified policy for both instruction and data caches. (default: POLICY = :code:`lru`) + + * cores=N + + Sets the number of cores for which we maintain separate icache and dcache. + (default: for linux-user, N = 1, for full system emulation: N = cores + available to guest) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index e6e815abc5..5f1017201f 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -577,4 +577,17 @@ int qemu_plugin_n_max_vcpus(void); */ void qemu_plugin_outs(const char *string); +/** + * qemu_plugin_bool_parse() - parses a boolean argument in the form of + * "<argname>=[on|yes|true|off|no|false]" + * + * @name: argument name, the part before the equals sign + * @val: argument value, what's after the equals sign + * @ret: output return value + * + * returns true if the combination @name=@val parses correctly to a boolean + * argument, and false otherwise + */ +bool qemu_plugin_bool_parse(const char *name, const char *val, bool *ret); + #endif /* QEMU_PLUGIN_API_H */ diff --git a/linux-user/main.c b/linux-user/main.c index 37ed50d98e..a6094563b6 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -463,7 +463,7 @@ static const struct qemu_argument arg_table[] = { "", "[[enable=]<pattern>][,events=<file>][,file=<file>]"}, #ifdef CONFIG_PLUGIN {"plugin", "QEMU_PLUGIN", true, handle_arg_plugin, - "", "[file=]<file>[,arg=<string>]"}, + "", "[file=]<file>[,<argname>=<argvalue>]"}, #endif {"version", "QEMU_VERSION", false, handle_arg_version, "", "display version information and exit"}, diff --git a/plugins/api.c b/plugins/api.c index 2d521e6ba8..acff9ce8ac 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -383,3 +383,8 @@ void qemu_plugin_outs(const char *string) { qemu_log_mask(CPU_LOG_PLUGIN, "%s", string); } + +bool qemu_plugin_bool_parse(const char *name, const char *value, bool *ret) +{ + return name && value && qapi_bool_parse(name, value, ret, NULL); +} diff --git a/plugins/loader.c b/plugins/loader.c index 05df40398d..a4ec281692 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -94,6 +94,8 @@ static int plugin_add(void *opaque, const char *name, const char *value, { struct qemu_plugin_parse_arg *arg = opaque; struct qemu_plugin_desc *p; + bool is_on; + char *fullarg; if (strcmp(name, "file") == 0) { if (strcmp(value, "") == 0) { @@ -107,18 +109,32 @@ static int plugin_add(void *opaque, const char *name, const char *value, QTAILQ_INSERT_TAIL(arg->head, p, entry); } arg->curr = p; - } else if (strcmp(name, "arg") == 0) { + } else { if (arg->curr == NULL) { error_setg(errp, "missing earlier '-plugin file=' option"); return 1; } + + if (g_strcmp0(name, "arg") == 0 && + !qapi_bool_parse(name, value, &is_on, NULL)) { + if (strchr(value, '=') == NULL) { + /* Will treat arg="argname" as "argname=on" */ + fullarg = g_strdup_printf("%s=%s", value, "on"); + } else { + fullarg = g_strdup_printf("%s", value); + } + warn_report("using 'arg=%s' is deprecated", value); + error_printf("Please use '%s' directly\n", fullarg); + } else { + fullarg = g_strdup_printf("%s=%s", name, value); + } + p = arg->curr; p->argc++; p->argv = g_realloc_n(p->argv, p->argc, sizeof(char *)); - p->argv[p->argc - 1] = g_strdup(value); - } else { - error_setg(errp, "-plugin: unexpected parameter '%s'; ignored", name); + p->argv[p->argc - 1] = fullarg; } + return 0; } diff --git a/plugins/meson.build b/plugins/meson.build index e77723010e..bfd5c9822a 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -1,9 +1,11 @@ -if 'CONFIG_HAS_LD_DYNAMIC_LIST' in config_host - plugin_ldflags = ['-Wl,--dynamic-list=' + (meson.build_root() / 'qemu-plugins-ld.symbols')] -elif 'CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST' in config_host - plugin_ldflags = ['-Wl,-exported_symbols_list,' + (meson.build_root() / 'qemu-plugins-ld64.symbols')] -else - plugin_ldflags = [] +plugin_ldflags = [] +# Modules need more symbols than just those in plugins/qemu-plugins.symbols +if not enable_modules + if 'CONFIG_HAS_LD_DYNAMIC_LIST' in config_host + plugin_ldflags = ['-Wl,--dynamic-list=' + (meson.build_root() / 'qemu-plugins-ld.symbols')] + elif 'CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST' in config_host + plugin_ldflags = ['-Wl,-exported_symbols_list,' + (meson.build_root() / 'qemu-plugins-ld64.symbols')] + endif endif specific_ss.add(when: 'CONFIG_PLUGIN', if_true: [files( diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols index 40b4ff3821..67b309ea2a 100644 --- a/plugins/qemu-plugins.symbols +++ b/plugins/qemu-plugins.symbols @@ -1,37 +1,38 @@ { - qemu_plugin_uninstall; - qemu_plugin_reset; - qemu_plugin_register_vcpu_init_cb; + qemu_plugin_bool_parse; + qemu_plugin_get_hwaddr; + qemu_plugin_hwaddr_is_io; + qemu_plugin_insn_data; + qemu_plugin_insn_disas; + qemu_plugin_insn_haddr; + qemu_plugin_insn_size; + qemu_plugin_insn_vaddr; + qemu_plugin_mem_is_big_endian; + qemu_plugin_mem_is_sign_extended; + qemu_plugin_mem_is_store; + qemu_plugin_mem_size_shift; + qemu_plugin_n_max_vcpus; + qemu_plugin_n_vcpus; + qemu_plugin_outs; + qemu_plugin_register_atexit_cb; + qemu_plugin_register_flush_cb; qemu_plugin_register_vcpu_exit_cb; qemu_plugin_register_vcpu_idle_cb; - qemu_plugin_register_vcpu_resume_cb; + qemu_plugin_register_vcpu_init_cb; qemu_plugin_register_vcpu_insn_exec_cb; qemu_plugin_register_vcpu_insn_exec_inline; qemu_plugin_register_vcpu_mem_cb; qemu_plugin_register_vcpu_mem_inline; - qemu_plugin_register_vcpu_tb_trans_cb; - qemu_plugin_register_vcpu_tb_exec_cb; - qemu_plugin_register_vcpu_tb_exec_inline; - qemu_plugin_register_flush_cb; + qemu_plugin_register_vcpu_resume_cb; qemu_plugin_register_vcpu_syscall_cb; qemu_plugin_register_vcpu_syscall_ret_cb; - qemu_plugin_register_atexit_cb; - qemu_plugin_tb_n_insns; + qemu_plugin_register_vcpu_tb_exec_cb; + qemu_plugin_register_vcpu_tb_exec_inline; + qemu_plugin_register_vcpu_tb_trans_cb; + qemu_plugin_reset; qemu_plugin_tb_get_insn; + qemu_plugin_tb_n_insns; qemu_plugin_tb_vaddr; - qemu_plugin_insn_data; - qemu_plugin_insn_size; - qemu_plugin_insn_vaddr; - qemu_plugin_insn_haddr; - qemu_plugin_insn_disas; - qemu_plugin_mem_size_shift; - qemu_plugin_mem_is_sign_extended; - qemu_plugin_mem_is_big_endian; - qemu_plugin_mem_is_store; - qemu_plugin_get_hwaddr; - qemu_plugin_hwaddr_is_io; + qemu_plugin_uninstall; qemu_plugin_vcpu_for_each; - qemu_plugin_n_vcpus; - qemu_plugin_n_max_vcpus; - qemu_plugin_outs; }; diff --git a/qapi/trace.json b/qapi/trace.json index 47c68f04da..eedfded512 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -99,7 +99,7 @@ # Example: # # -> { "execute": "trace-event-set-state", -# "arguments": { "name": "qemu_memalign", "enable": "true" } } +# "arguments": { "name": "qemu_memalign", "enable": true } } # <- { "return": {} } # ## diff --git a/qemu-options.hx b/qemu-options.hx index 83aa59a920..4a9ee722c9 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4532,19 +4532,18 @@ SRST ERST DEF("plugin", HAS_ARG, QEMU_OPTION_plugin, - "-plugin [file=]<file>[,arg=<string>]\n" + "-plugin [file=]<file>[,<argname>=<argvalue>]\n" " load a plugin\n", QEMU_ARCH_ALL) SRST -``-plugin file=file[,arg=string]`` +``-plugin file=file[,argname=argvalue]`` Load a plugin. ``file=file`` Load the given plugin from a shared library file. - ``arg=string`` - Argument string passed to the plugin. (Can be given multiple - times.) + ``argname=argvalue`` + Argument passed to the plugin. (Can be given multiple times.) ERST HXCOMM Internal use diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 1724ac32db..5f8f76e5b2 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -17,6 +17,7 @@ from typing import ( Dict, Match, Optional, + Sequence, Union, ) @@ -200,33 +201,39 @@ def guardend(name: str) -> str: name=c_fname(name).upper()) -def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: +def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]], + cond_fmt: str, not_fmt: str, + all_operator: str, any_operator: str) -> str: + + def do_gen(ifcond: Union[str, Dict[str, Any]], need_parens: bool): + if isinstance(ifcond, str): + return cond_fmt % ifcond + assert isinstance(ifcond, dict) and len(ifcond) == 1 + if 'not' in ifcond: + return not_fmt % do_gen(ifcond['not'], True) + if 'all' in ifcond: + gen = gen_infix(all_operator, ifcond['all']) + else: + gen = gen_infix(any_operator, ifcond['any']) + if need_parens: + gen = '(' + gen + ')' + return gen + + def gen_infix(operator: str, operands: Sequence[Any]) -> str: + return operator.join([do_gen(o, True) for o in operands]) + if not ifcond: return '' - if isinstance(ifcond, str): - return 'defined(' + ifcond + ')' + return do_gen(ifcond, False) - oper, operands = next(iter(ifcond.items())) - if oper == 'not': - return '!' + cgen_ifcond(operands) - oper = {'all': '&&', 'any': '||'}[oper] - operands = [cgen_ifcond(o) for o in operands] - return '(' + (') ' + oper + ' (').join(operands) + ')' +def cgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str: + return gen_ifcond(ifcond, 'defined(%s)', '!%s', ' && ', ' || ') -def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str: + +def docgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str: # TODO Doc generated for conditions needs polish - if not ifcond: - return '' - if isinstance(ifcond, str): - return ifcond - - oper, operands = next(iter(ifcond.items())) - if oper == 'not': - return '!' + docgen_ifcond(operands) - oper = {'all': ' and ', 'any': ' or '}[oper] - operands = [docgen_ifcond(o) for o in operands] - return '(' + oper.join(operands) + ')' + return gen_ifcond(ifcond, '%s', 'not %s', ' and ', ' or ') def gen_if(cond: str) -> str: diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index 019f4c97aa..b62f0a3640 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -275,7 +275,7 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: def _check_if(cond: Union[str, object]) -> None: if isinstance(cond, str): - if not re.match(r'^[A-Z][A-Z0-9_]*$', cond): + if not re.fullmatch(r'[A-Z][A-Z0-9_]*', cond): raise QAPISemError( info, "'if' condition '%s' of %s is not a valid identifier" @@ -286,13 +286,12 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None: raise QAPISemError( info, "'if' condition of %s must be a string or an object" % source) + check_keys(cond, info, "'if' condition of %s" % source, [], + ["all", "any", "not"]) if len(cond) != 1: raise QAPISemError( info, - "'if' condition dict of %s must have one key: " - "'all', 'any' or 'not'" % source) - check_keys(cond, info, "'if' condition", [], - ["all", "any", "not"]) + "'if' condition of %s has conflicting keys" % source) oper, operands = next(iter(cond.items())) if not operands: @@ -630,20 +629,15 @@ def check_exprs(exprs: List[_JSONObject]) -> List[_JSONObject]: if 'include' in expr: continue - if 'enum' in expr: - meta = 'enum' - elif 'union' in expr: - meta = 'union' - elif 'alternate' in expr: - meta = 'alternate' - elif 'struct' in expr: - meta = 'struct' - elif 'command' in expr: - meta = 'command' - elif 'event' in expr: - meta = 'event' - else: - raise QAPISemError(info, "expression is missing metatype") + metas = expr.keys() & {'enum', 'struct', 'union', 'alternate', + 'command', 'event'} + if len(metas) != 1: + raise QAPISemError( + info, + "expression must have exactly one key" + " 'enum', 'struct', 'union', 'alternate'," + " 'command', 'event'") + meta = metas.pop() check_name_is_str(expr[meta], info, "'%s'" % meta) name = cast(str, expr[meta]) diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 51a597a025..ab26d5c937 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -24,8 +24,6 @@ from typing import ( from .common import ( c_fname, c_name, - gen_endif, - gen_if, guardend, guardstart, mcgen, @@ -95,9 +93,9 @@ def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str: if added[0] == '\n': out += '\n' added = added[1:] - out += gen_if(ifcond.cgen()) + out += ifcond.gen_if() out += added - out += gen_endif(ifcond.cgen()) + out += ifcond.gen_endif() return out diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index bd4233ecee..4c079ee627 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -22,12 +22,7 @@ from typing import ( Union, ) -from .common import ( - c_name, - gen_endif, - gen_if, - mcgen, -) +from .common import c_name, mcgen from .gen import QAPISchemaMonolithicCVisitor from .schema import ( QAPISchema, @@ -124,10 +119,10 @@ def _tree_to_qlit(obj: JSONValue, if obj.comment: ret += indent(level) + f"/* {obj.comment} */\n" if obj.ifcond.is_present(): - ret += gen_if(obj.ifcond.cgen()) + ret += obj.ifcond.gen_if() ret += _tree_to_qlit(obj.value, level) if obj.ifcond.is_present(): - ret += '\n' + gen_endif(obj.ifcond.cgen()) + ret += '\n' + obj.ifcond.gen_endif() return ret ret = '' diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 229d24fce9..3d72c7dfc9 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -24,6 +24,8 @@ from .common import ( c_name, cgen_ifcond, docgen_ifcond, + gen_endif, + gen_if, ) from .error import QAPIError, QAPISemError, QAPISourceError from .expr import check_exprs @@ -32,11 +34,17 @@ from .parser import QAPISchemaParser class QAPISchemaIfCond: def __init__(self, ifcond=None): - self.ifcond = ifcond or {} + self.ifcond = ifcond - def cgen(self): + def _cgen(self): return cgen_ifcond(self.ifcond) + def gen_if(self): + return gen_if(self._cgen()) + + def gen_endif(self): + return gen_endif(self._cgen()) + def docgen(self): return docgen_ifcond(self.ifcond) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index db9ff95bd1..831294fe42 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -15,13 +15,7 @@ This work is licensed under the terms of the GNU GPL, version 2. from typing import List, Optional -from .common import ( - c_enum_const, - c_name, - gen_endif, - gen_if, - mcgen, -) +from .common import c_enum_const, c_name, mcgen from .gen import QAPISchemaModularCVisitor, ifcontext from .schema import ( QAPISchema, @@ -51,13 +45,13 @@ const QEnumLookup %(c_name)s_lookup = { ''', c_name=c_name(name)) for memb in members: - ret += gen_if(memb.ifcond.cgen()) + ret += memb.ifcond.gen_if() index = c_enum_const(name, memb.name, prefix) ret += mcgen(''' [%(index)s] = "%(name)s", ''', index=index, name=memb.name) - ret += gen_endif(memb.ifcond.cgen()) + ret += memb.ifcond.gen_endif() ret += mcgen(''' }, @@ -81,12 +75,12 @@ typedef enum %(c_name)s { c_name=c_name(name)) for memb in enum_members: - ret += gen_if(memb.ifcond.cgen()) + ret += memb.ifcond.gen_if() ret += mcgen(''' %(c_enum)s, ''', c_enum=c_enum_const(name, memb.name, prefix)) - ret += gen_endif(memb.ifcond.cgen()) + ret += memb.ifcond.gen_endif() ret += mcgen(''' } %(c_name)s; @@ -126,7 +120,7 @@ struct %(c_name)s { def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: ret = '' for memb in members: - ret += gen_if(memb.ifcond.cgen()) + ret += memb.ifcond.gen_if() if memb.optional: ret += mcgen(''' bool has_%(c_name)s; @@ -136,7 +130,7 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str: %(c_type)s %(c_name)s; ''', c_type=memb.type.c_type(), c_name=c_name(memb.name)) - ret += gen_endif(memb.ifcond.cgen()) + ret += memb.ifcond.gen_endif() return ret @@ -159,7 +153,7 @@ def gen_object(name: str, ifcond: QAPISchemaIfCond, ret += mcgen(''' ''') - ret += gen_if(ifcond.cgen()) + ret += ifcond.gen_if() ret += mcgen(''' struct %(c_name)s { ''', @@ -193,7 +187,7 @@ struct %(c_name)s { ret += mcgen(''' }; ''') - ret += gen_endif(ifcond.cgen()) + ret += ifcond.gen_endif() return ret @@ -220,13 +214,13 @@ def gen_variants(variants: QAPISchemaVariants) -> str: for var in variants.variants: if var.type.name == 'q_empty': continue - ret += gen_if(var.ifcond.cgen()) + ret += var.ifcond.gen_if() ret += mcgen(''' %(c_type)s %(c_name)s; ''', c_type=var.type.c_unboxed_type(), c_name=c_name(var.name)) - ret += gen_endif(var.ifcond.cgen()) + ret += var.ifcond.gen_endif() ret += mcgen(''' } u; diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index 56ea516399..9d9196a143 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -18,8 +18,6 @@ from typing import List, Optional from .common import ( c_enum_const, c_name, - gen_endif, - gen_if, indent, mcgen, ) @@ -79,7 +77,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) for memb in members: deprecated = 'deprecated' in [f.name for f in memb.features] - ret += gen_if(memb.ifcond.cgen()) + ret += memb.ifcond.gen_if() if memb.optional: ret += mcgen(''' if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) { @@ -112,7 +110,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ret += mcgen(''' } ''') - ret += gen_endif(memb.ifcond.cgen()) + ret += memb.ifcond.gen_endif() if variants: tag_member = variants.tag_member @@ -126,7 +124,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) for var in variants.variants: case_str = c_enum_const(tag_member.type.name, var.name, tag_member.type.prefix) - ret += gen_if(var.ifcond.cgen()) + ret += var.ifcond.gen_if() if var.type.name == 'q_empty': # valid variant and nothing to do ret += mcgen(''' @@ -142,7 +140,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) case=case_str, c_type=var.type.c_name(), c_name=c_name(var.name)) - ret += gen_endif(var.ifcond.cgen()) + ret += var.ifcond.gen_endif() ret += mcgen(''' default: abort(); @@ -228,7 +226,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, c_name=c_name(name)) for var in variants.variants: - ret += gen_if(var.ifcond.cgen()) + ret += var.ifcond.gen_if() ret += mcgen(''' case %(case)s: ''', @@ -254,7 +252,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name, ret += mcgen(''' break; ''') - ret += gen_endif(var.ifcond.cgen()) + ret += var.ifcond.gen_endif() ret += mcgen(''' case QTYPE_NONE: diff --git a/tests/plugin/bb.c b/tests/plugin/bb.c index de09bdde4e..7d470a1011 100644 --- a/tests/plugin/bb.c +++ b/tests/plugin/bb.c @@ -104,10 +104,17 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, for (i = 0; i < argc; i++) { char *opt = argv[i]; - if (g_strcmp0(opt, "inline") == 0) { - do_inline = true; - } else if (g_strcmp0(opt, "idle") == 0) { - idle_report = true; + g_autofree char **tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "inline") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "idle") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &idle_report)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; diff --git a/tests/plugin/insn.c b/tests/plugin/insn.c index c253980ec8..0f6a1938c1 100644 --- a/tests/plugin/insn.c +++ b/tests/plugin/insn.c @@ -62,8 +62,18 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { - if (argc && !strcmp(argv[0], "inline")) { - do_inline = true; + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_autofree char **tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "inline") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } } qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); diff --git a/tests/plugin/mem.c b/tests/plugin/mem.c index afd1d27e5c..4570f7d815 100644 --- a/tests/plugin/mem.c +++ b/tests/plugin/mem.c @@ -80,29 +80,40 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { - if (argc) { - if (argc >= 3) { - if (!strcmp(argv[2], "haddr")) { - do_haddr = true; - } - } - if (argc >= 2) { - const char *str = argv[1]; - if (!strcmp(str, "r")) { + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_autofree char **tokens = g_strsplit(opt, "=", 2); + + if (g_strcmp0(tokens[0], "haddr") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_haddr)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "track") == 0) { + if (g_strcmp0(tokens[1], "r") == 0) { rw = QEMU_PLUGIN_MEM_R; - } else if (!strcmp(str, "w")) { + } else if (g_strcmp0(tokens[1], "w") == 0) { rw = QEMU_PLUGIN_MEM_W; + } else if (g_strcmp0(tokens[1], "rw") == 0) { + rw = QEMU_PLUGIN_MEM_RW; + } else { + fprintf(stderr, "invaild value for argument track: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "inline") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "callback") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_callback)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; } - } - if (!strcmp(argv[0], "inline")) { - do_inline = true; - do_callback = false; - } else if (!strcmp(argv[0], "both")) { - do_inline = true; - do_callback = true; } else { - do_callback = true; + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; } } diff --git a/tests/plugin/syscall.c b/tests/plugin/syscall.c index 6dd71092e1..484b48de49 100644 --- a/tests/plugin/syscall.c +++ b/tests/plugin/syscall.c @@ -119,17 +119,26 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { - if (argc == 0) { - statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free); - } else { - for (int i = 0; i < argc; i++) { - if (g_strcmp0(argv[i], "print") != 0) { - fprintf(stderr, "unsupported argument: %s\n", argv[i]); - return -1; + bool do_print = false; + + for (int i = 0; i < argc; i++) { + char *opt = argv[i]; + g_autofree char **tokens = g_strsplit(opt, "=", 2); + + if (g_strcmp0(tokens[0], "print") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); } + } else { + fprintf(stderr, "unsupported argument: %s\n", argv[i]); + return -1; } } + if (!do_print) { + statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free); + } + qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall); qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); diff --git a/tests/qapi-schema/bad-if-key.err b/tests/qapi-schema/bad-if-key.err index a69dc9ee86..38cf44b687 100644 --- a/tests/qapi-schema/bad-if-key.err +++ b/tests/qapi-schema/bad-if-key.err @@ -1,3 +1,3 @@ bad-if-key.json: In struct 'TestIfStruct': -bad-if-key.json:2: 'if' condition has unknown key 'value' +bad-if-key.json:2: 'if' condition of struct has unknown key 'value' Valid keys are 'all', 'any', 'not'. diff --git a/tests/qapi-schema/bad-if-keys.err b/tests/qapi-schema/bad-if-keys.err index aceb31dc6d..fe87bd30ac 100644 --- a/tests/qapi-schema/bad-if-keys.err +++ b/tests/qapi-schema/bad-if-keys.err @@ -1,2 +1,2 @@ bad-if-keys.json: In struct 'TestIfStruct': -bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all', 'any' or 'not' +bad-if-keys.json:2: 'if' condition of struct has conflicting keys diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 5e30790730..e0027e4cf6 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -127,7 +127,7 @@ { 'alternate': 'Alternate', 'features': [ 'alt-feat' ], 'data': { 'i': 'int', 'b': 'bool' }, - 'if': { 'not': 'IFNOT' } } + 'if': { 'not': { 'any': [ 'IFONE', 'IFTWO' ] } } } ## # == Another subsection diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 26d1fa5d28..478fe6f82e 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -18,7 +18,7 @@ enum Enum feature enum-feat object Base member base1: Enum optional=False - if OrderedDict([('all', ['IFALL1', 'IFALL2'])]) + if {'all': ['IFALL1', 'IFALL2']} object Variant1 member var1: str optional=False if IFSTR @@ -30,7 +30,7 @@ object Object tag base1 case one: Variant1 case two: Variant2 - if OrderedDict([('any', ['IFONE', 'IFTWO'])]) + if {'any': ['IFONE', 'IFTWO']} feature union-feat1 object q_obj_Variant1-wrapper member data: Variant1 optional=False @@ -51,7 +51,7 @@ alternate Alternate tag type case i: int case b: bool - if OrderedDict([('not', 'IFNOT')]) + if {'not': {'any': ['IFONE', 'IFTWO']}} feature alt-feat object q_obj_cmd-arg member arg1: int optional=False diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 5bfe06e14e..0c59d75964 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -79,7 +79,7 @@ Members If ~~ -"(IFALL1 and IFALL2)" +"IFALL1 and IFALL2" "Variant1" (Object) @@ -120,8 +120,8 @@ Members The members of "Base" The members of "Variant1" when "base1" is ""one"" -The members of "Variant2" when "base1" is ""two"" (**If: **"(IFONE or -IFTWO)") +The members of "Variant2" when "base1" is ""two"" (**If: **"IFONE or +IFTWO") Features ~~~~~~~~ @@ -174,7 +174,7 @@ Features If ~~ -"!IFNOT" +"not (IFONE or IFTWO)" Another subsection diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err index 576e716197..6a1e8a5990 100644 --- a/tests/qapi-schema/double-type.err +++ b/tests/qapi-schema/double-type.err @@ -1,3 +1 @@ -double-type.json: In struct 'Bar': -double-type.json:2: struct has unknown key 'command' -Valid keys are 'base', 'data', 'features', 'if', 'struct'. +double-type.json:2: expression must have exactly one key 'enum', 'struct', 'union', 'alternate', 'command', 'event' diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err index 3bb84075a9..2b2bbffb65 100644 --- a/tests/qapi-schema/enum-if-invalid.err +++ b/tests/qapi-schema/enum-if-invalid.err @@ -1,3 +1,3 @@ enum-if-invalid.json: In enum 'TestIfEnum': -enum-if-invalid.json:2: 'if' condition has unknown key 'val' +enum-if-invalid.json:2: 'if' condition of 'data' member 'bar' has unknown key 'val' Valid keys are 'all', 'any', 'not'. diff --git a/tests/qapi-schema/missing-type.err b/tests/qapi-schema/missing-type.err index 5755386a18..cb39569e49 100644 --- a/tests/qapi-schema/missing-type.err +++ b/tests/qapi-schema/missing-type.err @@ -1 +1 @@ -missing-type.json:2: expression is missing metatype +missing-type.json:2: expression must have exactly one key 'enum', 'struct', 'union', 'alternate', 'command', 'event' diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index fe028145e4..b6c36a9eee 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -236,7 +236,7 @@ { 'command': 'test-if-union-cmd', 'data': { 'union-cmd-arg': 'TestIfUnion' }, - 'if': 'TEST_IF_UNION' } + 'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } } { 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', @@ -245,8 +245,7 @@ { 'command': 'test-if-alternate-cmd', 'data': { 'alt-cmd-arg': 'TestIfAlternate' }, - 'if': { 'all': ['TEST_IF_ALT', - {'not': 'TEST_IF_NOT_ALT'}] } } + 'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } } { 'command': 'test-if-cmd', 'data': { @@ -262,6 +261,10 @@ 'bar': { 'type': ['TestIfEnum'], 'if': 'TEST_IF_EVT_BAR' } }, 'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } } +{ 'event': 'TEST_IF_EVENT2', 'data': {}, + 'if': { 'not': { 'any': [ { 'not': 'TEST_IF_EVT' }, + { 'not': 'TEST_IF_STRUCT' } ] } } } + # test 'features' { 'struct': 'FeatureStruct0', diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 3d0c6a8f28..d557fe2d89 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -311,40 +311,40 @@ enum TestIfUnionKind member foo member bar if TEST_IF_UNION_BAR - if OrderedDict([('all', ['TEST_IF_UNION', 'TEST_IF_STRUCT'])]) + if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']} object TestIfUnion member type: TestIfUnionKind optional=False tag type case foo: q_obj_TestStruct-wrapper case bar: q_obj_str-wrapper if TEST_IF_UNION_BAR - if OrderedDict([('all', ['TEST_IF_UNION', 'TEST_IF_STRUCT'])]) + if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']} object q_obj_test-if-union-cmd-arg member union-cmd-arg: TestIfUnion optional=False - if TEST_IF_UNION + if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']} command test-if-union-cmd q_obj_test-if-union-cmd-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False - if TEST_IF_UNION + if {'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT']} alternate TestIfAlternate tag type case foo: int case bar: TestStruct if TEST_IF_ALT_BAR - if OrderedDict([('all', ['TEST_IF_ALT', 'TEST_IF_STRUCT'])]) + if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']} object q_obj_test-if-alternate-cmd-arg member alt-cmd-arg: TestIfAlternate optional=False - if OrderedDict([('all', ['TEST_IF_ALT', OrderedDict([('not', 'TEST_IF_NOT_ALT')])])]) + if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']} command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False - if OrderedDict([('all', ['TEST_IF_ALT', OrderedDict([('not', 'TEST_IF_NOT_ALT')])])]) + if {'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT']} object q_obj_test-if-cmd-arg member foo: TestIfStruct optional=False member bar: TestIfEnum optional=False if TEST_IF_CMD_BAR - if OrderedDict([('all', ['TEST_IF_CMD', 'TEST_IF_STRUCT'])]) + if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']} command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False - if OrderedDict([('all', ['TEST_IF_CMD', 'TEST_IF_STRUCT'])]) + if {'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT']} command test-cmd-return-def-three None -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False array TestIfEnumList TestIfEnum @@ -353,10 +353,13 @@ object q_obj_TEST_IF_EVENT-arg member foo: TestIfStruct optional=False member bar: TestIfEnumList optional=False if TEST_IF_EVT_BAR - if OrderedDict([('all', ['TEST_IF_EVT', 'TEST_IF_STRUCT'])]) + if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']} event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg boxed=False - if OrderedDict([('all', ['TEST_IF_EVT', 'TEST_IF_STRUCT'])]) + if {'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT']} +event TEST_IF_EVENT2 None + boxed=False + if {'not': {'any': [{'not': 'TEST_IF_EVT'}, {'not': 'TEST_IF_STRUCT'}]}} object FeatureStruct0 member foo: int optional=False object FeatureStruct1 @@ -389,11 +392,11 @@ object CondFeatureStruct2 object CondFeatureStruct3 member foo: int optional=False feature feature1 - if OrderedDict([('all', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])]) + if {'all': ['TEST_IF_COND_1', 'TEST_IF_COND_2']} object CondFeatureStruct4 member foo: int optional=False feature feature1 - if OrderedDict([('any', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])]) + if {'any': ['TEST_IF_COND_1', 'TEST_IF_COND_2']} enum FeatureEnum1 member eins member zwei @@ -444,7 +447,7 @@ command test-command-cond-features2 None -> None command test-command-cond-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 - if OrderedDict([('all', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])]) + if {'all': ['TEST_IF_COND_1', 'TEST_IF_COND_2']} event TEST_EVENT_FEATURES0 FeatureStruct1 boxed=False event TEST_EVENT_FEATURES1 None diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index c92be2d086..73cffae2b6 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -94,8 +94,17 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): @staticmethod def _print_if(ifcond, indent=4): + # TODO Drop this hack after replacing OrderedDict by plain + # dict (requires Python 3.7) + def _massage(subcond): + if isinstance(subcond, str): + return subcond + if isinstance(subcond, list): + return [_massage(val) for val in subcond] + return {key: _massage(val) for key, val in subcond.items()} + if ifcond.is_present(): - print('%sif %s' % (' ' * indent, ifcond.ifcond)) + print('%sif %s' % (' ' * indent, _massage(ifcond.ifcond))) @classmethod def _print_features(cls, features, indent=4): diff --git a/tests/tcg/i386/Makefile.softmmu-target b/tests/tcg/i386/Makefile.softmmu-target index fa9b1b9f90..9b9038d0be 100644 --- a/tests/tcg/i386/Makefile.softmmu-target +++ b/tests/tcg/i386/Makefile.softmmu-target @@ -38,7 +38,7 @@ run-plugin-%-with-libinsn.so: $(call run-test, $@, \ $(QEMU) -monitor none -display none \ -chardev file$(COMMA)path=$@.out$(COMMA)id=output \ - -plugin ../../plugin/libinsn.so$(COMMA)arg=inline \ + -plugin ../../plugin/libinsn.so$(COMMA)inline=on \ -d plugin -D $*-with-libinsn.so.pout \ $(QEMU_OPTS) $*, \ "$* on $(TARGET_NAME)") diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index b0a2128980..a053ca3f15 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -61,7 +61,7 @@ endif # non-inline runs will trigger the duplicate instruction heuristics in libinsn.so run-plugin-%-with-libinsn.so: $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ - -plugin ../../plugin/libinsn.so$(COMMA)arg=inline \ + -plugin ../../plugin/libinsn.so$(COMMA)inline=on \ -d plugin -D $*-with-libinsn.so.pout $*, \ "$* (inline) on $(TARGET_NAME)") diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index 9896319f0e..2afa3298bf 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -38,7 +38,7 @@ run-plugin-%-with-libinsn.so: $(call run-test, $@, \ $(QEMU) -monitor none -display none \ -chardev file$(COMMA)path=$@.out$(COMMA)id=output \ - -plugin ../../plugin/libinsn.so$(COMMA)arg=inline \ + -plugin ../../plugin/libinsn.so$(COMMA)inline=on \ -d plugin -D $*-with-libinsn.so.pout \ $(QEMU_OPTS) $*, \ "$* on $(TARGET_NAME)") |