Testing and plugin updates:

- fix typo in execlog plugin
   - clean-up and document gitlab FOO_RUNNER_AVAILABLE vars
   - fix plugin build issue on OSX and modules
   - add multi-core support to cache modelling plugin
   - clean-ups for plugin arg=FOO handling
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEZoWumedRZ7yvyN81+9DbCVqeKkQFAmEwqF4ACgkQ+9DbCVqe
 KkTCcAf/fCHK1hhXJIaxpFyMGvCkhYzUgKlpL4b05ofXFyQJ4JWp+0OadQzBcVIN
 PwsVVsXik/5ibKvQiud8wt3/kbr9gj5RZdwVITZMEpod6hy9Nt5oaUhxEZpiVs3f
 XZG17zaK8huwTwU6EtHWOnvIWMCuvBXpw8cM/6jF6pEiq162VJbiRo9L/aQ+n0Io
 TZTy9YqEbrZUTj+XrvgPV0TewjM6T/zTujXFJtyCzYJ3P3kx9z5a3/KzfyJI9qkk
 TYw0MX9MY8J5/sXcP6OjOqXqppsRe+G5Uaz0BoW9SvOmR/0nNGy/N8mVCZTD6mqs
 zMsWw5RyIZsZCyWEYj4TYWEORv5Kiw==
 =CBs1
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/stsquad/tags/pull-for-6.2-020921-1' into staging

Testing and plugin updates:

  - fix typo in execlog plugin
  - clean-up and document gitlab FOO_RUNNER_AVAILABLE vars
  - fix plugin build issue on OSX and modules
  - add multi-core support to cache modelling plugin
  - clean-ups for plugin arg=FOO handling

# gpg: Signature made Thu 02 Sep 2021 11:33:02 BST
# gpg:                using RSA key 6685AE99E75167BCAFC8DF35FBD0DB095A9E2A44
# gpg: Good signature from "Alex Bennée (Master Work Key) <alex.bennee@linaro.org>" [full]
# Primary key fingerprint: 6685 AE99 E751 67BC AFC8  DF35 FBD0 DB09 5A9E 2A44

* remotes/stsquad/tags/pull-for-6.2-020921-1: (22 commits)
  docs/devel: be consistent about example plugin names
  docs/deprecated: deprecate passing plugin args through `arg=`
  tests/plugins/syscalls: adhere to new arg-passing scheme
  tests/plugins/mem: introduce "track" arg and make args not positional
  tests/plugins/insn: made arg inline not positional and parse it as bool
  tests/plugins/bb: adapt to the new arg passing scheme
  docs/tcg-plugins: new passing parameters scheme for cache docs
  plugins/howvec: adapting to the new argument passing scheme
  plugins/hwprofile: adapt to the new plugin arguments scheme
  plugins/lockstep: make socket path not positional & parse bool arg
  plugins/hotblocks: Added correct boolean argument parsing
  plugins/hotpages: introduce sortby arg and parsed bool args correctly
  plugins/api: added a boolean parsing plugin api
  plugins: allow plugin arguments to be passed directly
  docs/devel/tcg-plugins: added cores arg to cache plugin
  plugins: sort exported symbol list
  plugins/cache: supported multicore cache modelling
  plugins: do not limit exported symbols if modules are active
  gitlab-ci: Fix ..._RUNNER_AVAILABLE variables and document them
  gitlab-ci: Remove superfluous "dnf install" statement
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-09-03 14:23:36 +01:00
commit 9c03aa87e5
27 changed files with 468 additions and 281 deletions

View File

@ -202,85 +202,6 @@ acceptance-system-opensuse:
MAKE_CHECK_ARGS: check-acceptance 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 # This jobs explicitly disable TCG (--disable-tcg), KVM is detected by
# the configure script. The container doesn't contain Xen headers so # the configure script. The container doesn't contain Xen headers so
# Xen accelerator is not detected / selected. As result it build the # Xen accelerator is not detected / selected. As result it build the
@ -649,20 +570,26 @@ build-without-default-devices:
build-without-default-features: build-without-default-features:
extends: .native_build_job_template extends: .native_build_job_template
needs: needs:
job: amd64-debian-container job: amd64-fedora-container
variables: variables:
IMAGE: debian-amd64 IMAGE: fedora
CONFIGURE_ARGS: --without-default-features --disable-user CONFIGURE_ARGS:
--target-list-exclude=arm-softmmu,i386-softmmu,mipsel-softmmu,mips64-softmmu,ppc-softmmu --without-default-features
MAKE_CHECK_ARGS: check-unit --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: build-libvhost-user:
stage: build stage: build
image: $CI_REGISTRY_IMAGE/qemu/fedora:latest image: $CI_REGISTRY_IMAGE/qemu/fedora:latest
needs: needs:
job: amd64-fedora-container job: amd64-fedora-container
before_script:
- dnf install -y meson ninja-build
script: script:
- mkdir subprojects/libvhost-user/build - mkdir subprojects/libvhost-user/build
- cd subprojects/libvhost-user/build - cd subprojects/libvhost-user/build

