From: Oleg Vasilev <vasilev.o...@huawei.com> Plugin can be used to track statistics based on virtual address mask matching. Useful for tracking kernel vs user translation blocks.
Signed-off-by: Oleg Vasilev <vasilev.o...@huawei.com> Signed-off-by: Oleg Vasilev <m...@svin.in> --- contrib/plugins/Makefile | 1 + contrib/plugins/mask.c | 144 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 contrib/plugins/mask.c diff --git a/contrib/plugins/Makefile b/contrib/plugins/Makefile index 54ac5ccd9f..7e9cb51c9d 100644 --- a/contrib/plugins/Makefile +++ b/contrib/plugins/Makefile @@ -20,6 +20,7 @@ NAMES += howvec NAMES += lockstep NAMES += hwprofile NAMES += cache +NAMES += mask SONAMES := $(addsuffix .so,$(addprefix lib,$(NAMES))) diff --git a/contrib/plugins/mask.c b/contrib/plugins/mask.c new file mode 100644 index 0000000000..c6d2bd2386 --- /dev/null +++ b/contrib/plugins/mask.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2022, Oleg Vasilev <vasilev.o...@huawei.com> + * + * Track statistics based on virtual address mask matching. + * Useful for tracking kernel vs user translation blocks. + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <assert.h> +#include <compiler.h> +#include <glib.h> +#include <inttypes.h> +#include <qemu-plugin.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atomic.h> + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +typedef struct { + GMutex lock; + const char *hint; + uint64_t mask; + uint64_t bits; + uint64_t tb_exec; + uint64_t tb_trans; + uint64_t isins; +} MaskCounter; + +static GPtrArray *counters; + +static uint64_t report_every = 1 << 28; +static uint64_t tb_exec_every = 1 << 10; +static uint64_t total_tb_exec; + +static void gen_one_report(MaskCounter *counter, GString *report) +{ + g_mutex_lock(&counter->lock); + uint64_t tb_exec = counter->tb_exec * tb_exec_every; + + double hit_rate = (double)counter->tb_trans / tb_exec; + hit_rate = 1 - hit_rate; + + double mask_freq = (double) counter->tb_exec * tb_exec_every / report_every; + + g_string_append_printf(report, + "hint: %s, mask: 0x%016lx, bits: 0x%016lx, hit_rate: %f, " + "mask_freq: %f, tb_exec: %ld, tb_trans: %ld\n", + counter->hint, counter->mask, counter->bits, hit_rate, + mask_freq, tb_exec, counter->tb_trans); + + counter->tb_exec = 0; + counter->tb_trans = 0; + counter->isins = 0; + + g_mutex_unlock(&counter->lock); +} + +static void report_all(void) +{ + g_autoptr(GString) report = g_string_new(""); + g_ptr_array_foreach(counters, (GFunc)gen_one_report, report); + qemu_plugin_outs(report->str); +} + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + report_all(); +} + +static bool match(MaskCounter *counter, struct qemu_plugin_tb *tb) +{ + return (counter->mask & qemu_plugin_tb_vaddr(tb)) == counter->bits; +} + +static void tb_exec(MaskCounter *counter, struct qemu_plugin_tb *tb) +{ + if (!match(counter, tb)) { + return; + } + g_mutex_lock(&counter->lock); + counter->tb_exec++; + g_mutex_unlock(&counter->lock); +} + +static void vcpu_tb_exec(unsigned int cpu_index, void *tb) +{ + uint64_t cur_tb_exec = qatomic_fetch_inc(&total_tb_exec); + if ((cur_tb_exec & (tb_exec_every - 1)) == 0) { + g_ptr_array_foreach(counters, (GFunc)tb_exec, tb); + } + + if ((cur_tb_exec & (report_every - 1)) == 0) { + report_all(); + } +} + +static void tb_trans(MaskCounter *counter, struct qemu_plugin_tb *tb) +{ + if (!match(counter, tb)) { + return; + } + g_mutex_lock(&counter->lock); + counter->tb_trans++; + g_mutex_unlock(&counter->lock); +} + +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + qemu_plugin_register_vcpu_tb_exec_cb(tb, vcpu_tb_exec, + QEMU_PLUGIN_CB_NO_REGS, tb); + g_ptr_array_foreach(counters, (GFunc)tb_trans, tb); +} + +static void add_counter(const char *hint, uint64_t mask, uint64_t bits) +{ + MaskCounter *counter = g_new0(MaskCounter, 1); + counter->hint = hint; + counter->mask = mask; + counter->bits = bits; + g_mutex_init(&counter->lock); + g_ptr_array_add(counters, counter); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + counters = g_ptr_array_new(); + + // Update for a different mask + add_counter("all", 0, 0); + add_counter("kernel", 0x1ll << 63, 0x1ll << 63); + add_counter("user", 0x1ll << 63, 0); + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + return 0; +} -- 2.33.1