From: "Alex Bennée" <alex.bennee@linaro.org>
To: Julian Ganz <neither@nut.email>
Cc: qemu-devel@nongnu.org,
Richard Henderson <richard.henderson@linaro.org>,
Paolo Bonzini <pbonzini@redhat.com>,
Alexandre Iooss <erdnaxe@crans.org>,
Mahmoud Mandour <ma.mandourr@gmail.com>
Subject: Re: [PATCH] tcg-plugins: add a hook for interrupts, exceptions and traps
Date: Mon, 23 Oct 2023 14:08:51 +0100 [thread overview]
Message-ID: <87v8ax4fqd.fsf@linaro.org> (raw)
In-Reply-To: <20231021122502.26746-1-neither@nut.email>
Julian Ganz <neither@nut.email> writes:
> Some analysis greatly benefits, or depends on, information about
> interrupts. For example, we may need to handle the execution of a new
> translation block differently if it is not the result of normal program
> flow but of an interrupt.
I can see the benefit for plugins knowing the context - for QEMU itself
there is no real difference in how it handles blocks that are part of an
interrupt.
> Even with the existing interfaces, it is more or less possible to
> discern these situations using some heuristice. For example, the PC
> landing in a trap vector is a strong indicator that a trap, i.e. an
> interrupt or event occured. However, such heuristics require knowledge
> about the architecture and may be prone to errors.
Does this requirement go away if you can query the current execution
state via registers?
> The callback introduced by this change provides a generic and
> easy-to-use interface for plugin authors. It allows them to register a
> callback in which they may alter some plugin-internal state to convey
> the firing of an interrupt for a given CPU, or perform some stand-alone
> analysis based on the interrupt and, for example, the CPU state.
>
> Signed-off-by: Julian Ganz <neither@nut.email>
> ---
> accel/tcg/cpu-exec.c | 3 +++
> include/qemu/plugin-event.h | 1 +
> include/qemu/plugin.h | 4 ++++
> include/qemu/qemu-plugin.h | 11 +++++++++++
> plugins/core.c | 12 ++++++++++++
> plugins/qemu-plugins.symbols | 1 +
> 6 files changed, 32 insertions(+)
>
> diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
> index 1a5bc90220..e094d9236d 100644
> --- a/accel/tcg/cpu-exec.c
> +++ b/accel/tcg/cpu-exec.c
> @@ -754,6 +754,8 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret)
> qemu_mutex_unlock_iothread();
> cpu->exception_index = -1;
>
> + qemu_plugin_vcpu_interrupt_cb(cpu);
> +
> if (unlikely(cpu->singlestep_enabled)) {
> /*
> * After processing the exception, ensure an EXCP_DEBUG is
> @@ -866,6 +868,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
> if (need_replay_interrupt(interrupt_request)) {
> replay_interrupt();
> }
> + qemu_plugin_vcpu_interrupt_cb(cpu);
My worry here is:
a) we are conflating QEMU exceptions and interrupts as the same thing
b) this is leaking internal implementation details of the translator
For example EXCP_SEMIHOST isn't actually an interrupt (we don't change
processor state or control flow). It's just the internal signalling we
use to handle our semihosting implementation. Similarly the
CPU_INTERRUPT_EXITTB interrupt isn't really changing state, just
ensuring we exit the run loop so house keeping is done.
The "correct" way for ARM for example would be to register a helper
function with arm_register_el_change_hook() and trigger the callbacks
that way. However each architecture does its own IRQ modelling so you
would need to work out where in the plumbing to do each callback.
> /*
> * After processing the interrupt, ensure an EXCP_DEBUG is
> * raised when single-stepping so that GDB doesn't miss the
> diff --git a/include/qemu/plugin-event.h b/include/qemu/plugin-event.h
> index 7056d8427b..d085bdda4e 100644
> --- a/include/qemu/plugin-event.h
> +++ b/include/qemu/plugin-event.h
> @@ -20,6 +20,7 @@ enum qemu_plugin_event {
> QEMU_PLUGIN_EV_VCPU_SYSCALL_RET,
> QEMU_PLUGIN_EV_FLUSH,
> QEMU_PLUGIN_EV_ATEXIT,
> + QEMU_PLUGIN_EV_VCPU_INTERRUPT,
> QEMU_PLUGIN_EV_MAX, /* total number of plugin events we support */
> };
>
> diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h
> index 7fdc3a4849..f942e45f41 100644
> --- a/include/qemu/plugin.h
> +++ b/include/qemu/plugin.h
> @@ -190,6 +190,7 @@ void qemu_plugin_vcpu_exit_hook(CPUState *cpu);
> void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb);
> void qemu_plugin_vcpu_idle_cb(CPUState *cpu);
> void qemu_plugin_vcpu_resume_cb(CPUState *cpu);
> +void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu);
> void
> qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1,
> uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5,
> @@ -270,6 +271,9 @@ static inline void qemu_plugin_vcpu_idle_cb(CPUState *cpu)
> static inline void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
> { }
>
> +static inline void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu)
> +{ }
> +
> static inline void
> qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2,
> uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6,
> diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h
> index 50a9957279..2eb4b325fe 100644
> --- a/include/qemu/qemu-plugin.h
> +++ b/include/qemu/qemu-plugin.h
> @@ -206,6 +206,17 @@ void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
> void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
> qemu_plugin_vcpu_simple_cb_t cb);
>
> +/**
> + * qemu_plugin_register_vcpu_interrupt_cb() - register a vCPU interrupt callback
> + * @id: plugin ID
> + * @cb: callback function
> + *
> + * The @cb function is called every time a vCPU receives an interrupt, exception
> + * or trap.
As discussed above you would trigger for a lot more than that. You would
also miss a lot of the other interesting transitions which don't need an
asynchronous signal. For example HELPER(exception_return) happily
changes control flow but doesn't need to use the exception mechanism to
do it.
> + */
> +void qemu_plugin_register_vcpu_interrupt_cb(qemu_plugin_id_t id,
> + qemu_plugin_vcpu_simple_cb_t cb);
> +
> /** struct qemu_plugin_tb - Opaque handle for a translation block */
> struct qemu_plugin_tb;
> /** struct qemu_plugin_insn - Opaque handle for a translated instruction */
> diff --git a/plugins/core.c b/plugins/core.c
> index fcd33a2bff..3658bdef45 100644
> --- a/plugins/core.c
> +++ b/plugins/core.c
> @@ -103,6 +103,7 @@ static void plugin_vcpu_cb__simple(CPUState *cpu, enum qemu_plugin_event ev)
> case QEMU_PLUGIN_EV_VCPU_EXIT:
> case QEMU_PLUGIN_EV_VCPU_IDLE:
> case QEMU_PLUGIN_EV_VCPU_RESUME:
> + case QEMU_PLUGIN_EV_VCPU_INTERRUPT:
> /* iterate safely; plugins might uninstall themselves at any time */
> QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) {
> qemu_plugin_vcpu_simple_cb_t func = cb->f.vcpu_simple;
> @@ -400,6 +401,11 @@ void qemu_plugin_vcpu_resume_cb(CPUState *cpu)
> plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME);
> }
>
> +void qemu_plugin_vcpu_interrupt_cb(CPUState *cpu)
> +{
> + plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INTERRUPT);
> +}
> +
> void qemu_plugin_register_vcpu_idle_cb(qemu_plugin_id_t id,
> qemu_plugin_vcpu_simple_cb_t cb)
> {
> @@ -412,6 +418,12 @@ void qemu_plugin_register_vcpu_resume_cb(qemu_plugin_id_t id,
> plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_RESUME, cb);
> }
>
> +void qemu_plugin_register_vcpu_interrupt_cb(qemu_plugin_id_t id,
> + qemu_plugin_vcpu_simple_cb_t cb)
> +{
> + plugin_register_cb(id, QEMU_PLUGIN_EV_VCPU_INTERRUPT, cb);
> +}
> +
> void qemu_plugin_register_flush_cb(qemu_plugin_id_t id,
> qemu_plugin_simple_cb_t cb)
> {
> diff --git a/plugins/qemu-plugins.symbols b/plugins/qemu-plugins.symbols
> index 71f6c90549..1fddb4b9fd 100644
> --- a/plugins/qemu-plugins.symbols
> +++ b/plugins/qemu-plugins.symbols
> @@ -35,6 +35,7 @@
> qemu_plugin_register_vcpu_tb_exec_cb;
> qemu_plugin_register_vcpu_tb_exec_inline;
> qemu_plugin_register_vcpu_tb_trans_cb;
> + qemu_plugin_register_vcpu_interrupt_cb;
> qemu_plugin_reset;
> qemu_plugin_start_code;
> qemu_plugin_tb_get_insn;
I'm not opposed to adding such a API hook to plugins but I don't think
the current approach does what you want. If we do add a new API it is
customary to either expand an existing plugin or add a new one to
demonstrate the use of the API.
--
Alex Bennée
Virtualisation Tech Lead @ Linaro
next prev parent reply other threads:[~2023-10-23 13:43 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-21 12:24 [PATCH] tcg-plugins: add a hook for interrupts, exceptions and traps Julian Ganz
2023-10-23 13:08 ` Alex Bennée [this message]
2023-10-23 18:45 ` Julian Ganz
2024-10-19 16:39 ` [RFC PATCH v2 0/7] tcg-plugins: add hooks " Julian Ganz
2024-10-19 16:39 ` [RFC PATCH v2 1/7] plugins: add API for registering trap related callbacks Julian Ganz
2024-10-19 16:39 ` [RFC PATCH v2 2/7] plugins: add hooks for new " Julian Ganz
2024-10-19 16:39 ` [RFC PATCH v2 3/7] contrib/plugins: add plugin showcasing new trap related API Julian Ganz
2024-10-21 18:06 ` Pierrick Bouvier
2024-10-21 18:07 ` Pierrick Bouvier
2024-10-21 20:22 ` Julian Ganz
2024-10-19 16:39 ` [RFC PATCH v2 4/7] target/arm: call plugin trap callbacks Julian Ganz
2024-10-21 12:58 ` Peter Maydell
2024-10-21 16:25 ` Julian Ganz
2024-10-19 16:39 ` [RFC PATCH v2 5/7] target/avr: " Julian Ganz
2024-10-19 17:29 ` Michael Rolnik
2024-10-19 16:39 ` [RFC PATCH v2 6/7] target/riscv: " Julian Ganz
2024-10-19 16:39 ` [RFC PATCH v2 7/7] target/sparc: " Julian Ganz
2024-10-20 19:37 ` [RFC PATCH v2 0/7] tcg-plugins: add hooks for interrupts, exceptions and traps Alex Bennée
2024-10-21 18:00 ` Pierrick Bouvier
2024-10-21 18:47 ` Alex Bennée
2024-10-21 20:45 ` Pierrick Bouvier
2024-10-21 21:02 ` Julian Ganz
2024-10-21 21:59 ` Pierrick Bouvier
2024-10-22 8:21 ` Julian Ganz
2024-10-22 8:58 ` Alex Bennée
2024-10-22 20:12 ` Julian Ganz
2024-10-22 21:15 ` Pierrick Bouvier
2024-10-23 12:56 ` Julian Ganz
2024-10-23 13:57 ` Alex Bennée
2024-10-23 15:21 ` Pierrick Bouvier
2024-10-23 15:16 ` Pierrick Bouvier
2024-10-23 16:12 ` Julian Ganz
2024-10-23 16:39 ` Pierrick Bouvier
2024-10-23 17:12 ` Julian Ganz
2024-10-23 17:53 ` Pierrick Bouvier
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=87v8ax4fqd.fsf@linaro.org \
--to=alex.bennee@linaro.org \
--cc=erdnaxe@crans.org \
--cc=ma.mandourr@gmail.com \
--cc=neither@nut.email \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=richard.henderson@linaro.org \
/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).