3f9f9a37ae
Reflect recent changes on API (inline ops) and new plugins. Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Message-Id: <20240812231945.169310-1-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240813202329.1237572-21-alex.bennee@linaro.org>
173 lines
6.7 KiB
ReStructuredText
173 lines
6.7 KiB
ReStructuredText
..
|
|
Copyright (C) 2017, Emilio G. Cota <cota@braap.org>
|
|
Copyright (c) 2019, Linaro Limited
|
|
Written by Emilio Cota and Alex Bennée
|
|
|
|
.. _TCG Plugins:
|
|
|
|
QEMU TCG Plugins
|
|
================
|
|
|
|
|
|
Writing plugins
|
|
---------------
|
|
|
|
API versioning
|
|
~~~~~~~~~~~~~~
|
|
|
|
This is a new feature for QEMU and it does allow people to develop
|
|
out-of-tree plugins that can be dynamically linked into a running QEMU
|
|
process. However the project reserves the right to change or break the
|
|
API should it need to do so. The best way to avoid this is to submit
|
|
your plugin upstream so they can be updated if/when the API changes.
|
|
|
|
All plugins need to declare a symbol which exports the plugin API
|
|
version they were built against. This can be done simply by::
|
|
|
|
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
|
|
|
|
The core code will refuse to load a plugin that doesn't export a
|
|
``qemu_plugin_version`` symbol or if plugin version is outside of QEMU's
|
|
supported range of API versions.
|
|
|
|
Additionally the ``qemu_info_t`` structure which is passed to the
|
|
``qemu_plugin_install`` method of a plugin will detail the minimum and
|
|
current API versions supported by QEMU. The API version will be
|
|
incremented if new APIs are added. The minimum API version will be
|
|
incremented if existing APIs are changed or removed.
|
|
|
|
Lifetime of the query handle
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Each callback provides an opaque anonymous information handle which
|
|
can usually be further queried to find out information about a
|
|
translation, instruction or operation. The handles themselves are only
|
|
valid during the lifetime of the callback so it is important that any
|
|
information that is needed is extracted during the callback and saved
|
|
by the plugin.
|
|
|
|
Plugin life cycle
|
|
~~~~~~~~~~~~~~~~~
|
|
|
|
First the plugin is loaded and the public qemu_plugin_install function
|
|
is called. The plugin will then register callbacks for various plugin
|
|
events. Generally plugins will register a handler for the *atexit*
|
|
if they want to dump a summary of collected information once the
|
|
program/system has finished running.
|
|
|
|
When a registered event occurs the plugin callback is invoked. The
|
|
callbacks may provide additional information. In the case of a
|
|
translation event the plugin has an option to enumerate the
|
|
instructions in a block of instructions and optionally register
|
|
callbacks to some or all instructions when they are executed.
|
|
|
|
There is also a facility to add inline instructions doing various operations,
|
|
like adding or storing an immediate value. It is also possible to execute a
|
|
callback conditionally, with condition being evaluated inline. All those inline
|
|
operations are associated to a ``scoreboard``, which is a thread-local storage
|
|
automatically expanded when new cores/threads are created and that can be
|
|
accessed/modified in a thread-safe way without any lock needed. Combining inline
|
|
operations and conditional callbacks offer a more efficient way to instrument
|
|
binaries, compared to classic callbacks.
|
|
|
|
Finally when QEMU exits all the registered *atexit* callbacks are
|
|
invoked.
|
|
|
|
Exposure of QEMU internals
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The plugin architecture actively avoids leaking implementation details
|
|
about how QEMU's translation works to the plugins. While there are
|
|
conceptions such as translation time and translation blocks the
|
|
details are opaque to plugins. The plugin is able to query select
|
|
details of instructions and system configuration only through the
|
|
exported *qemu_plugin* functions.
|
|
|
|
However the following assumptions can be made:
|
|
|
|
Translation Blocks
|
|
++++++++++++++++++
|
|
|
|
All code will go through a translation phase although not all
|
|
translations will be necessarily be executed. You need to instrument
|
|
actual executions to track what is happening.
|
|
|
|
It is quite normal to see the same address translated multiple times.
|
|
If you want to track the code in system emulation you should examine
|
|
the underlying physical address (``qemu_plugin_insn_haddr``) to take
|
|
into account the effects of virtual memory although if the system does
|
|
paging this will change too.
|
|
|
|
Not all instructions in a block will always execute so if its
|
|
important to track individual instruction execution you need to
|
|
instrument them directly. However asynchronous interrupts will not
|
|
change control flow mid-block.
|
|
|
|
Instructions
|
|
++++++++++++
|
|
|
|
Instruction instrumentation runs before the instruction executes. You
|
|
can be can be sure the instruction will be dispatched, but you can't
|
|
be sure it will complete. Generally this will be because of a
|
|
synchronous exception (e.g. SIGILL) triggered by the instruction
|
|
attempting to execute. If you want to be sure you will need to
|
|
instrument the next instruction as well. See the ``execlog.c`` plugin
|
|
for examples of how to track this and finalise details after execution.
|
|
|
|
Memory Accesses
|
|
+++++++++++++++
|
|
|
|
Memory callbacks are called after a successful load or store.
|
|
Unsuccessful operations (i.e. faults) will not be visible to memory
|
|
instrumentation although the execution side effects can be observed
|
|
(e.g. entering a exception handler).
|
|
|
|
System Idle and Resume States
|
|
+++++++++++++++++++++++++++++
|
|
|
|
The ``qemu_plugin_register_vcpu_idle_cb`` and
|
|
``qemu_plugin_register_vcpu_resume_cb`` functions can be used to track
|
|
when CPUs go into and return from sleep states when waiting for
|
|
external I/O. Be aware though that these may occur less frequently
|
|
than in real HW due to the inefficiencies of emulation giving less
|
|
chance for the CPU to idle.
|
|
|
|
Internals
|
|
---------
|
|
|
|
Locking
|
|
~~~~~~~
|
|
|
|
We have to ensure we cannot deadlock, particularly under MTTCG. For
|
|
this we acquire a lock when called from plugin code. We also keep the
|
|
list of callbacks under RCU so that we do not have to hold the lock
|
|
when calling the callbacks. This is also for performance, since some
|
|
callbacks (e.g. memory access callbacks) might be called very
|
|
frequently.
|
|
|
|
* A consequence of this is that we keep our own list of CPUs, so that
|
|
we do not have to worry about locking order wrt cpu_list_lock.
|
|
* Use a recursive lock, since we can get registration calls from
|
|
callbacks.
|
|
|
|
As a result registering/unregistering callbacks is "slow", since it
|
|
takes a lock. But this is very infrequent; we want performance when
|
|
calling (or not calling) callbacks, not when registering them. Using
|
|
RCU is great for this.
|
|
|
|
We support the uninstallation of a plugin at any time (e.g. from
|
|
plugin callbacks). This allows plugins to remove themselves if they no
|
|
longer want to instrument the code. This operation is asynchronous
|
|
which means callbacks may still occur after the uninstall operation is
|
|
requested. The plugin isn't completely uninstalled until the safe work
|
|
has executed while all vCPUs are quiescent.
|
|
|
|
Plugin API
|
|
==========
|
|
|
|
The following API is generated from the inline documentation in
|
|
``include/qemu/qemu-plugin.h``. Please ensure any updates to the API
|
|
include the full kernel-doc annotations.
|
|
|
|
.. kernel-doc:: include/qemu/qemu-plugin.h
|