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


Reply via email to