From: "Alex Bennée" <alex.bennee@linaro.org>
To: qemu-devel@nongnu.org
Cc: "Liu Zhiwei" <zhiwei_liu@linux.alibaba.com>,
"Alexandre Iooss" <erdnaxe@crans.org>,
"Chinmay Rath" <rathc@linux.ibm.com>,
qemu-arm@nongnu.org, "Peter Maydell" <peter.maydell@linaro.org>,
"Nicholas Piggin" <npiggin@gmail.com>,
"Ilya Leoshkevich" <iii@linux.ibm.com>,
"David Hildenbrand" <david@redhat.com>,
qemu-ppc@nongnu.org, "Paolo Bonzini" <pbonzini@redhat.com>,
qemu-s390x@nongnu.org,
"Pierrick Bouvier" <pierrick.bouvier@linaro.org>,
"Bastian Koppelmann" <kbastian@mail.uni-paderborn.de>,
"Helge Deller" <deller@gmx.de>,
"Alex Bennée" <alex.bennee@linaro.org>,
"Stafford Horne" <shorne@gmail.com>,
"Mahmoud Mandour" <ma.mandourr@gmail.com>,
"Artyom Tarasenko" <atar4qemu@gmail.com>,
"Eduardo Habkost" <eduardo@habkost.net>,
"Palmer Dabbelt" <palmer@dabbelt.com>,
"Alistair Francis" <alistair.francis@wdc.com>,
"Laurent Vivier" <laurent@vivier.eu>,
"Weiwei Li" <liwei1518@gmail.com>,
"Daniel Henrique Barboza" <dbarboza@ventanamicro.com>,
"Mark Cave-Ayland" <mark.cave-ayland@ilande.co.uk>,
"Yoshinori Sato" <yoshinori.sato@nifty.com>,
"Edgar E. Iglesias" <edgar.iglesias@gmail.com>,
"Aurelien Jarno" <aurelien@aurel32.net>,
"Richard Henderson" <richard.henderson@linaro.org>,
"Thomas Huth" <thuth@redhat.com>,
"Max Filippov" <jcmvbkbc@gmail.com>,
"Philippe Mathieu-Daudé" <philmd@linaro.org>,
"Jiaxun Yang" <jiaxun.yang@flygoat.com>,
"Michael Rolnik" <mrolnik@gmail.com>,
"Song Gao" <gaosong@loongson.cn>,
qemu-riscv@nongnu.org, "Aleksandar Rikalo" <arikalo@gmail.com>,
"Julian Ganz" <neither@nut.email>
Subject: [PATCH 29/35] tests: add plugin asserting correctness of discon event's to_pc
Date: Mon, 27 Oct 2025 11:03:36 +0000 [thread overview]
Message-ID: <20251027110344.2289945-30-alex.bennee@linaro.org> (raw)
In-Reply-To: <20251027110344.2289945-1-alex.bennee@linaro.org>
From: Julian Ganz <neither@nut.email>
We recently introduced plugin API for the registration of callbacks for
discontinuity events, specifically for interrupts, exceptions and host
call events. The callback receives various bits of information,
including the VCPU index and PCs.
This change introduces a test plugin asserting the correctness of that
behaviour in cases where this is possible with reasonable effort.
Reviewed-by: Pierrick Bouvier <pierrick.bouvier@linaro.org>
Signed-off-by: Julian Ganz <neither@nut.email>
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
---
tests/tcg/plugins/discons.c | 221 ++++++++++++++++++++++++++++++++++
tests/tcg/plugins/meson.build | 2 +-
2 files changed, 222 insertions(+), 1 deletion(-)
create mode 100644 tests/tcg/plugins/discons.c
diff --git a/tests/tcg/plugins/discons.c b/tests/tcg/plugins/discons.c
new file mode 100644
index 00000000000..2e0e664e823
--- /dev/null
+++ b/tests/tcg/plugins/discons.c
@@ -0,0 +1,221 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright (C) 2025, Julian Ganz <neither@nut.email>
+ *
+ * This plugin exercises the discontinuity plugin API and asserts some
+ * of its behaviour regarding reported program counters.
+ */
+#include <stdio.h>
+
+#include <qemu-plugin.h>
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+
+struct cpu_state {
+ uint64_t last_pc;
+ uint64_t from_pc;
+ uint64_t next_pc;
+ uint64_t has_from;
+ bool has_next;
+ enum qemu_plugin_discon_type next_type;
+};
+
+struct insn_data {
+ uint64_t addr;
+ uint64_t next_pc;
+ bool next_valid;
+};
+
+static struct qemu_plugin_scoreboard *states;
+
+static qemu_plugin_u64 last_pc;
+static qemu_plugin_u64 from_pc;
+static qemu_plugin_u64 has_from;
+
+static bool abort_on_mismatch;
+static bool trace_all_insns;
+
+static bool addr_eq(uint64_t a, uint64_t b)
+{
+ if (a == b) {
+ return true;
+ }
+
+ uint64_t a_hw;
+ uint64_t b_hw;
+ if (!qemu_plugin_translate_vaddr(a, &a_hw) ||
+ !qemu_plugin_translate_vaddr(b, &b_hw))
+ {
+ return false;
+ }
+
+ return a_hw == b_hw;
+}
+
+static void report_mismatch(const char *pc_name, unsigned int vcpu_index,
+ enum qemu_plugin_discon_type type, uint64_t last,
+ uint64_t expected, uint64_t encountered)
+{
+ gchar *report;
+ const char *discon_type_name = "unknown";
+
+ if (addr_eq(expected, encountered)) {
+ return;
+ }
+
+ switch (type) {
+ case QEMU_PLUGIN_DISCON_INTERRUPT:
+ discon_type_name = "interrupt";
+ break;
+ case QEMU_PLUGIN_DISCON_EXCEPTION:
+ discon_type_name = "exception";
+ break;
+ case QEMU_PLUGIN_DISCON_HOSTCALL:
+ discon_type_name = "hostcall";
+ break;
+ default:
+ break;
+ }
+
+ report = g_strdup_printf("Discon %s PC mismatch on VCPU %d\n"
+ "Expected: %"PRIx64"\nEncountered: %"
+ PRIx64"\nExecuted Last: %"PRIx64
+ "\nEvent type: %s\n",
+ pc_name, vcpu_index, expected, encountered, last,
+ discon_type_name);
+ if (abort_on_mismatch) {
+ /*
+ * The qemu log infrastructure may lose messages when aborting. Using
+ * fputs directly ensures the final report is visible to developers.
+ */
+ fputs(report, stderr);
+ g_abort();
+ } else {
+ qemu_plugin_outs(report);
+ }
+ g_free(report);
+}
+
+static void vcpu_discon(qemu_plugin_id_t id, unsigned int vcpu_index,
+ enum qemu_plugin_discon_type type, uint64_t from_pc,
+ uint64_t to_pc)
+{
+ struct cpu_state *state = qemu_plugin_scoreboard_find(states, vcpu_index);
+
+ if (type == QEMU_PLUGIN_DISCON_EXCEPTION &&
+ addr_eq(state->last_pc, from_pc))
+ {
+ /*
+ * For some types of exceptions, insn_exec will be called for the
+ * instruction that caused the exception. This is valid behaviour and
+ * does not need to be reported.
+ */
+ } else if (state->has_next) {
+ /*
+ * We may encounter discontinuity chains without any instructions
+ * being executed in between.
+ */
+ report_mismatch("source", vcpu_index, type, state->last_pc,
+ state->next_pc, from_pc);
+ } else if (state->has_from) {
+ report_mismatch("source", vcpu_index, type, state->last_pc,
+ state->from_pc, from_pc);
+ }
+
+ state->has_from = false;
+
+ state->next_pc = to_pc;
+ state->next_type = type;
+ state->has_next = true;
+}
+
+static void insn_exec(unsigned int vcpu_index, void *userdata)
+{
+ struct cpu_state *state = qemu_plugin_scoreboard_find(states, vcpu_index);
+
+ if (state->has_next) {
+ report_mismatch("target", vcpu_index, state->next_type, state->last_pc,
+ state->next_pc, state->last_pc);
+ state->has_next = false;
+ }
+
+ if (trace_all_insns) {
+ g_autoptr(GString) report = g_string_new(NULL);
+ g_string_append_printf(report, "Exec insn at %"PRIx64" on VCPU %d\n",
+ state->last_pc, vcpu_index);
+ qemu_plugin_outs(report->str);
+ }
+}
+
+static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb)
+{
+ size_t n_insns = qemu_plugin_tb_n_insns(tb);
+ for (size_t i = 0; i < n_insns; i++) {
+ struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i);
+ uint64_t pc = qemu_plugin_insn_vaddr(insn);
+ uint64_t next_pc = pc + qemu_plugin_insn_size(insn);
+ uint64_t has_next = (i + 1) < n_insns;
+
+ qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn,
+ QEMU_PLUGIN_INLINE_STORE_U64,
+ last_pc, pc);
+ qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn,
+ QEMU_PLUGIN_INLINE_STORE_U64,
+ from_pc, next_pc);
+ qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn,
+ QEMU_PLUGIN_INLINE_STORE_U64,
+ has_from, has_next);
+ qemu_plugin_register_vcpu_insn_exec_cb(insn, insn_exec,
+ QEMU_PLUGIN_CB_NO_REGS, NULL);
+ }
+}
+
+QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
+ const qemu_info_t *info,
+ int argc, char **argv)
+{
+ if (!info->system_emulation) {
+ qemu_plugin_outs("Testing of the disontinuity plugin API is only"
+ " possible in system emulation mode.");
+ return 0;
+ }
+
+ /* Set defaults */
+ abort_on_mismatch = true;
+ trace_all_insns = false;
+
+ for (int i = 0; i < argc; i++) {
+ char *opt = argv[i];
+ g_auto(GStrv) tokens = g_strsplit(opt, "=", 2);
+ if (g_strcmp0(tokens[0], "abort") == 0) {
+ if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
+ &abort_on_mismatch)) {
+ fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
+ return -1;
+ }
+ } else if (g_strcmp0(tokens[0], "trace-all") == 0) {
+ if (!qemu_plugin_bool_parse(tokens[0], tokens[1],
+ &trace_all_insns)) {
+ fprintf(stderr, "boolean argument parsing failed: %s\n", opt);
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "option parsing failed: %s\n", opt);
+ return -1;
+ }
+ }
+
+ states = qemu_plugin_scoreboard_new(sizeof(struct cpu_state));
+ last_pc = qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_state,
+ last_pc);
+ from_pc = qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_state,
+ from_pc);
+ has_from = qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_state,
+ has_from);
+
+ qemu_plugin_register_vcpu_discon_cb(id, QEMU_PLUGIN_DISCON_ALL,
+ vcpu_discon);
+ qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans);
+
+ return 0;
+}
diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build
index 61a007d9e74..561584159eb 100644
--- a/tests/tcg/plugins/meson.build
+++ b/tests/tcg/plugins/meson.build
@@ -1,6 +1,6 @@
t = []
if get_option('plugins')
- foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall', 'patch']
+ foreach i : ['bb', 'discons', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall', 'patch']
if host_os == 'windows'
t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c',
include_directories: '../../../include/qemu',
--
2.47.3
next prev parent reply other threads:[~2025-10-27 11:06 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-27 11:03 [PATCH 00/35] maintainer updates for 8.2 softfeeeze (ci, plugins, semihosting) pre-PR Alex Bennée
2025-10-27 11:03 ` [PATCH 01/35] ci: clean-up remaining bits of armhf builds Alex Bennée
2025-10-27 11:20 ` Philippe Mathieu-Daudé
2025-10-27 11:03 ` [PATCH 02/35] scripts/ci/setup: regenerate yaml Alex Bennée
2025-10-27 11:03 ` [PATCH 03/35] scripts/ci: move build-environment.yaml up a level Alex Bennée
2025-10-27 11:03 ` [PATCH 04/35] scripts/ci: allow both Ubuntu or Debian to run upgrade Alex Bennée
2025-10-27 11:03 ` [PATCH 05/35] tests/lcitool: generate a yaml file for the ppc64le runner Alex Bennée
2025-10-27 11:03 ` [PATCH 06/35] scripts/ci: modify gitlab runner deb setup Alex Bennée
2025-10-27 11:03 ` [PATCH 07/35] plugins: add types for callbacks related to certain discontinuities Alex Bennée
2025-10-27 11:03 ` [PATCH 08/35] plugins: add API for registering discontinuity callbacks Alex Bennée
2025-10-27 11:03 ` [PATCH 09/35] plugins: add hooks for new discontinuity related callbacks Alex Bennée
2025-10-27 11:03 ` [PATCH 10/35] contrib/plugins: add plugin showcasing new dicontinuity related API Alex Bennée
2025-10-27 11:03 ` [PATCH 11/35] target/alpha: call plugin trap callbacks Alex Bennée
2025-10-27 11:03 ` [PATCH 12/35] target/arm: " Alex Bennée
2025-10-27 11:03 ` [PATCH 13/35] target/avr: " Alex Bennée
2025-10-27 11:03 ` [PATCH 14/35] target/hppa: " Alex Bennée
2025-10-27 11:03 ` [PATCH 15/35] target/i386: " Alex Bennée
2025-10-27 11:03 ` [PATCH 16/35] target/loongarch: " Alex Bennée
2025-10-27 11:03 ` [PATCH 17/35] target/m68k: " Alex Bennée
2025-10-27 11:03 ` [PATCH 18/35] target/microblaze: " Alex Bennée
2025-10-27 11:03 ` [PATCH 19/35] target/mips: " Alex Bennée
2025-10-27 11:03 ` [PATCH 20/35] target/openrisc: " Alex Bennée
2025-10-27 11:03 ` [PATCH 21/35] target/ppc: " Alex Bennée
2025-10-27 11:03 ` [PATCH 22/35] target/riscv: " Alex Bennée
2025-10-27 11:03 ` [PATCH 23/35] target/rx: " Alex Bennée
2025-10-27 11:03 ` [PATCH 24/35] target/s390x: " Alex Bennée
2025-10-27 11:03 ` [PATCH 25/35] target/sh4: " Alex Bennée
2025-10-27 11:03 ` [PATCH 26/35] target/sparc: " Alex Bennée
2025-10-27 11:03 ` [PATCH 27/35] target/tricore: " Alex Bennée
2025-10-27 11:03 ` [PATCH 28/35] target/xtensa: " Alex Bennée
2025-10-27 11:03 ` Alex Bennée [this message]
2025-10-27 11:03 ` [PATCH 30/35] tests: add test for double-traps on rv64 Alex Bennée
2025-10-27 11:03 ` [PATCH 31/35] tests: add test with interrupted memory accesses " Alex Bennée
2025-10-27 11:03 ` [PATCH 32/35] plugins/core: add missing QEMU_DISABLE_CFI annotations Alex Bennée
2025-10-27 11:03 ` [PATCH 33/35] configs: drop SBSA_REF from minimal specification Alex Bennée
2025-10-27 11:03 ` [PATCH 34/35] gdbstub: Fix %s formatting Alex Bennée
2025-10-27 11:18 ` Philippe Mathieu-Daudé
2025-10-27 11:03 ` [PATCH 35/35] semihosting: Fix GDB File-I/O FLEN Alex Bennée
2025-10-27 11:29 ` [PATCH 00/35] maintainer updates for 10.2 softfeeeze (ci, plugins, semihosting) pre-PR Alex Bennée
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251027110344.2289945-30-alex.bennee@linaro.org \
--to=alex.bennee@linaro.org \
--cc=alistair.francis@wdc.com \
--cc=arikalo@gmail.com \
--cc=atar4qemu@gmail.com \
--cc=aurelien@aurel32.net \
--cc=david@redhat.com \
--cc=dbarboza@ventanamicro.com \
--cc=deller@gmx.de \
--cc=edgar.iglesias@gmail.com \
--cc=eduardo@habkost.net \
--cc=erdnaxe@crans.org \
--cc=gaosong@loongson.cn \
--cc=iii@linux.ibm.com \
--cc=jcmvbkbc@gmail.com \
--cc=jiaxun.yang@flygoat.com \
--cc=kbastian@mail.uni-paderborn.de \
--cc=laurent@vivier.eu \
--cc=liwei1518@gmail.com \
--cc=ma.mandourr@gmail.com \
--cc=mark.cave-ayland@ilande.co.uk \
--cc=mrolnik@gmail.com \
--cc=neither@nut.email \
--cc=npiggin@gmail.com \
--cc=palmer@dabbelt.com \
--cc=pbonzini@redhat.com \
--cc=peter.maydell@linaro.org \
--cc=philmd@linaro.org \
--cc=pierrick.bouvier@linaro.org \
--cc=qemu-arm@nongnu.org \
--cc=qemu-devel@nongnu.org \
--cc=qemu-ppc@nongnu.org \
--cc=qemu-riscv@nongnu.org \
--cc=qemu-s390x@nongnu.org \
--cc=rathc@linux.ibm.com \
--cc=richard.henderson@linaro.org \
--cc=shorne@gmail.com \
--cc=thuth@redhat.com \
--cc=yoshinori.sato@nifty.com \
--cc=zhiwei_liu@linux.alibaba.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).