mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-02 23:33:54 -06:00
libqos/qgraph: format qgraph comments for sphinx documentation
Change documentation style and fix minor typos in tests/qtest/libqos/qgraph.h to automatically generate sphinx documentation in docs/devel/qgraph.rst The mechanism explanation that once was in qgraph.h is now moved to qgraph.rst There is no functional change intended. Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com> Message-Id: <20210308073240.6363-1-eesposit@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
This commit is contained in:
parent
6179f32eeb
commit
222455ef81
5 changed files with 386 additions and 335 deletions
|
@ -12,6 +12,7 @@ Contents:
|
|||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:includehidden:
|
||||
|
||||
build-system
|
||||
style
|
||||
|
|
261
docs/devel/qgraph.rst
Normal file
261
docs/devel/qgraph.rst
Normal file
|
@ -0,0 +1,261 @@
|
|||
.. _qgraph:
|
||||
|
||||
========================================
|
||||
Qtest Driver Framework
|
||||
========================================
|
||||
|
||||
This Qgraph API provides all basic functions to create a graph
|
||||
and instantiate nodes representing machines, drivers and tests
|
||||
representing their relations with ``CONSUMES``, ``PRODUCES``, and
|
||||
``CONTAINS`` edges.
|
||||
|
||||
The idea is to have a framework where each test asks for a specific
|
||||
driver, and the framework takes care of allocating the proper devices
|
||||
required and passing the correct command line arguments to QEMU.
|
||||
|
||||
Nodes
|
||||
^^^^^^
|
||||
|
||||
A node can be of four types:
|
||||
|
||||
- **QNODE_MACHINE**: for example ``arm/raspi2``
|
||||
- **QNODE_DRIVER**: for example ``generic-sdhci``
|
||||
- **QNODE_INTERFACE**: for example ``sdhci`` (interface for all ``-sdhci``
|
||||
drivers).
|
||||
An interface is not explicitly created, it will be automatically
|
||||
instantiated when a node consumes or produces it.
|
||||
- **QNODE_TEST**: for example ``sdhci-test``, consumes an interface and
|
||||
tests the functions provided.
|
||||
|
||||
Notes for the nodes:
|
||||
|
||||
- QNODE_MACHINE: each machine struct must have a ``QGuestAllocator`` and
|
||||
implement ``get_driver()`` to return the allocator mapped to the interface
|
||||
"memory". The function can also return ``NULL`` if the allocator
|
||||
is not set.
|
||||
- QNODE_DRIVER: driver names must be unique, and machines and nodes
|
||||
planned to be "consumed" by other nodes must match QEMU
|
||||
drivers name, otherwise they won't be discovered
|
||||
|
||||
Edges
|
||||
^^^^^^
|
||||
|
||||
An edge relation between two nodes (drivers or machines) `X` and `Y` can be:
|
||||
|
||||
- ``X CONSUMES Y``: `Y` can be plugged into `X`
|
||||
- ``X PRODUCES Y``: `X` provides the interface `Y`
|
||||
- ``X CONTAINS Y``: `Y` is part of `X` component
|
||||
|
||||
Execution steps
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
The basic framework steps are the following:
|
||||
|
||||
- All nodes and edges are created in their respective
|
||||
machine/driver/test files
|
||||
- The framework starts QEMU and asks for a list of available devices
|
||||
and machines (note that only machines and "consumed" nodes are mapped
|
||||
1:1 with QEMU devices)
|
||||
- The framework walks the graph starting from the available machines and
|
||||
performs a Depth First Search for tests
|
||||
- Once a test is found, the path is walked again and all drivers are
|
||||
allocated accordingly and the final interface is passed to the test
|
||||
- The test is executed
|
||||
- Unused objects are cleaned and the path discovery is continued
|
||||
|
||||
Depending on the QEMU binary used, only some drivers/machines will be
|
||||
available and only test that are reached by them will be executed.
|
||||
|
||||
Creating a new driver and its interface
|
||||
"""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
.. code::
|
||||
|
||||
#include "qgraph.h"
|
||||
|
||||
struct My_driver {
|
||||
QOSGraphObject obj;
|
||||
Node_produced prod;
|
||||
Node_contained cont;
|
||||
}
|
||||
|
||||
static void my_destructor(QOSGraphObject *obj)
|
||||
{
|
||||
g_free(obj);
|
||||
}
|
||||
|
||||
static void *my_get_driver(void *object, const char *interface) {
|
||||
My_driver *dev = object;
|
||||
if (!g_strcmp0(interface, "my_interface")) {
|
||||
return &dev->prod;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static void *my_get_device(void *object, const char *device) {
|
||||
My_driver *dev = object;
|
||||
if (!g_strcmp0(device, "my_driver_contained")) {
|
||||
return &dev->cont;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static void *my_driver_constructor(void *node_consumed,
|
||||
QOSGraphObject *alloc)
|
||||
{
|
||||
My_driver dev = g_new(My_driver, 1);
|
||||
|
||||
// get the node pointed by the produce edge
|
||||
dev->obj.get_driver = my_get_driver;
|
||||
|
||||
// get the node pointed by the contains
|
||||
dev->obj.get_device = my_get_device;
|
||||
|
||||
// free the object
|
||||
dev->obj.destructor = my_destructor;
|
||||
|
||||
do_something_with_node_consumed(node_consumed);
|
||||
|
||||
// set all fields of contained device
|
||||
init_contained_device(&dev->cont);
|
||||
return &dev->obj;
|
||||
}
|
||||
|
||||
static void register_my_driver(void)
|
||||
{
|
||||
qos_node_create_driver("my_driver", my_driver_constructor);
|
||||
|
||||
// contained drivers don't need a constructor,
|
||||
// they will be init by the parent.
|
||||
qos_node_create_driver("my_driver_contained", NULL);
|
||||
|
||||
// For the sake of this example, assume machine x86_64/pc
|
||||
// contains "other_node".
|
||||
// This relation, along with the machine and "other_node"
|
||||
// creation, should be defined in the x86_64_pc-machine.c file.
|
||||
// "my_driver" will then consume "other_node"
|
||||
qos_node_contains("my_driver", "my_driver_contained");
|
||||
qos_node_produces("my_driver", "my_interface");
|
||||
qos_node_consumes("my_driver", "other_node");
|
||||
}
|
||||
|
||||
In the above example, all possible types of relations are created:
|
||||
node "my_driver" consumes, contains and produces other nodes.
|
||||
More specifically::
|
||||
|
||||
x86_64/pc -->contains--> other_node <--consumes-- my_driver
|
||||
|
|
||||
my_driver_contained <--contains--+
|
||||
|
|
||||
my_interface <--produces--+
|
||||
|
||||
or inverting the consumes edge in consumed_by::
|
||||
|
||||
x86_64/pc -->contains--> other_node --consumed_by--> my_driver
|
||||
|
|
||||
my_driver_contained <--contains--+
|
||||
|
|
||||
my_interface <--produces--+
|
||||
|
||||
Creating new test
|
||||
"""""""""""""""""
|
||||
|
||||
.. code::
|
||||
|
||||
#include "qgraph.h"
|
||||
|
||||
static void my_test_function(void *obj, void *data)
|
||||
{
|
||||
Node_produced *interface_to_test = obj;
|
||||
// test interface_to_test
|
||||
}
|
||||
|
||||
static void register_my_test(void)
|
||||
{
|
||||
qos_add_test("my_interface", "my_test", my_test_function);
|
||||
}
|
||||
|
||||
libqos_init(register_my_test);
|
||||
|
||||
Here a new test is created, consuming "my_interface" node
|
||||
and creating a valid path from a machine to a test.
|
||||
Final graph will be like this::
|
||||
|
||||
x86_64/pc --contains--> other_node <--consumes-- my_driver
|
||||
|
|
||||
my_driver_contained <--contains--+
|
||||
|
|
||||
my_test --consumes--> my_interface <--produces--+
|
||||
|
||||
or inverting the consumes edge in consumed_by::
|
||||
|
||||
x86_64/pc --contains--> other_node --consumed_by--> my_driver
|
||||
|
|
||||
my_driver_contained <--contains--+
|
||||
|
|
||||
my_test <--consumed_by-- my_interface <--produces--+
|
||||
|
||||
Assuming there the binary is
|
||||
``QTEST_QEMU_BINARY=./qemu-system-x86_64``
|
||||
a valid test path will be:
|
||||
``/x86_64/pc/other_node/my_driver/my_interface/my_test``.
|
||||
|
||||
Additional examples are also in ``test-qgraph.c``
|
||||
|
||||
Command line:
|
||||
""""""""""""""
|
||||
|
||||
Command line is built by using node names and optional arguments
|
||||
passed by the user when building the edges.
|
||||
|
||||
There are three types of command line arguments:
|
||||
|
||||
- ``in node`` : created from the node name. For example, machines will
|
||||
have ``-M <machine>`` to its command line, while devices
|
||||
``-device <device>``. It is automatically done by the framework.
|
||||
- ``after node`` : added as additional argument to the node name.
|
||||
This argument is added optionally when creating edges,
|
||||
by setting the parameter @after_cmd_line and
|
||||
@extra_edge_opts in #QOSGraphEdgeOptions.
|
||||
The framework automatically adds
|
||||
a comma before @extra_edge_opts,
|
||||
because it is going to add attributes
|
||||
after the destination node pointed by
|
||||
the edge containing these options, and automatically
|
||||
adds a space before @after_cmd_line, because it
|
||||
adds an additional device, not an attribute.
|
||||
- ``before node`` : added as additional argument to the node name.
|
||||
This argument is added optionally when creating edges,
|
||||
by setting the parameter @before_cmd_line in
|
||||
#QOSGraphEdgeOptions. This attribute
|
||||
is going to add attributes before the destination node
|
||||
pointed by the edge containing these options. It is
|
||||
helpful to commands that are not node-representable,
|
||||
such as ``-fdsev`` or ``-netdev``.
|
||||
|
||||
While adding command line in edges is always used, not all nodes names are
|
||||
used in every path walk: this is because the contained or produced ones
|
||||
are already added by QEMU, so only nodes that "consumes" will be used to
|
||||
build the command line. Also, nodes that will have ``{ "abstract" : true }``
|
||||
as QMP attribute will loose their command line, since they are not proper
|
||||
devices to be added in QEMU.
|
||||
|
||||
Example::
|
||||
|
||||
QOSGraphEdgeOptions opts = {
|
||||
.arg = NULL,
|
||||
.size_arg = 0,
|
||||
.after_cmd_line = "-device other",
|
||||
.before_cmd_line = "-netdev something",
|
||||
.extra_edge_opts = "addr=04.0",
|
||||
};
|
||||
QOSGraphNodeS *node = qos_node_create_driver("my_node", constructor);
|
||||
qos_node_consumes_args("my_node", "interface", &opts);
|
||||
|
||||
Will produce the following command line:
|
||||
``-netdev something -device my_node,addr=04.0 -device other``
|
||||
|
||||
Qgraph API reference
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. kernel-doc:: tests/qtest/libqos/qgraph.h
|
|
@ -2,6 +2,11 @@
|
|||
QTest Device Emulation Testing Framework
|
||||
========================================
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
qgraph
|
||||
|
||||
QTest is a device emulation testing framework. It can be very useful to test
|
||||
device models; it could also control certain aspects of QEMU (such as virtual
|
||||
clock stepping), with a special purpose "qtest" protocol. Refer to
|
||||
|
@ -24,6 +29,9 @@ On top of libqtest, a higher level library, ``libqos``, was created to
|
|||
encapsulate common tasks of device drivers, such as memory management and
|
||||
communicating with system buses or devices. Many virtual device tests use
|
||||
libqos instead of directly calling into libqtest.
|
||||
Libqos also offers the Qgraph API to increase each test coverage and
|
||||
automate QEMU command line arguments and devices setup.
|
||||
Refer to :ref:`qgraph` for Qgraph explanation and API.
|
||||
|
||||
Steps to add a new QTest case are:
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue