summaryrefslogtreecommitdiffstats
path: root/tests/libqos/qgraph.c
diff options
context:
space:
mode:
authorThomas Huth2019-09-10 16:41:20 +0200
committerThomas Huth2020-01-12 11:42:41 +0100
commit1cf4323ecd03235984e43a416a42f10c975cf785 (patch)
tree57ad5475a4cbbbe0088aecf86b682d8e11150b9f /tests/libqos/qgraph.c
parenttests/Makefile: Move qtest-related settings to a separate Makefile.include (diff)
downloadqemu-1cf4323ecd03235984e43a416a42f10c975cf785.tar.gz
qemu-1cf4323ecd03235984e43a416a42f10c975cf785.tar.xz
qemu-1cf4323ecd03235984e43a416a42f10c975cf785.zip
tests/libqos: Move the libqos files under tests/qtest/
The qos stuff belongs to qtest, so move it into that directory, too. Message-Id: <20191218103059.11729-8-thuth@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
Diffstat (limited to 'tests/libqos/qgraph.c')
-rw-r--r--tests/libqos/qgraph.c759
1 files changed, 0 insertions, 759 deletions
diff --git a/tests/libqos/qgraph.c b/tests/libqos/qgraph.c
deleted file mode 100644
index 7a7ae2a19e..0000000000
--- a/tests/libqos/qgraph.c
+++ /dev/null
@@ -1,759 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/queue.h"
-#include "libqos/qgraph_internal.h"
-#include "libqos/qgraph.h"
-
-#define QGRAPH_PRINT_DEBUG 0
-#define QOS_ROOT ""
-typedef struct QOSStackElement QOSStackElement;
-
-/* Graph Edge.*/
-struct QOSGraphEdge {
- QOSEdgeType type;
- char *dest;
- void *arg; /* just for QEDGE_CONTAINS
- * and QEDGE_CONSUMED_BY */
- char *extra_device_opts; /* added to -device option, "," is
- * automatically added
- */
- char *before_cmd_line; /* added before node cmd_line */
- char *after_cmd_line; /* added after -device options */
- char *edge_name; /* used by QEDGE_CONTAINS */
- QSLIST_ENTRY(QOSGraphEdge) edge_list;
-};
-
-typedef QSLIST_HEAD(, QOSGraphEdge) QOSGraphEdgeList;
-
-/**
- * Stack used to keep track of the discovered path when using
- * the DFS algorithm
- */
-struct QOSStackElement {
- QOSGraphNode *node;
- QOSStackElement *parent;
- QOSGraphEdge *parent_edge;
- int length;
-};
-
-/* Each enty in these hash table will consist of <string, node/edge> pair. */
-static GHashTable *edge_table;
-static GHashTable *node_table;
-
-/* stack used by the DFS algorithm to store the path from machine to test */
-static QOSStackElement qos_node_stack[QOS_PATH_MAX_ELEMENT_SIZE];
-static int qos_node_tos;
-
-/**
- * add_edge(): creates an edge of type @type
- * from @source to @dest node, and inserts it in the
- * edges hash table
- *
- * Nodes @source and @dest do not necessarily need to exist.
- * Possibility to add also options (see #QOSGraphEdgeOptions)
- * edge->edge_name is used as identifier for get_device relationships,
- * so by default is equal to @dest.
- */
-static void add_edge(const char *source, const char *dest,
- QOSEdgeType type, QOSGraphEdgeOptions *opts)
-{
- char *key;
- QOSGraphEdgeList *list = g_hash_table_lookup(edge_table, source);
- QOSGraphEdgeOptions def_opts = { };
-
- if (!list) {
- list = g_new0(QOSGraphEdgeList, 1);
- key = g_strdup(source);
- g_hash_table_insert(edge_table, key, list);
- }
-
- if (!opts) {
- opts = &def_opts;
- }
-
- QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1);
- edge->type = type;
- edge->dest = g_strdup(dest);
- edge->edge_name = g_strdup(opts->edge_name ?: dest);
- edge->arg = g_memdup(opts->arg, opts->size_arg);
-
- edge->before_cmd_line =
- opts->before_cmd_line ? g_strconcat(" ", opts->before_cmd_line, NULL) : NULL;
- edge->extra_device_opts =
- opts->extra_device_opts ? g_strconcat(",", opts->extra_device_opts, NULL) : NULL;
- edge->after_cmd_line =
- opts->after_cmd_line ? g_strconcat(" ", opts->after_cmd_line, NULL) : NULL;
-
- QSLIST_INSERT_HEAD(list, edge, edge_list);
-}
-
-/* destroy_edges(): frees all edges inside a given @list */
-static void destroy_edges(void *list)
-{
- QOSGraphEdge *temp;
- QOSGraphEdgeList *elist = list;
-
- while (!QSLIST_EMPTY(elist)) {
- temp = QSLIST_FIRST(elist);
- QSLIST_REMOVE_HEAD(elist, edge_list);
- g_free(temp->dest);
- g_free(temp->before_cmd_line);
- g_free(temp->after_cmd_line);
- g_free(temp->extra_device_opts);
- g_free(temp->edge_name);
- g_free(temp->arg);
- g_free(temp);
- }
- g_free(elist);
-}
-
-/**
- * create_node(): creates a node @name of type @type
- * and inserts it to the nodes hash table.
- * By default, node is not available.
- */
-static QOSGraphNode *create_node(const char *name, QOSNodeType type)
-{
- if (g_hash_table_lookup(node_table, name)) {
- g_printerr("Node %s already created\n", name);
- abort();
- }
-
- QOSGraphNode *node = g_new0(QOSGraphNode, 1);
- node->type = type;
- node->available = false;
- node->name = g_strdup(name);
- g_hash_table_insert(node_table, node->name, node);
- return node;
-}
-
-/**
- * destroy_node(): frees a node @val from the nodes hash table.
- * Note that node->name is not free'd since it will represent the
- * hash table key
- */
-static void destroy_node(void *val)
-{
- QOSGraphNode *node = val;
- g_free(node->command_line);
- g_free(node);
-}
-
-/**
- * destroy_string(): frees @key from the nodes hash table.
- * Actually frees the node->name
- */
-static void destroy_string(void *key)
-{
- g_free(key);
-}
-
-/**
- * search_node(): search for a node @key in the nodes hash table
- * Returns the QOSGraphNode if found, #NULL otherwise
- */
-static QOSGraphNode *search_node(const char *key)
-{
- return g_hash_table_lookup(node_table, key);
-}
-
-/**
- * get_edgelist(): returns the edge list (value) assigned to
- * the @key in the edge hash table.
- * This list will contain all edges with source equal to @key
- *
- * Returns: on success: the %QOSGraphEdgeList
- * otherwise: abort()
- */
-static QOSGraphEdgeList *get_edgelist(const char *key)
-{
- return g_hash_table_lookup(edge_table, key);
-}
-
-/**
- * search_list_edges(): search for an edge with destination @dest
- * in the given @edgelist.
- *
- * Returns: on success: the %QOSGraphEdge
- * otherwise: #NULL
- */
-static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist,
- const char *dest)
-{
- QOSGraphEdge *tmp, *next;
- if (!edgelist) {
- return NULL;
- }
- QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) {
- if (g_strcmp0(tmp->dest, dest) == 0) {
- break;
- }
- }
- return tmp;
-}
-
-/**
- * search_machine(): search for a machine @name in the node hash
- * table. A machine is the child of the root node.
- * This function forces the research in the childs of the root,
- * to check the node is a proper machine
- *
- * Returns: on success: the %QOSGraphNode
- * otherwise: #NULL
- */
-static QOSGraphNode *search_machine(const char *name)
-{
- QOSGraphNode *n;
- QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT);
- QOSGraphEdge *e = search_list_edges(root_list, name);
- if (!e) {
- return NULL;
- }
- n = search_node(e->dest);
- if (n->type == QNODE_MACHINE) {
- return n;
- }
- return NULL;
-}
-
-/**
- * create_interface(): checks if there is already
- * a node @node in the node hash table, if not
- * creates a node @node of type #QNODE_INTERFACE
- * and inserts it. If there is one, check it's
- * a #QNODE_INTERFACE and abort() if it's not.
- */
-static void create_interface(const char *node)
-{
- QOSGraphNode *interface;
- interface = search_node(node);
- if (!interface) {
- create_node(node, QNODE_INTERFACE);
- } else if (interface->type != QNODE_INTERFACE) {
- fprintf(stderr, "Error: Node %s is not an interface\n", node);
- abort();
- }
-}
-
-/**
- * build_machine_cmd_line(): builds the command line for the machine
- * @node. The node name must be a valid qemu identifier, since it
- * will be used to build the command line.
- *
- * It is also possible to pass an optional @args that will be
- * concatenated to the command line.
- *
- * For machines, prepend -M to the machine name. ", @rgs" is added
- * after the -M <machine> command.
- */
-static void build_machine_cmd_line(QOSGraphNode *node, const char *args)
-{
- char *machine = qos_get_machine_type(node->name);
- if (args) {
- node->command_line = g_strconcat("-M ", machine, ",", args, NULL);
- } else {
- node->command_line = g_strconcat("-M ", machine, " ", NULL);
- }
-}
-
-/**
- * build_driver_cmd_line(): builds the command line for the driver
- * @node. The node name must be a valid qemu identifier, since it
- * will be used to build the command line.
- *
- * Driver do not need additional command line, since it will be
- * provided by the edge options.
- *
- * For drivers, prepend -device to the node name.
- */
-static void build_driver_cmd_line(QOSGraphNode *node)
-{
- node->command_line = g_strconcat(" -device ", node->name, NULL);
-}
-
-/* qos_print_cb(): callback prints all path found by the DFS algorithm. */
-static void qos_print_cb(QOSGraphNode *path, int length)
-{
- #if QGRAPH_PRINT_DEBUG
- printf("%d elements\n", length);
-
- if (!path) {
- return;
- }
-
- while (path->path_edge) {
- printf("%s ", path->name);
- switch (path->path_edge->type) {
- case QEDGE_PRODUCES:
- printf("--PRODUCES--> ");
- break;
- case QEDGE_CONSUMED_BY:
- printf("--CONSUMED_BY--> ");
- break;
- case QEDGE_CONTAINS:
- printf("--CONTAINS--> ");
- break;
- }
- path = search_node(path->path_edge->dest);
- }
-
- printf("%s\n\n", path->name);
- #endif
-}
-
-/* qos_push(): push a node @el and edge @e in the qos_node_stack */
-static void qos_push(QOSGraphNode *el, QOSStackElement *parent,
- QOSGraphEdge *e)
-{
- int len = 0; /* root is not counted */
- if (qos_node_tos == QOS_PATH_MAX_ELEMENT_SIZE) {
- g_printerr("QOSStack: full stack, cannot push");
- abort();
- }
-
- if (parent) {
- len = parent->length + 1;
- }
- qos_node_stack[qos_node_tos++] = (QOSStackElement) {
- .node = el,
- .parent = parent,
- .parent_edge = e,
- .length = len,
- };
-}
-
-/* qos_tos(): returns the top of stack, without popping */
-static QOSStackElement *qos_tos(void)
-{
- return &qos_node_stack[qos_node_tos - 1];
-}
-
-/* qos_pop(): pops an element from the tos, setting it unvisited*/
-static QOSStackElement *qos_pop(void)
-{
- if (qos_node_tos == 0) {
- g_printerr("QOSStack: empty stack, cannot pop");
- abort();
- }
- QOSStackElement *e = qos_tos();
- e->node->visited = false;
- qos_node_tos--;
- return e;
-}
-
-/**
- * qos_reverse_path(): reverses the found path, going from
- * test-to-machine to machine-to-test
- */
-static QOSGraphNode *qos_reverse_path(QOSStackElement *el)
-{
- if (!el) {
- return NULL;
- }
-
- el->node->path_edge = NULL;
-
- while (el->parent) {
- el->parent->node->path_edge = el->parent_edge;
- el = el->parent;
- }
-
- return el->node;
-}
-
-/**
- * qos_traverse_graph(): graph-walking algorithm, using Depth First Search it
- * starts from the root @machine and walks all possible path until it
- * reaches a test node.
- * At that point, it reverses the path found and invokes the @callback.
- *
- * Being Depth First Search, time complexity is O(|V| + |E|), while
- * space is O(|V|). In this case, the maximum stack size is set by
- * QOS_PATH_MAX_ELEMENT_SIZE.
- */
-static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback)
-{
- QOSGraphNode *v, *dest_node, *path;
- QOSStackElement *s_el;
- QOSGraphEdge *e, *next;
- QOSGraphEdgeList *list;
-
- qos_push(root, NULL, NULL);
-
- while (qos_node_tos > 0) {
- s_el = qos_tos();
- v = s_el->node;
- if (v->visited) {
- qos_pop();
- continue;
- }
- v->visited = true;
- list = get_edgelist(v->name);
- if (!list) {
- qos_pop();
- if (v->type == QNODE_TEST) {
- v->visited = false;
- path = qos_reverse_path(s_el);
- callback(path, s_el->length);
- }
- } else {
- QSLIST_FOREACH_SAFE(e, list, edge_list, next) {
- dest_node = search_node(e->dest);
-
- if (!dest_node) {
- fprintf(stderr, "node %s in %s -> %s does not exist\n",
- e->dest, v->name, e->dest);
- abort();
- }
-
- if (!dest_node->visited && dest_node->available) {
- qos_push(dest_node, s_el, e);
- }
- }
- }
- }
-}
-
-/* QGRAPH API*/
-
-QOSGraphNode *qos_graph_get_node(const char *key)
-{
- return search_node(key);
-}
-
-bool qos_graph_has_node(const char *node)
-{
- QOSGraphNode *n = search_node(node);
- return n != NULL;
-}
-
-QOSNodeType qos_graph_get_node_type(const char *node)
-{
- QOSGraphNode *n = search_node(node);
- if (n) {
- return n->type;
- }
- return -1;
-}
-
-bool qos_graph_get_node_availability(const char *node)
-{
- QOSGraphNode *n = search_node(node);
- if (n) {
- return n->available;
- }
- return false;
-}
-
-QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest)
-{
- QOSGraphEdgeList *list = get_edgelist(node);
- return search_list_edges(list, dest);
-}
-
-QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge)
-{
- if (!edge) {
- return -1;
- }
- return edge->type;;
-}
-
-char *qos_graph_edge_get_dest(QOSGraphEdge *edge)
-{
- if (!edge) {
- return NULL;
- }
- return edge->dest;
-}
-
-void *qos_graph_edge_get_arg(QOSGraphEdge *edge)
-{
- if (!edge) {
- return NULL;
- }
- return edge->arg;
-}
-
-char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge)
-{
- if (!edge) {
- return NULL;
- }
- return edge->after_cmd_line;
-}
-
-char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge)
-{
- if (!edge) {
- return NULL;
- }
- return edge->before_cmd_line;
-}
-
-char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge)
-{
- if (!edge) {
- return NULL;
- }
- return edge->extra_device_opts;
-}
-
-char *qos_graph_edge_get_name(QOSGraphEdge *edge)
-{
- if (!edge) {
- return NULL;
- }
- return edge->edge_name;
-}
-
-bool qos_graph_has_edge(const char *start, const char *dest)
-{
- QOSGraphEdgeList *list = get_edgelist(start);
- QOSGraphEdge *e = search_list_edges(list, dest);
- return e != NULL;
-}
-
-QOSGraphNode *qos_graph_get_machine(const char *node)
-{
- return search_machine(node);
-}
-
-bool qos_graph_has_machine(const char *node)
-{
- QOSGraphNode *m = search_machine(node);
- return m != NULL;
-}
-
-void qos_print_graph(void)
-{
- qos_graph_foreach_test_path(qos_print_cb);
-}
-
-void qos_graph_init(void)
-{
- if (!node_table) {
- node_table = g_hash_table_new_full(g_str_hash, g_str_equal,
- destroy_string, destroy_node);
- create_node(QOS_ROOT, QNODE_DRIVER);
- }
-
- if (!edge_table) {
- edge_table = g_hash_table_new_full(g_str_hash, g_str_equal,
- destroy_string, destroy_edges);
- }
-}
-
-void qos_graph_destroy(void)
-{
- if (node_table) {
- g_hash_table_destroy(node_table);
- }
-
- if (edge_table) {
- g_hash_table_destroy(edge_table);
- }
-
- node_table = NULL;
- edge_table = NULL;
-}
-
-void qos_node_destroy(void *key)
-{
- g_hash_table_remove(node_table, key);
-}
-
-void qos_edge_destroy(void *key)
-{
- g_hash_table_remove(edge_table, key);
-}
-
-void qos_add_test(const char *name, const char *interface,
- QOSTestFunc test_func, QOSGraphTestOptions *opts)
-{
- QOSGraphNode *node;
- char *test_name = g_strdup_printf("%s-tests/%s", interface, name);;
- QOSGraphTestOptions def_opts = { };
-
- if (!opts) {
- opts = &def_opts;
- }
- node = create_node(test_name, QNODE_TEST);
- node->u.test.function = test_func;
- node->u.test.arg = opts->arg;
- assert(!opts->edge.arg);
- assert(!opts->edge.size_arg);
-
- node->u.test.before = opts->before;
- node->u.test.subprocess = opts->subprocess;
- node->available = true;
- add_edge(interface, test_name, QEDGE_CONSUMED_BY, &opts->edge);
- g_free(test_name);
-}
-
-void qos_node_create_machine(const char *name, QOSCreateMachineFunc function)
-{
- qos_node_create_machine_args(name, function, NULL);
-}
-
-void qos_node_create_machine_args(const char *name,
- QOSCreateMachineFunc function,
- const char *opts)
-{
- QOSGraphNode *node = create_node(name, QNODE_MACHINE);
- build_machine_cmd_line(node, opts);
- node->u.machine.constructor = function;
- add_edge(QOS_ROOT, name, QEDGE_CONTAINS, NULL);
-}
-
-void qos_node_create_driver(const char *name, QOSCreateDriverFunc function)
-{
- QOSGraphNode *node = create_node(name, QNODE_DRIVER);
- build_driver_cmd_line(node);
- node->u.driver.constructor = function;
-}
-
-void qos_node_contains(const char *container, const char *contained,
- QOSGraphEdgeOptions *opts, ...)
-{
- va_list va;
-
- if (opts == NULL) {
- add_edge(container, contained, QEDGE_CONTAINS, NULL);
- return;
- }
-
- va_start(va, opts);
- do {
- add_edge(container, contained, QEDGE_CONTAINS, opts);
- opts = va_arg(va, QOSGraphEdgeOptions *);
- } while (opts != NULL);
-
- va_end(va);
-}
-
-void qos_node_produces(const char *producer, const char *interface)
-{
- create_interface(interface);
- add_edge(producer, interface, QEDGE_PRODUCES, NULL);
-}
-
-void qos_node_consumes(const char *consumer, const char *interface,
- QOSGraphEdgeOptions *opts)
-{
- create_interface(interface);
- add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts);
-}
-
-void qos_graph_node_set_availability(const char *node, bool av)
-{
- QOSGraphEdgeList *elist;
- QOSGraphNode *n = search_node(node);
- QOSGraphEdge *e, *next;
- if (!n) {
- return;
- }
- n->available = av;
- elist = get_edgelist(node);
- if (!elist) {
- return;
- }
- QSLIST_FOREACH_SAFE(e, elist, edge_list, next) {
- if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES) {
- qos_graph_node_set_availability(e->dest, av);
- }
- }
-}
-
-void qos_graph_foreach_test_path(QOSTestCallback fn)
-{
- QOSGraphNode *root = qos_graph_get_node(QOS_ROOT);
- qos_traverse_graph(root, fn);
-}
-
-QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts)
-{
- QOSGraphObject *obj;
-
- g_assert(node->type == QNODE_MACHINE);
- obj = node->u.machine.constructor(qts);
- obj->free = g_free;
- return obj;
-}
-
-QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
- QGuestAllocator *alloc, void *arg)
-{
- QOSGraphObject *obj;
-
- g_assert(node->type == QNODE_DRIVER);
- obj = node->u.driver.constructor(parent, alloc, arg);
- obj->free = g_free;
- return obj;
-}
-
-void qos_object_destroy(QOSGraphObject *obj)
-{
- if (!obj) {
- return;
- }
- if (obj->destructor) {
- obj->destructor(obj);
- }
- if (obj->free) {
- obj->free(obj);
- }
-}
-
-void qos_object_queue_destroy(QOSGraphObject *obj)
-{
- g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj);
-}
-
-void qos_object_start_hw(QOSGraphObject *obj)
-{
- if (obj->start_hw) {
- obj->start_hw(obj);
- }
-}
-
-char *qos_get_machine_type(char *name)
-{
- while (*name != '\0' && *name != '/') {
- name++;
- }
-
- if (!*name || !name[1]) {
- fprintf(stderr, "Machine name has to be of the form <arch>/<machine>\n");
- abort();
- }
-
- return name + 1;
-}
-
-void qos_delete_cmd_line(const char *name)
-{
- QOSGraphNode *node = search_node(name);
- if (node) {
- g_free(node->command_line);
- node->command_line = NULL;
- }
-}