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
 |