View File

@ -137,7 +137,7 @@ ubuntu-20.04-aarch64-all-linux-static:
- aarch64 - aarch64
rules: rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE" - if: "$AARCH64_RUNNER_AVAILABLE"
script: script:
# --disable-libssh is needed because of https://bugs.launchpad.net/qemu/+bug/1838763 # --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 # --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 - aarch64
rules: rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE" - if: "$AARCH64_RUNNER_AVAILABLE"
script: script:
- mkdir build - mkdir build
- cd build - cd build
@ -174,7 +174,7 @@ ubuntu-20.04-aarch64-alldbg:
- aarch64 - aarch64
rules: rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE" - if: "$AARCH64_RUNNER_AVAILABLE"
script: script:
- mkdir build - mkdir build
- cd build - cd build
@ -193,7 +193,7 @@ ubuntu-20.04-aarch64-clang:
rules: rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual when: manual
- if: "$S390X_RUNNER_AVAILABLE" - if: "$AARCH64_RUNNER_AVAILABLE"
when: manual when: manual
script: script:
- mkdir build - mkdir build
@ -211,7 +211,7 @@ ubuntu-20.04-aarch64-tci:
- aarch64 - aarch64
rules: rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
- if: "$S390X_RUNNER_AVAILABLE" - if: "$AARCH64_RUNNER_AVAILABLE"
script: script:
- mkdir build - mkdir build
- cd build - cd build
@ -228,7 +228,7 @@ ubuntu-20.04-aarch64-notcg:
rules: rules:
- if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/'
when: manual when: manual
- if: "$S390X_RUNNER_AVAILABLE" - if: "$AARCH64_RUNNER_AVAILABLE"
when: manual when: manual
script: script:
- mkdir build - mkdir build

5
configure vendored
View File

@ -3187,9 +3187,8 @@ glib_req_ver=2.56
glib_modules=gthread-2.0 glib_modules=gthread-2.0
if test "$modules" = yes; then if test "$modules" = yes; then
glib_modules="$glib_modules gmodule-export-2.0" glib_modules="$glib_modules gmodule-export-2.0"
fi elif test "$plugins" = "yes"; then
if test "$plugins" = "yes"; then glib_modules="$glib_modules gmodule-no-export-2.0"
glib_modules="$glib_modules gmodule-2.0"
fi fi
for i in $glib_modules; do for i in $glib_modules; do

View File

