qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
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


  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).