Akihiko Odaki <akihiko.odaki@daynix.com> writes:
This demonstrates how a register can be read from a plugin.
Signed-off-by: Akihiko Odaki <akihiko.odaki@daynix.com>
---
docs/devel/tcg-plugins.rst | 10 ++-
contrib/plugins/execlog.c | 140 ++++++++++++++++++++++++++++---------
2 files changed, 117 insertions(+), 33 deletions(-)
diff --git a/docs/devel/tcg-plugins.rst b/docs/devel/tcg-plugins.rst
index 81dcd43a61..c9f8b27590 100644
--- a/docs/devel/tcg-plugins.rst
+++ b/docs/devel/tcg-plugins.rst
@@ -497,6 +497,15 @@ arguments if required::
$ qemu-system-arm $(QEMU_ARGS) \
-plugin ./contrib/plugins/libexeclog.so,ifilter=st1w,afilter=0x40001808
-d plugin
+This plugin can also dump a specified register. The specification of register
+follows `GDB standard target features
<https://sourceware.org/gdb/onlinedocs/gdb/Standard-Target-Features.html>`__.
+
+Specify the name of the feature that contains the register and the name of the
+register with ``rfile`` and ``reg`` options, respectively::
+
+ $ qemu-system-arm $(QEMU_ARGS) \
+ -plugin ./contrib/plugins/libexeclog.so,rfile=org.gnu.gdb.arm.core,reg=sp
-d plugin
+
- contrib/plugins/cache.c
Cache modelling plugin that measures the performance of a given L1 cache
@@ -583,4 +592,3 @@ The following API is generated from the inline
documentation in
include the full kernel-doc annotations.
.. kernel-doc:: include/qemu/qemu-plugin.h
-
diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c
index 82dc2f584e..aa05840fd0 100644
--- a/contrib/plugins/execlog.c
+++ b/contrib/plugins/execlog.c
@@ -15,27 +15,43 @@
#include <qemu-plugin.h>
+typedef struct CPU {
+ /* Store last executed instruction on each vCPU as a GString */
+ GString *last_exec;
+ GByteArray *reg_history[2];
+
+ int reg;
+} CPU;
+
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
<snip>
/* Store new instruction in cache */
/* vcpu_mem will add memory access information to last_exec */
- g_string_printf(s, "%u, ", cpu_index);
- g_string_append(s, (char *)udata);
+ g_string_printf(cpus[cpu_index].last_exec, "%u, ", cpu_index);
+ g_string_append(cpus[cpu_index].last_exec, (char *)udata);
+
+ g_rw_lock_reader_unlock(&expand_array_lock);
}
/**
@@ -167,8 +197,10 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct
qemu_plugin_tb *tb)
QEMU_PLUGIN_MEM_RW, NULL);
/* Register callback on instruction */
- qemu_plugin_register_vcpu_insn_exec_cb(insn, vcpu_insn_exec,
- QEMU_PLUGIN_CB_NO_REGS,
output);
+ qemu_plugin_register_vcpu_insn_exec_cb(
+ insn, vcpu_insn_exec,
+ rfile_name ? QEMU_PLUGIN_CB_R_REGS : QEMU_PLUGIN_CB_NO_REGS,
+ output);
/* reset skip */
skip = (imatches || amatches);
@@ -177,17 +209,53 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct
qemu_plugin_tb *tb)
}
}
+static void vcpu_init(qemu_plugin_id_t id, unsigned int vcpu_index)
+{
+ int reg = 0;
+ bool found = false;
+
+ expand_cpu(vcpu_index);
+
+ if (rfile_name) {
+ int i;
+ int j;
+ int n;
+
+ qemu_plugin_register_file_t *rfiles =
+ qemu_plugin_get_register_files(vcpu_index, &n);
+
+ for (i = 0; i < n; i++) {
+ if (g_strcmp0(rfiles[i].name, rfile_name) == 0) {
+ for (j = 0; j < rfiles[i].num_regs; j++) {
+ if (g_strcmp0(rfiles[i].regs[j], reg_name) == 0) {
+ reg += j;
+ found = true;
+ break;
+ }
+ }
+ break;
+ }
+
+ reg += rfiles[i].num_regs;
+ }
+
+ g_free(rfiles);
+ }
This makes me question the value of exposing the register file directly
to the plugin. I would much rather have a lookup utility function with
an optional tag. Something like:
plugin_reg_t qemu_plugin_find_register(const char *name, const char *tag);
And make tag optional. I think in the general case "name" should be enough.
+
+ g_rw_lock_writer_lock(&expand_array_lock);
+ cpus[vcpu_index].reg = found ? reg : -1;
+ g_rw_lock_writer_unlock(&expand_array_lock);
+}
+
/**
* On plugin exit, print last instruction in cache
*/
static void plugin_exit(qemu_plugin_id_t id, void *p)
{
guint i;
- GString *s;
- for (i = 0; i < last_exec->len; i++) {
- s = g_ptr_array_index(last_exec, i);
- if (s->str) {
- qemu_plugin_outs(s->str);
+ for (i = 0; i < num_cpus; i++) {
+ if (cpus[i].last_exec->str) {
+ qemu_plugin_outs(cpus[i].last_exec->str);
qemu_plugin_outs("\n");
}
}
@@ -224,9 +292,7 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t
id,
* we don't know the size before emulation.
*/
if (info->system_emulation) {
- last_exec = g_ptr_array_sized_new(info->system.max_vcpus);
- } else {
- last_exec = g_ptr_array_new();
+ cpus = g_new(CPU, info->system.max_vcpus);
}
for (int i = 0; i < argc; i++) {
@@ -236,13 +302,23 @@ QEMU_PLUGIN_EXPORT int
qemu_plugin_install(qemu_plugin_id_t id,
parse_insn_match(tokens[1]);
} else if (g_strcmp0(tokens[0], "afilter") == 0) {
parse_vaddr_match(tokens[1]);
+ } else if (g_strcmp0(tokens[0], "rfile") == 0) {
+ rfile_name = g_strdup(tokens[1]);
+ } else if (g_strcmp0(tokens[0], "reg") == 0) {
+ reg_name = g_strdup(tokens[1]);
And then instead of having the rfile/reg distinction support a command
line like:
qemu-aarch64 -plugin contrib/plugins/libexeclog.so,reg=sp,reg=x1,reg=sve:p1
so if the user specifies a reg of the form TAG:REG we can pass that as
the option tag string. It also avoids exposing all the details of gdb to
plugins while still allowing the utility function to search by rname
internally (even if only a substring match?),
} else {
fprintf(stderr, "option parsing failed: %s\n", opt);
return -1;
}
}
+ if ((!rfile_name) != (!reg_name)) {
+ fputs("file and reg need to be set at the same time\n", stderr);
+ return -1;
+ }
+
/* Register translation block and exit callbacks */
+ qemu_plugin_register_vcpu_init_cb(id, vcpu_init);
qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);