@ -17,18 +17,12 @@ static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW;
static GHashTable *miss_ht; static GHashTable *miss_ht;
static GMutex mtx; static GMutex hashtable_lock;
static GRand *rng; static GRand *rng;
static int limit; static int limit;
static bool sys; static bool sys;
static uint64_t dmem_accesses;
static uint64_t dmisses;
static uint64_t imem_accesses;
static uint64_t imisses;
enum EvictionPolicy { enum EvictionPolicy {
LRU, LRU,
FIFO, FIFO,
@ -80,6 +74,8 @@ typedef struct {
int blksize_shift; int blksize_shift;
uint64_t set_mask; uint64_t set_mask;
uint64_t tag_mask; uint64_t tag_mask;
uint64_t accesses;
uint64_t misses;
} Cache; } Cache;
typedef struct { typedef struct {
@ -96,7 +92,16 @@ void (*update_miss)(Cache *cache, int set, int blk);
void (*metadata_init)(Cache *cache); void (*metadata_init)(Cache *cache);
void (*metadata_destroy)(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) 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) static Cache *cache_init(int blksize, int assoc, int cachesize)
{ {
if (bad_cache_params(blksize, assoc, cachesize)) {
return NULL;
}
Cache *cache; Cache *cache;
int i; int i;
uint64_t blk_mask; 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 = g_new(Cache, 1);
cache->assoc = assoc; cache->assoc = assoc;
cache->cachesize = cachesize; cache->cachesize = cachesize;
cache->num_sets = cachesize / (blksize * assoc); cache->num_sets = cachesize / (blksize * assoc);
cache->sets = g_new(CacheSet, cache->num_sets); cache->sets = g_new(CacheSet, cache->num_sets);
cache->blksize_shift = pow_of_two(blksize); cache->blksize_shift = pow_of_two(blksize);
cache->accesses = 0;
cache->misses = 0;
for (i = 0; i < cache->num_sets; i++) { for (i = 0; i < cache->num_sets; i++) {
cache->sets[i].blocks = g_new0(CacheBlock, assoc); cache->sets[i].blocks = g_new0(CacheBlock, assoc);
@ -263,6 +272,24 @@ static Cache *cache_init(int blksize, int assoc, int cachesize)
return cache; 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) static int get_invalid_block(Cache *cache, uint64_t set)
{ {
int i; int i;
@ -353,6 +380,7 @@ static void vcpu_mem_access(unsigned int vcpu_index, qemu_plugin_meminfo_t info,
{ {
uint64_t effective_addr; uint64_t effective_addr;
struct qemu_plugin_hwaddr *hwaddr; struct qemu_plugin_hwaddr *hwaddr;
int cache_idx;
InsnData *insn; InsnData *insn;
hwaddr = qemu_plugin_get_hwaddr(info, vaddr); 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; effective_addr = hwaddr ? qemu_plugin_hwaddr_phys_addr(hwaddr) : vaddr;
cache_idx = vcpu_index % cores;
g_mutex_lock(&mtx); g_mutex_lock(&dcache_locks[cache_idx]);
if (!access_cache(dcache, effective_addr)) { if (!access_cache(dcaches[cache_idx], effective_addr)) {
insn = (InsnData *) userdata; insn = (InsnData *) userdata;
insn->dmisses++; __atomic_fetch_add(&insn->dmisses, 1, __ATOMIC_SEQ_CST);
dmisses++; dcaches[cache_idx]->misses++;
} }
dmem_accesses++; dcaches[cache_idx]->accesses++;
g_mutex_unlock(&mtx); g_mutex_unlock(&dcache_locks[cache_idx]);
} }
static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata) static void vcpu_insn_exec(unsigned int vcpu_index, void *userdata)
{ {
uint64_t insn_addr; uint64_t insn_addr;
InsnData *insn; InsnData *insn;
int cache_idx;
g_mutex_lock(&mtx);
insn_addr = ((InsnData *) userdata)->addr; 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 = (InsnData *) userdata;
insn->imisses++; __atomic_fetch_add(&insn->imisses, 1, __ATOMIC_SEQ_CST);
imisses++; icaches[cache_idx]->misses++;
} }
imem_accesses++; icaches[cache_idx]->accesses++;
g_mutex_unlock(&mtx); g_mutex_unlock(&icache_locks[cache_idx]);
} }
static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) 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 * new entries for those instructions. Instead, we fetch the same
* entry from the hash table and register it for the callback again. * 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)); data = g_hash_table_lookup(miss_ht, GUINT_TO_POINTER(effective_addr));
if (data == NULL) { if (data == NULL) {
data = g_new0(InsnData, 1); 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), g_hash_table_insert(miss_ht, GUINT_TO_POINTER(effective_addr),
(gpointer) data); (gpointer) data);
} }
g_mutex_unlock(&mtx); g_mutex_unlock(&hashtable_lock);
qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access, qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem_access,
QEMU_PLUGIN_CB_NO_REGS, QEMU_PLUGIN_CB_NO_REGS,
@ -453,6 +484,15 @@ static void cache_free(Cache *cache)
g_free(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) static int dcmp(gconstpointer a, gconstpointer b)
{ {
InsnData *insn_a = (InsnData *) a; 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; 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) static int icmp(gconstpointer a, gconstpointer b)
{ {
InsnData *insn_a = (InsnData *) a; InsnData *insn_a = (InsnData *) a;
@ -471,19 +542,29 @@ static int icmp(gconstpointer a, gconstpointer b)
static void log_stats(void) static void log_stats(void)
{ {
g_autoptr(GString) rep = g_string_new(""); int i;
g_string_append_printf(rep, Cache *icache, *dcache;
"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, g_autoptr(GString) rep = g_string_new("core #, data accesses, data misses,"
"Instruction accesses: %lu, Misses: %lu\nMiss rate: %lf%%\n\n", " dmiss rate, insn accesses,"
imem_accesses, " insn misses, imiss rate\n");
imisses,
((double) imisses / (double) imem_accesses) * 100.0);
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); qemu_plugin_outs(rep->str);
} }
@ -530,8 +611,8 @@ static void plugin_exit(qemu_plugin_id_t id, void *p)
log_stats(); log_stats();
log_top_insns(); log_top_insns();
cache_free(dcache); caches_free(dcaches);
cache_free(icache); caches_free(icaches);
g_hash_table_destroy(miss_ht); 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; policy = LRU;
cores = sys ? qemu_plugin_n_vcpus() : 1;
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
char *opt = argv[i]; char *opt = argv[i];
if (g_str_has_prefix(opt, "iblksize=")) { 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); dcachesize = g_ascii_strtoll(opt + 11, NULL, 10);
} else if (g_str_has_prefix(opt, "limit=")) { } else if (g_str_has_prefix(opt, "limit=")) {
limit = g_ascii_strtoll(opt + 6, NULL, 10); 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=")) { } else if (g_str_has_prefix(opt, "evict=")) {
gchar *p = opt + 6; gchar *p = opt + 6;
if (g_strcmp0(p, "rand") == 0) { 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(); policy_init();
dcache = cache_init(dblksize, dassoc, dcachesize); dcaches = caches_init(dblksize, dassoc, dcachesize);
if (!dcache) { if (!dcaches) {
const char *err = cache_config_error(dblksize, dassoc, dcachesize); const char *err = cache_config_error(dblksize, dassoc, dcachesize);
fprintf(stderr, "dcache cannot be constructed from given parameters\n"); fprintf(stderr, "dcache cannot be constructed from given parameters\n");
fprintf(stderr, "%s\n", err); fprintf(stderr, "%s\n", err);
return -1; return -1;
} }
icache = cache_init(iblksize, iassoc, icachesize); icaches = caches_init(iblksize, iassoc, icachesize);
if (!icache) { if (!icaches) {
const char *err = cache_config_error(iblksize, iassoc, icachesize); const char *err = cache_config_error(iblksize, iassoc, icachesize);
fprintf(stderr, "icache cannot be constructed from given parameters\n"); fprintf(stderr, "icache cannot be constructed from given parameters\n");
fprintf(stderr, "%s\n", err); fprintf(stderr, "%s\n", err);
return -1; 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_vcpu_tb_trans_cb(id, vcpu_tb_trans);
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);

View File

@ -67,7 +67,7 @@ static void vcpu_insn_exec(unsigned int cpu_index, void *udata)
/* Print previous instruction in cache */ /* Print previous instruction in cache */
if (s->len) { if (s->len) {
qemu_plugin_outs(s->str); qemu_plugin_outs(s->str);
qemu_plugin_outs("s\n"); qemu_plugin_outs("\n");
} }
/* Store new instruction in cache */ /* Store new instruction in cache */

View File

@ -133,8 +133,18 @@ QEMU_PLUGIN_EXPORT
int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
int argc, char **argv) int argc, char **argv)
{ {
if (argc && strcmp(argv[0], "inline") == 0) { for (int i = 0; i < argc; i++) {
do_inline = true; 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(); plugin_init();

View File

@ -169,16 +169,26 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
char *opt = argv[i]; char *opt = argv[i];
if (g_strcmp0(opt, "reads") == 0) { g_autofree char **tokens = g_strsplit(opt, "=", -1);
sort_by = SORT_R;
} else if (g_strcmp0(opt, "writes") == 0) { if (g_strcmp0(tokens[0], "sortby") == 0) {
sort_by = SORT_W; if (g_strcmp0(tokens[1], "reads") == 0) {
} else if (g_strcmp0(opt, "address") == 0) { sort_by = SORT_R;
sort_by = SORT_A; } else if (g_strcmp0(tokens[1], "writes") == 0) {
} else if (g_strcmp0(opt, "io") == 0) { sort_by = SORT_W;
track_io = true; } else if (g_strcmp0(tokens[1], "address") == 0) {
} else if (g_str_has_prefix(opt, "pagesize=")) { sort_by = SORT_A;
page_size = g_ascii_strtoull(opt + 9, NULL, 10); } 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 { } else {
fprintf(stderr, "option parsing failed: %s\n", opt); fprintf(stderr, "option parsing failed: %s\n", opt);
return -1; return -1;

View File

@ -333,23 +333,34 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
char *p = argv[i]; char *p = argv[i];
if (strcmp(p, "inline") == 0) { g_autofree char **tokens = g_strsplit(p, "=", -1);
do_inline = true; if (g_strcmp0(tokens[0], "inline") == 0) {
} else if (strcmp(p, "verbose") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
verbose = true; fprintf(stderr, "boolean argument parsing failed: %s\n", p);
} else { 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; int j;
CountType type = COUNT_INDIVIDUAL; CountType type = COUNT_INDIVIDUAL;
if (*p == '!') { if (*value == '!') {
type = COUNT_NONE; type = COUNT_NONE;
p++; value++;
} }
for (j = 0; j < class_table_sz; j++) { 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; class_table[j].what = type;
break; break;
} }
} }
} else {
fprintf(stderr, "option parsing failed: %s\n", p);
return -1;
} }
} }

View File

@ -259,27 +259,42 @@ int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info,
int argc, char **argv) int argc, char **argv)
{ {
int i; int i;
g_autoptr(GString) matches_raw = g_string_new("");
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
char *opt = argv[i]; char *opt = argv[i];
if (g_strcmp0(opt, "read") == 0) { g_autofree char **tokens = g_strsplit(opt, "=", 2);
rw = QEMU_PLUGIN_MEM_R;
} else if (g_strcmp0(opt, "write") == 0) { if (g_strcmp0(tokens[0], "track") == 0) {
rw = QEMU_PLUGIN_MEM_W; if (g_strcmp0(tokens[1], "read") == 0) {
} else if (g_strcmp0(opt, "pattern") == 0) { rw = QEMU_PLUGIN_MEM_R;
pattern = true; } else if (g_strcmp0(tokens[1], "write") == 0) {
} else if (g_strcmp0(opt, "source") == 0) { rw = QEMU_PLUGIN_MEM_W;
source = true; } else {
} else if (g_str_has_prefix(opt, "match")) { fprintf(stderr, "invalid value for track: %s\n", tokens[1]);
gchar **parts = g_strsplit(opt, "=", 2); 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; check_match = true;
matches = g_strsplit(parts[1], ",", -1); g_string_append_printf(matches_raw, "%s,", tokens[1]);
g_strfreev(parts);
} else { } else {
fprintf(stderr, "option parsing failed: %s\n", opt); fprintf(stderr, "option parsing failed: %s\n", opt);
return -1; return -1;
} }
} }
if (check_match) {
matches = g_strsplit(matches_raw->str, ",", -1);
}
if (source && pattern) { if (source && pattern) {
fprintf(stderr, "can only currently track either source or pattern.\n"); fprintf(stderr, "can only currently track either source or pattern.\n");

View File

@ -319,22 +319,35 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
int argc, char **argv) int argc, char **argv)
{ {
int i; int i;
g_autofree char *sock_path = NULL;
if (!argc || !argv[0]) {
qemu_plugin_outs("Need a socket path to talk to other instance.");
return -1;
}
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
char *p = argv[i]; char *p = argv[i];
if (strcmp(p, "verbose") == 0) { g_autofree char **tokens = g_strsplit(p, "=", 2);
verbose = true;
} else if (!setup_unix_socket(argv[0])) { if (g_strcmp0(tokens[0], "verbose") == 0) {
qemu_plugin_outs("Failed to setup socket for communications."); 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; 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; our_id = id;
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);

View File

@ -139,6 +139,18 @@ The ``-no-quit`` is a synonym for ``-display ...,window-close=off`` which
should be used instead. 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 QEMU Machine Protocol (QMP) commands
------------------------------------ ------------------------------------

View File

@ -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 reach the timeout limit). Set this variable to have the tests using the
Avocado framework run automatically. 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".

View File

@ -80,7 +80,7 @@ Once built a program can be run with multiple plugins loaded each with
their own arguments:: their own arguments::
$QEMU $OTHER_QEMU_ARGS \ $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 -plugin tests/plugin/libhotblocks.so
Arguments are plugin specific and can be used to modify their 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 0x0000000048b000, 0x0001, 130594, 0x0001, 355
0x0000000048a000, 0x0001, 1826, 0x0001, 11 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 - contrib/plugins/howvec.c
This is an instruction classifier so can be used to count different 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 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 counted. You can give a value to the `count` argument for a class of
it down fully, so for example to see all the system registers instructions to break it down fully, so for example to see all the system
accesses:: registers accesses::
./aarch64-softmmu/qemu-system-aarch64 $(QEMU_ARGS) \ ./aarch64-softmmu/qemu-system-aarch64 $(QEMU_ARGS) \
-append "root=/dev/sda2 systemd.unit=benchmark.service" \ -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:: 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 \ ./sparc-softmmu/qemu-system-sparc -monitor none -parallel none \
-net none -M SS-20 -m 256 -kernel day11/zImage.elf \ -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 -d plugin,nochain
which will eventually report:: which will eventually report::
@ -286,27 +301,27 @@ which will eventually report::
previously @ 0x000000ffd08098/5 (809900593 insns) previously @ 0x000000ffd08098/5 (809900593 insns)
previously @ 0x000000ffd080c0/1 (809900588 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 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: 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 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. 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 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 cirrus-low-memory @ 0xfffffd00000a0000
pc:fffffc0000005cdc, 1, 256 pc:fffffc0000005cdc, 1, 256
pc:fffffc0000005ce8, 1, 256 pc:fffffc0000005ce8, 1, 256
pc:fffffc0000005cec, 1, 256 pc:fffffc0000005cec, 1, 256
* arg=pattern * pattern
Instead break down the accesses based on the offset into the HW 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 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, 0xd34, 0xf9c8f000, "bl #0x10c8"
0, 0x10c8, 0xfff96c43, "ldr r3, [r0, #0x44]", load, 0x200000e4, RAM 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 Cache modelling plugin that measures the performance of a given cache
configuration when a given working set is run:: configuration when a given working set is run::
@ -355,11 +370,8 @@ configuration when a given working set is run::
will report the following:: will report the following::
Data accesses: 996479, Misses: 507 core #, data accesses, data misses, dmiss rate, insn accesses, insn misses, imiss rate
Miss rate: 0.050879% 0 996695 508 0.0510% 2642799 18617 0.7044%
Instruction accesses: 2641737, Misses: 18617
Miss rate: 0.704726%
address, data misses, instruction address, data misses, instruction
0x424f1e (_int_malloc), 109, movq %rax, 8(%rcx) 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: 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 Print top N icache and dcache thrashing instructions along with their
address, number of misses, and its disassembly. (default: 32) address, number of misses, and its disassembly. (default: 32)
* arg="icachesize=N" * icachesize=N
* arg="iblksize=B" * iblksize=B
* arg="iassoc=A" * iassoc=A
Instruction cache configuration arguments. They specify the cache size, block Instruction cache configuration arguments. They specify the cache size, block
size, and associativity of the instruction cache, respectively. size, and associativity of the instruction cache, respectively.
(default: N = 16384, B = 64, A = 8) (default: N = 16384, B = 64, A = 8)
* arg="dcachesize=N" * dcachesize=N
* arg="dblksize=B" * dblksize=B
* arg="dassoc=A" * dassoc=A
Data cache configuration arguments. They specify the cache size, block size, Data cache configuration arguments. They specify the cache size, block size,
and associativity of the data cache, respectively. and associativity of the data cache, respectively.
(default: N = 16384, B = 64, A = 8) (default: N = 16384, B = 64, A = 8)
* arg="evict=POLICY" * evict=POLICY
Sets the eviction policy to POLICY. Available policies are: :code:`lru`, Sets the eviction policy to POLICY. Available policies are: :code:`lru`,
:code:`fifo`, and :code:`rand`. The plugin will use the specified policy for :code:`fifo`, and :code:`rand`. The plugin will use the specified policy for
both instruction and data caches. (default: POLICY = :code:`lru`) 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)

View File

@ -577,4 +577,17 @@ int qemu_plugin_n_max_vcpus(void);
*/ */
void qemu_plugin_outs(const char *string); 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 */ #endif /* QEMU_PLUGIN_API_H */

View File

@ -463,7 +463,7 @@ static const struct qemu_argument arg_table[] = {
"", "[[enable=]<pattern>][,events=<file>][,file=<file>]"}, "", "[[enable=]<pattern>][,events=<file>][,file=<file>]"},
#ifdef CONFIG_PLUGIN #ifdef CONFIG_PLUGIN
{"plugin", "QEMU_PLUGIN", true, handle_arg_plugin, {"plugin", "QEMU_PLUGIN", true, handle_arg_plugin,
"", "[file=]<file>[,arg=<string>]"}, "", "[file=]<file>[,<argname>=<argvalue>]"},
#endif #endif
{"version", "QEMU_VERSION", false, handle_arg_version, {"version", "QEMU_VERSION", false, handle_arg_version,
"", "display version information and exit"}, "", "display version information and exit"},

View File

@ -383,3 +383,8 @@ void qemu_plugin_outs(const char *string)
{ {
qemu_log_mask(CPU_LOG_PLUGIN, "%s", 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);
}

View File

@ -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_parse_arg *arg = opaque;
struct qemu_plugin_desc *p; struct qemu_plugin_desc *p;
bool is_on;
char *fullarg;
if (strcmp(name, "file") == 0) { if (strcmp(name, "file") == 0) {
if (strcmp(value, "") == 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); QTAILQ_INSERT_TAIL(arg->head, p, entry);
} }
arg->curr = p; arg->curr = p;
} else if (strcmp(name, "arg") == 0) { } else {
if (arg->curr == NULL) { if (arg->curr == NULL) {
error_setg(errp, "missing earlier '-plugin file=' option"); error_setg(errp, "missing earlier '-plugin file=' option");
return 1; 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 = arg->curr;
p->argc++; p->argc++;
p->argv = g_realloc_n(p->argv, p->argc, sizeof(char *)); p->argv = g_realloc_n(p->argv, p->argc, sizeof(char *));
p->argv[p->argc - 1] = g_strdup(value); p->argv[p->argc - 1] = fullarg;
} else {
error_setg(errp, "-plugin: unexpected parameter '%s'; ignored", name);
} }
return 0; return 0;
} }

View File

@ -1,9 +1,11 @@
if 'CONFIG_HAS_LD_DYNAMIC_LIST' in config_host plugin_ldflags = []
plugin_ldflags = ['-Wl,--dynamic-list=' + (meson.build_root() / 'qemu-plugins-ld.symbols')] # Modules need more symbols than just those in plugins/qemu-plugins.symbols
elif 'CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST' in config_host if not enable_modules
plugin_ldflags = ['-Wl,-exported_symbols_list,' + (meson.build_root() / 'qemu-plugins-ld64.symbols')] if 'CONFIG_HAS_LD_DYNAMIC_LIST' in config_host
else plugin_ldflags = ['-Wl,--dynamic-list=' + (meson.build_root() / 'qemu-plugins-ld.symbols')]
plugin_ldflags = [] 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 endif
specific_ss.add(when: 'CONFIG_PLUGIN', if_true: [files( specific_ss.add(when: 'CONFIG_PLUGIN', if_true: [files(

View File

@ -1,37 +1,38 @@
{ {
qemu_plugin_uninstall; qemu_plugin_bool_parse;
qemu_plugin_reset; qemu_plugin_get_hwaddr;
qemu_plugin_register_vcpu_init_cb; 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_exit_cb;
qemu_plugin_register_vcpu_idle_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_cb;
qemu_plugin_register_vcpu_insn_exec_inline; qemu_plugin_register_vcpu_insn_exec_inline;
qemu_plugin_register_vcpu_mem_cb; qemu_plugin_register_vcpu_mem_cb;
qemu_plugin_register_vcpu_mem_inline; qemu_plugin_register_vcpu_mem_inline;
qemu_plugin_register_vcpu_tb_trans_cb; qemu_plugin_register_vcpu_resume_cb;
qemu_plugin_register_vcpu_tb_exec_cb;
qemu_plugin_register_vcpu_tb_exec_inline;
qemu_plugin_register_flush_cb;
qemu_plugin_register_vcpu_syscall_cb; qemu_plugin_register_vcpu_syscall_cb;
qemu_plugin_register_vcpu_syscall_ret_cb; qemu_plugin_register_vcpu_syscall_ret_cb;
qemu_plugin_register_atexit_cb; qemu_plugin_register_vcpu_tb_exec_cb;
qemu_plugin_tb_n_insns; 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_get_insn;
qemu_plugin_tb_n_insns;
qemu_plugin_tb_vaddr; qemu_plugin_tb_vaddr;
qemu_plugin_insn_data; qemu_plugin_uninstall;
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_vcpu_for_each; qemu_plugin_vcpu_for_each;
qemu_plugin_n_vcpus;
qemu_plugin_n_max_vcpus;
qemu_plugin_outs;
}; };

View File

@ -4532,19 +4532,18 @@ SRST
ERST ERST
DEF("plugin", HAS_ARG, QEMU_OPTION_plugin, DEF("plugin", HAS_ARG, QEMU_OPTION_plugin,
"-plugin [file=]<file>[,arg=<string>]\n" "-plugin [file=]<file>[,<argname>=<argvalue>]\n"
" load a plugin\n", " load a plugin\n",
QEMU_ARCH_ALL) QEMU_ARCH_ALL)
SRST SRST
``-plugin file=file[,arg=string]`` ``-plugin file=file[,argname=argvalue]``
Load a plugin. Load a plugin.
``file=file`` ``file=file``
Load the given plugin from a shared library file. Load the given plugin from a shared library file.
``arg=string`` ``argname=argvalue``
Argument string passed to the plugin. (Can be given multiple Argument passed to the plugin. (Can be given multiple times.)
times.)
ERST ERST
HXCOMM Internal use HXCOMM Internal use

View File

@ -104,10 +104,17 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
for (i = 0; i < argc; i++) { for (i = 0; i < argc; i++) {
char *opt = argv[i]; char *opt = argv[i];
if (g_strcmp0(opt, "inline") == 0) { g_autofree char **tokens = g_strsplit(opt, "=", 2);
do_inline = true; if (g_strcmp0(tokens[0], "inline") == 0) {
} else if (g_strcmp0(opt, "idle") == 0) { if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_inline)) {
idle_report = true; 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 { } else {
fprintf(stderr, "option parsing failed: %s\n", opt); fprintf(stderr, "option parsing failed: %s\n", opt);
return -1; return -1;

View File

@ -62,8 +62,18 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
const qemu_info_t *info, const qemu_info_t *info,
int argc, char **argv) int argc, char **argv)
{ {
if (argc && !strcmp(argv[0], "inline")) { for (int i = 0; i < argc; i++) {
do_inline = true; 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); qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);

View File

@ -80,29 +80,40 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
const qemu_info_t *info, const qemu_info_t *info,
int argc, char **argv) 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++) {
rw = QEMU_PLUGIN_MEM_R; char *opt = argv[i];
} else if (!strcmp(str, "w")) { g_autofree char **tokens = g_strsplit(opt, "=", 2);
rw = QEMU_PLUGIN_MEM_W;
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 (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 { } else {
do_callback = true; fprintf(stderr, "option parsing failed: %s\n", opt);
return -1;
} }
} }

View File

@ -119,17 +119,26 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
const qemu_info_t *info, const qemu_info_t *info,
int argc, char **argv) int argc, char **argv)
{ {
if (argc == 0) { bool do_print = false;
statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free);
} else { for (int i = 0; i < argc; i++) {
for (int i = 0; i < argc; i++) { char *opt = argv[i];
if (g_strcmp0(argv[i], "print") != 0) { g_autofree char **tokens = g_strsplit(opt, "=", 2);
fprintf(stderr, "unsupported argument: %s\n", argv[i]);
return -1; 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_cb(id, vcpu_syscall);
qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret); qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);

View File

@ -38,7 +38,7 @@ run-plugin-%-with-libinsn.so:
$(call run-test, $@, \ $(call run-test, $@, \
$(QEMU) -monitor none -display none \ $(QEMU) -monitor none -display none \
-chardev file$(COMMA)path=$@.out$(COMMA)id=output \ -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 \ -d plugin -D $*-with-libinsn.so.pout \
$(QEMU_OPTS) $*, \ $(QEMU_OPTS) $*, \
"$* on $(TARGET_NAME)") "$* on $(TARGET_NAME)")

View File

@ -61,7 +61,7 @@ endif
# non-inline runs will trigger the duplicate instruction heuristics in libinsn.so # non-inline runs will trigger the duplicate instruction heuristics in libinsn.so
run-plugin-%-with-libinsn.so: run-plugin-%-with-libinsn.so:
$(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ $(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 $*, \ -d plugin -D $*-with-libinsn.so.pout $*, \
"$* (inline) on $(TARGET_NAME)") "$* (inline) on $(TARGET_NAME)")

View File

@ -38,7 +38,7 @@ run-plugin-%-with-libinsn.so:
$(call run-test, $@, \ $(call run-test, $@, \
$(QEMU) -monitor none -display none \ $(QEMU) -monitor none -display none \
-chardev file$(COMMA)path=$@.out$(COMMA)id=output \ -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 \ -d plugin -D $*-with-libinsn.so.pout \
$(QEMU_OPTS) $*, \ $(QEMU_OPTS) $*, \
"$* on $(TARGET_NAME)") "$* on $(TARGET_NAME)")