From nobody Sat Sep 6 17:35:42 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1757019134; cv=none; d=zohomail.com; s=zohoarc; b=OZ4ViDwB4P5TlnlU3ehFUED6RNI27GFqjpu8JPS0FCKy5aWDy2AMZAnMfL3xYZSJnO9zgOVJvFJaPIYre8oUFxzmig9V4hpd2pxjoKVGJqUwCQ7A7hxTZtgJ+9yrEceKKCqmx6f8uaTzjDPKDjme1e1ZKoUzTCgt13a/t8Ox2Io= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1757019134; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=D+NUJw7/ipgVmbuCF9DZaj1ptx17pcYNXQ4ea1Er9zc=; b=Sbx8KaowWYSw48J+h3EydGSY06G/qkjtZ/1ek+guCtsxrM9D+ctEN7uC5/Voq301Aqp22IiibTvh1vEWbe5ICwxgjxhOc1/3NrMyEUnHj9l79L1ydv/5Bl4aUKtVMdQ581X6UtgvQ8YQ4fd4madrSH0JN7rLabXy234HJsk0gIs= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1757019134313528.6699270460269; Thu, 4 Sep 2025 13:52:14 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1uuGu1-0006bX-I0; Thu, 04 Sep 2025 16:49:53 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1uuGti-0005ss-S0 for qemu-devel@nongnu.org; Thu, 04 Sep 2025 16:49:36 -0400 Received: from mailgate01.uberspace.is ([2001:1a50:11:0:c83f:a8ff:fea6:c8da]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1uuGtX-0000zw-VF for qemu-devel@nongnu.org; Thu, 04 Sep 2025 16:49:34 -0400 Received: from skiff.uberspace.de (skiff.uberspace.de [185.26.156.131]) by mailgate01.uberspace.is (Postfix) with ESMTPS id 4C3FA60C14 for ; Thu, 4 Sep 2025 22:49:18 +0200 (CEST) Received: (qmail 2422 invoked by uid 990); 4 Sep 2025 20:49:18 -0000 Received: from unknown (HELO unkown) (::1) by skiff.uberspace.de (Haraka/3.0.1) with ESMTPSA; Thu, 04 Sep 2025 22:49:18 +0200 Authentication-Results: skiff.uberspace.de; auth=pass (plain) From: Julian Ganz To: qemu-devel@nongnu.org Cc: Julian Ganz , Pierrick Bouvier , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Alexandre Iooss , Mahmoud Mandour Subject: [PATCH v6 23/25] tests: add plugin asserting correctness of discon event's to_pc Date: Thu, 4 Sep 2025 22:48:58 +0200 Message-ID: <073cad7dd8ae509ff64a2835fd146833b60c1f60.1757018626.git.neither@nut.email> X-Mailer: git-send-email 2.49.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Rspamd-Bar: ---- X-Rspamd-Report: REPLY(-4) SUSPICIOUS_RECIPS(1.5) MID_CONTAINS_FROM(1) MIME_GOOD(-0.1) BAYES_HAM(-2.999961) R_MISSING_CHARSET(0.5) X-Rspamd-Score: -4.099961 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nut.email; s=uberspace; h=from:to:cc:subject:date; bh=6bA+cSQ7uPfSE8TI1kgQE3TU/Q1amvHJS4CJP6CUHhY=; b=WTZ7xm88MIV6WY40wFQVcP2XxGLacJlQ1Lvbkkx6xGI5l19aN7bBBmr/GRtzrO1TldpadAAb4P uF+uF9hjwodDbs3G2WX9PQIFEgGVfhBxzT+MdfZs56qElQTMbdcB3GQABIoJT110x4Bkz1K34Mjj klFZVZGYG8EULKE1XpWDZ86dKGMydnFgrnXHwqi11xgTKMEFR2eWY1ksoNCWykvGvVf4zCLdFaO8 4fojI9yEu29/P5XgQBtzBYruubyQaXw3ZKKM1sdN9xCQJPjoAUP402/OBV1xy2zdj9c7sN2HevmN ZJWA8yNkh7QdeDEnRCh/1kTkzGdAr2/A4IdpHTFcAnPiZOo0aKuPalSF9hPtVOsmsK7Np45yIM2X Du8qKrcMTHZiQM2UveiKTaiwYgq3wo47UldGyLF/Op5aXAzH/QMoX2/HLwrVk45Q6kSeSphSnUWj HWvV3C7hT8FLepuT7z18nlrNDdwpAePt9ufbLeRG30rb8kAsP3scLMemq/a4UI7eBDEu6jnnLjiJ wSIIGf8K/VIXCyobNDWm5BCTh6Rc3I3uPdtWDyQl+dHX5EULc6kNNLrta18RKcHVjHyuU2hot4bu DcAb1gyP4n/1NcvUXDquxoatHSKbcIVhM0E/cKxt18vblvKhHqvBRXLFA4CgRSteTWoXpfzRCtfs 0= Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2001:1a50:11:0:c83f:a8ff:fea6:c8da; envelope-from=neither@nut.email; helo=mailgate01.uberspace.is X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_PASS=-0.001, T_SPF_HELO_TEMPERROR=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @nut.email) X-ZM-MESSAGEID: 1757019138023124100 Content-Type: text/plain; charset="utf-8" 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 Signed-off-by: Julian Ganz --- tests/tcg/plugins/discons.c | 210 ++++++++++++++++++++++++++++++++++ tests/tcg/plugins/meson.build | 2 +- 2 files changed, 211 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 0000000000..f185e3948b --- /dev/null +++ b/tests/tcg/plugins/discons.c @@ -0,0 +1,210 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2025, Julian Ganz + * + * This plugin exercises the discontinuity plugin API and asserts some + * of its behaviour regarding reported program counters. + */ +#include + +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version =3D 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 =3D=3D 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 =3D=3D b_hw; +} + +static void report_mismatch(const char *pc_name, unsigned int vcpu_index, + enum qemu_plugin_discon_type type, uint64_t la= st, + uint64_t expected, uint64_t encountered) +{ + GString *report; + const char *discon_type_name =3D "unknown"; + + if (addr_eq(expected, encountered)) { + return; + } + + switch (type) { + case QEMU_PLUGIN_DISCON_INTERRUPT: + discon_type_name =3D "interrupt"; + break; + case QEMU_PLUGIN_DISCON_EXCEPTION: + discon_type_name =3D "exception"; + break; + case QEMU_PLUGIN_DISCON_HOSTCALL: + discon_type_name =3D "hostcall"; + break; + default: + break; + } + + report =3D g_string_new(NULL); + g_string_append_printf(report, + "Discon %s PC mismatch on VCPU %d\nExpected: = %" + PRIx64"\nEncountered: %"PRIx64"\nExecuted Las= t: %" + PRIx64"\nEvent type: %s\n", + pc_name, vcpu_index, expected, encountered, las= t, + discon_type_name); + qemu_plugin_outs(report->str); + if (abort_on_mismatch) { + g_abort(); + } + g_string_free(report, true); +} + +static void vcpu_discon(qemu_plugin_id_t id, unsigned int vcpu_index, + enum qemu_plugin_discon_type type, uint64_t from_p= c, + uint64_t to_pc) +{ + struct cpu_state *state =3D qemu_plugin_scoreboard_find(states, vcpu_i= ndex); + + if (type =3D=3D 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 =3D false; + + state->next_pc =3D to_pc; + state->next_type =3D type; + state->has_next =3D true; +} + +static void insn_exec(unsigned int vcpu_index, void *userdata) +{ + struct cpu_state *state =3D qemu_plugin_scoreboard_find(states, vcpu_i= ndex); + + if (state->has_next) { + report_mismatch("target", vcpu_index, state->next_type, state->las= t_pc, + state->next_pc, state->last_pc); + state->has_next =3D false; + } + + if (trace_all_insns) { + g_autoptr(GString) report =3D 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 =3D qemu_plugin_tb_n_insns(tb); + for (size_t i =3D 0; i < n_insns; i++) { + struct qemu_plugin_insn *insn =3D qemu_plugin_tb_get_insn(tb, i); + uint64_t pc =3D qemu_plugin_insn_vaddr(insn); + uint64_t next_pc =3D pc + qemu_plugin_insn_size(insn); + uint64_t has_next =3D (i + 1) < n_insns; + + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn, + QEMU_PLUGIN_IN= LINE_STORE_U64, + last_pc, pc); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn, + QEMU_PLUGIN_IN= LINE_STORE_U64, + from_pc, next_= pc); + qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu(insn, + QEMU_PLUGIN_IN= LINE_STORE_U64, + has_from, has_= next); + qemu_plugin_register_vcpu_insn_exec_cb(insn, insn_exec, + QEMU_PLUGIN_CB_NO_REGS, NUL= L); + } +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + /* Set defaults */ + abort_on_mismatch =3D true; + trace_all_insns =3D false; + + for (int i =3D 0; i < argc; i++) { + char *opt =3D argv[i]; + g_auto(GStrv) tokens =3D g_strsplit(opt, "=3D", 2); + if (g_strcmp0(tokens[0], "abort") =3D=3D 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &abort_on_mismatch)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", o= pt); + return -1; + } + } else if (g_strcmp0(tokens[0], "trace-all") =3D=3D 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &trace_all_insns)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", o= pt); + return -1; + } + } else { + fprintf(stderr, "option parsing failed: %s\n", opt); + return -1; + } + } + + states =3D qemu_plugin_scoreboard_new(sizeof(struct cpu_state)); + last_pc =3D qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_st= ate, + last_pc); + from_pc =3D qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_st= ate, + from_pc); + has_from =3D qemu_plugin_scoreboard_u64_in_struct(states, struct cpu_s= tate, + 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 61a007d9e7..561584159e 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -1,6 +1,6 @@ t =3D [] 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 =3D=3D 'windows' t +=3D shared_module(i, files(i + '.c') + '../../../contrib/plugins/= win32_linker.c', include_directories: '../../../include/qemu', --=20 2.49.1