qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

qgraph.c (22778B)


      1 /*
      2  * libqos driver framework
      3  *
      4  * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Lesser General Public
      8  * License version 2.1 as published by the Free Software Foundation.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Lesser General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Lesser General Public
     16  * License along with this library; if not, see <http://www.gnu.org/licenses/>
     17  */
     18 
     19 #include "qemu/osdep.h"
     20 #include "../libqtest.h"
     21 #include "qemu/queue.h"
     22 #include "qgraph_internal.h"
     23 #include "qgraph.h"
     24 
     25 #define QGRAPH_PRINT_DEBUG 0
     26 #define QOS_ROOT ""
     27 typedef struct QOSStackElement QOSStackElement;
     28 
     29 /* Graph Edge.*/
     30 struct QOSGraphEdge {
     31     QOSEdgeType type;
     32     char *dest;
     33     void *arg;                /* just for QEDGE_CONTAINS
     34                                * and QEDGE_CONSUMED_BY */
     35     char *extra_device_opts;  /* added to -device option, "," is
     36                                * automatically added
     37                                */
     38     char *before_cmd_line;    /* added before node cmd_line */
     39     char *after_cmd_line;     /* added after -device options */
     40     char *edge_name;          /* used by QEDGE_CONTAINS */
     41     QSLIST_ENTRY(QOSGraphEdge) edge_list;
     42 };
     43 
     44 typedef QSLIST_HEAD(, QOSGraphEdge) QOSGraphEdgeList;
     45 
     46 /**
     47  * Stack used to keep track of the discovered path when using
     48  * the DFS algorithm
     49  */
     50 struct QOSStackElement {
     51     QOSGraphNode *node;
     52     QOSStackElement *parent;
     53     QOSGraphEdge *parent_edge;
     54     int length;
     55 };
     56 
     57 /* Each enty in these hash table will consist of <string, node/edge> pair. */
     58 static GHashTable *edge_table;
     59 static GHashTable *node_table;
     60 
     61 /* stack used by the DFS algorithm to store the path from machine to test */
     62 static QOSStackElement qos_node_stack[QOS_PATH_MAX_ELEMENT_SIZE];
     63 static int qos_node_tos;
     64 
     65 /**
     66  * add_edge(): creates an edge of type @type
     67  * from @source to @dest node, and inserts it in the
     68  * edges hash table
     69  *
     70  * Nodes @source and @dest do not necessarily need to exist.
     71  * Possibility to add also options (see #QOSGraphEdgeOptions)
     72  * edge->edge_name is used as identifier for get_device relationships,
     73  * so by default is equal to @dest.
     74  */
     75 static void add_edge(const char *source, const char *dest,
     76                      QOSEdgeType type, QOSGraphEdgeOptions *opts)
     77 {
     78     char *key;
     79     QOSGraphEdgeList *list = g_hash_table_lookup(edge_table, source);
     80     QOSGraphEdgeOptions def_opts = { };
     81 
     82     if (!list) {
     83         list = g_new0(QOSGraphEdgeList, 1);
     84         key = g_strdup(source);
     85         g_hash_table_insert(edge_table, key, list);
     86     }
     87 
     88     if (!opts) {
     89         opts = &def_opts;
     90     }
     91 
     92     QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1);
     93     edge->type = type;
     94     edge->dest = g_strdup(dest);
     95     edge->edge_name = g_strdup(opts->edge_name ?: dest);
     96     edge->arg = g_memdup2(opts->arg, opts->size_arg);
     97 
     98     edge->before_cmd_line =
     99         opts->before_cmd_line ? g_strconcat(" ", opts->before_cmd_line, NULL) : NULL;
    100     edge->extra_device_opts =
    101         opts->extra_device_opts ? g_strconcat(",", opts->extra_device_opts, NULL) : NULL;
    102     edge->after_cmd_line =
    103         opts->after_cmd_line ? g_strconcat(" ", opts->after_cmd_line, NULL) : NULL;
    104 
    105     QSLIST_INSERT_HEAD(list, edge, edge_list);
    106 }
    107 
    108 /* destroy_edges(): frees all edges inside a given @list */
    109 static void destroy_edges(void *list)
    110 {
    111     QOSGraphEdge *temp;
    112     QOSGraphEdgeList *elist = list;
    113 
    114     while (!QSLIST_EMPTY(elist)) {
    115         temp = QSLIST_FIRST(elist);
    116         QSLIST_REMOVE_HEAD(elist, edge_list);
    117         g_free(temp->dest);
    118         g_free(temp->before_cmd_line);
    119         g_free(temp->after_cmd_line);
    120         g_free(temp->extra_device_opts);
    121         g_free(temp->edge_name);
    122         g_free(temp->arg);
    123         g_free(temp);
    124     }
    125     g_free(elist);
    126 }
    127 
    128 /**
    129  * create_node(): creates a node @name of type @type
    130  * and inserts it to the nodes hash table.
    131  * By default, node is not available.
    132  */
    133 static QOSGraphNode *create_node(const char *name, QOSNodeType type)
    134 {
    135     if (g_hash_table_lookup(node_table, name)) {
    136         g_printerr("Node %s already created\n", name);
    137         abort();
    138     }
    139 
    140     QOSGraphNode *node = g_new0(QOSGraphNode, 1);
    141     node->type = type;
    142     node->available = false;
    143     node->name = g_strdup(name);
    144     g_hash_table_insert(node_table, node->name, node);
    145     return node;
    146 }
    147 
    148 /**
    149  * destroy_node(): frees a node @val from the nodes hash table.
    150  * Note that node->name is not free'd since it will represent the
    151  * hash table key
    152  */
    153 static void destroy_node(void *val)
    154 {
    155     QOSGraphNode *node = val;
    156     g_free(node->qemu_name);
    157     g_free(node->command_line);
    158     g_free(node);
    159 }
    160 
    161 /**
    162  * destroy_string(): frees @key from the nodes hash table.
    163  * Actually frees the node->name
    164  */
    165 static void destroy_string(void *key)
    166 {
    167     g_free(key);
    168 }
    169 
    170 /**
    171  * search_node(): search for a node @key in the nodes hash table
    172  * Returns the QOSGraphNode if found, #NULL otherwise
    173  */
    174 static QOSGraphNode *search_node(const char *key)
    175 {
    176     return g_hash_table_lookup(node_table, key);
    177 }
    178 
    179 /**
    180  * get_edgelist(): returns the edge list (value) assigned to
    181  * the @key in the edge hash table.
    182  * This list will contain all edges with source equal to @key
    183  *
    184  * Returns: on success: the %QOSGraphEdgeList
    185  *          otherwise: abort()
    186  */
    187 static QOSGraphEdgeList *get_edgelist(const char *key)
    188 {
    189     return g_hash_table_lookup(edge_table, key);
    190 }
    191 
    192 /**
    193  * search_list_edges(): search for an edge with destination @dest
    194  * in the given @edgelist.
    195  *
    196  * Returns: on success: the %QOSGraphEdge
    197  *          otherwise: #NULL
    198  */
    199 static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist,
    200                                        const char *dest)
    201 {
    202     QOSGraphEdge *tmp, *next;
    203     if (!edgelist) {
    204         return NULL;
    205     }
    206     QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) {
    207         if (g_strcmp0(tmp->dest, dest) == 0) {
    208             break;
    209         }
    210     }
    211     return tmp;
    212 }
    213 
    214 /**
    215  * search_machine(): search for a machine @name in the node hash
    216  * table. A machine is the child of the root node.
    217  * This function forces the research in the childs of the root,
    218  * to check the node is a proper machine
    219  *
    220  * Returns: on success: the %QOSGraphNode
    221  *          otherwise: #NULL
    222  */
    223 static QOSGraphNode *search_machine(const char *name)
    224 {
    225     QOSGraphNode *n;
    226     QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT);
    227     QOSGraphEdge *e = search_list_edges(root_list, name);
    228     if (!e) {
    229         return NULL;
    230     }
    231     n = search_node(e->dest);
    232     if (n->type == QNODE_MACHINE) {
    233         return n;
    234     }
    235     return NULL;
    236 }
    237 
    238 /**
    239  * create_interface(): checks if there is already
    240  * a node @node in the node hash table, if not
    241  * creates a node @node of type #QNODE_INTERFACE
    242  * and inserts it. If there is one, check it's
    243  * a #QNODE_INTERFACE and abort() if it's not.
    244  */
    245 static void create_interface(const char *node)
    246 {
    247     QOSGraphNode *interface;
    248     interface = search_node(node);
    249     if (!interface) {
    250         create_node(node, QNODE_INTERFACE);
    251     } else if (interface->type != QNODE_INTERFACE) {
    252         fprintf(stderr, "Error: Node %s is not an interface\n", node);
    253         abort();
    254     }
    255 }
    256 
    257 /**
    258  * build_machine_cmd_line(): builds the command line for the machine
    259  * @node. The node name must be a valid qemu identifier, since it
    260  * will be used to build the command line.
    261  *
    262  * It is also possible to pass an optional @args that will be
    263  * concatenated to the command line.
    264  *
    265  * For machines, prepend -M to the machine name. ", @rgs" is added
    266  * after the -M <machine> command.
    267  */
    268 static void build_machine_cmd_line(QOSGraphNode *node, const char *args)
    269 {
    270     char *machine = qos_get_machine_type(node->name);
    271     if (args) {
    272         node->command_line = g_strconcat("-M ", machine, ",", args, NULL);
    273     } else {
    274         node->command_line = g_strconcat("-M ", machine, " ", NULL);
    275     }
    276 }
    277 
    278 /**
    279  * build_driver_cmd_line(): builds the command line for the driver
    280  * @node. The node name must be a valid qemu identifier, since it
    281  * will be used to build the command line.
    282  *
    283  * Driver do not need additional command line, since it will be
    284  * provided by the edge options.
    285  *
    286  * For drivers, prepend -device to the node name.
    287  */
    288 static void build_driver_cmd_line(QOSGraphNode *node)
    289 {
    290     const char *name = node->qemu_name ?: node->name;
    291     node->command_line = g_strconcat(" -device ", name, NULL);
    292 }
    293 
    294 /* qos_print_cb(): callback prints all path found by the DFS algorithm. */
    295 static void qos_print_cb(QOSGraphNode *path, int length)
    296 {
    297     #if QGRAPH_PRINT_DEBUG
    298         printf("%d elements\n", length);
    299 
    300         if (!path) {
    301             return;
    302         }
    303 
    304         while (path->path_edge) {
    305             printf("%s ", path->name);
    306             switch (path->path_edge->type) {
    307             case QEDGE_PRODUCES:
    308                 printf("--PRODUCES--> ");
    309                 break;
    310             case QEDGE_CONSUMED_BY:
    311                 printf("--CONSUMED_BY--> ");
    312                 break;
    313             case QEDGE_CONTAINS:
    314                 printf("--CONTAINS--> ");
    315                 break;
    316             }
    317             path = search_node(path->path_edge->dest);
    318         }
    319 
    320         printf("%s\n\n", path->name);
    321     #endif
    322 }
    323 
    324 /* qos_push(): push a node @el and edge @e in the qos_node_stack */
    325 static void qos_push(QOSGraphNode *el, QOSStackElement *parent,
    326                      QOSGraphEdge *e)
    327 {
    328     int len = 0; /* root is not counted */
    329     if (qos_node_tos == QOS_PATH_MAX_ELEMENT_SIZE) {
    330         g_printerr("QOSStack: full stack, cannot push");
    331         abort();
    332     }
    333 
    334     if (parent) {
    335         len = parent->length + 1;
    336     }
    337     qos_node_stack[qos_node_tos++] = (QOSStackElement) {
    338         .node = el,
    339         .parent = parent,
    340         .parent_edge = e,
    341         .length = len,
    342     };
    343 }
    344 
    345 /* qos_tos(): returns the top of stack, without popping */
    346 static QOSStackElement *qos_tos(void)
    347 {
    348     return &qos_node_stack[qos_node_tos - 1];
    349 }
    350 
    351 /* qos_pop(): pops an element from the tos, setting it unvisited*/
    352 static QOSStackElement *qos_pop(void)
    353 {
    354     if (qos_node_tos == 0) {
    355         g_printerr("QOSStack: empty stack, cannot pop");
    356         abort();
    357     }
    358     QOSStackElement *e = qos_tos();
    359     e->node->visited = false;
    360     qos_node_tos--;
    361     return e;
    362 }
    363 
    364 /**
    365  * qos_reverse_path(): reverses the found path, going from
    366  * test-to-machine to machine-to-test
    367  */
    368 static QOSGraphNode *qos_reverse_path(QOSStackElement *el)
    369 {
    370     if (!el) {
    371         return NULL;
    372     }
    373 
    374     el->node->path_edge = NULL;
    375 
    376     while (el->parent) {
    377         el->parent->node->path_edge = el->parent_edge;
    378         el = el->parent;
    379     }
    380 
    381     return el->node;
    382 }
    383 
    384 /**
    385  * qos_traverse_graph(): graph-walking algorithm, using Depth First Search it
    386  * starts from the root @machine and walks all possible path until it
    387  * reaches a test node.
    388  * At that point, it reverses the path found and invokes the @callback.
    389  *
    390  * Being Depth First Search, time complexity is O(|V| + |E|), while
    391  * space is O(|V|). In this case, the maximum stack size is set by
    392  * QOS_PATH_MAX_ELEMENT_SIZE.
    393  */
    394 static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback)
    395 {
    396     QOSGraphNode *v, *dest_node, *path;
    397     QOSStackElement *s_el;
    398     QOSGraphEdge *e, *next;
    399     QOSGraphEdgeList *list;
    400 
    401     qos_push(root, NULL, NULL);
    402 
    403     while (qos_node_tos > 0) {
    404         s_el = qos_tos();
    405         v = s_el->node;
    406         if (v->visited) {
    407             qos_pop();
    408             continue;
    409         }
    410         v->visited = true;
    411         list = get_edgelist(v->name);
    412         if (!list) {
    413             qos_pop();
    414             if (v->type == QNODE_TEST) {
    415                 v->visited = false;
    416                 path = qos_reverse_path(s_el);
    417                 callback(path, s_el->length);
    418             }
    419         } else {
    420             QSLIST_FOREACH_SAFE(e, list, edge_list, next) {
    421                 dest_node = search_node(e->dest);
    422 
    423                 if (!dest_node) {
    424                     fprintf(stderr, "node %s in %s -> %s does not exist\n",
    425                             e->dest, v->name, e->dest);
    426                     abort();
    427                 }
    428 
    429                 if (!dest_node->visited && dest_node->available) {
    430                     qos_push(dest_node, s_el, e);
    431                 }
    432             }
    433         }
    434     }
    435 }
    436 
    437 /* QGRAPH API*/
    438 
    439 QOSGraphNode *qos_graph_get_node(const char *key)
    440 {
    441     return search_node(key);
    442 }
    443 
    444 bool qos_graph_has_node(const char *node)
    445 {
    446     QOSGraphNode *n = search_node(node);
    447     return n != NULL;
    448 }
    449 
    450 QOSNodeType qos_graph_get_node_type(const char *node)
    451 {
    452     QOSGraphNode *n = search_node(node);
    453     if (n) {
    454         return n->type;
    455     }
    456     return -1;
    457 }
    458 
    459 bool qos_graph_get_node_availability(const char *node)
    460 {
    461     QOSGraphNode *n = search_node(node);
    462     if (n) {
    463         return n->available;
    464     }
    465     return false;
    466 }
    467 
    468 QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest)
    469 {
    470     QOSGraphEdgeList *list = get_edgelist(node);
    471     return search_list_edges(list, dest);
    472 }
    473 
    474 QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge)
    475 {
    476     if (!edge) {
    477         return -1;
    478     }
    479     return edge->type;
    480 }
    481 
    482 char *qos_graph_edge_get_dest(QOSGraphEdge *edge)
    483 {
    484     if (!edge) {
    485         return NULL;
    486     }
    487     return edge->dest;
    488 }
    489 
    490 void *qos_graph_edge_get_arg(QOSGraphEdge *edge)
    491 {
    492     if (!edge) {
    493         return NULL;
    494     }
    495     return edge->arg;
    496 }
    497 
    498 char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge)
    499 {
    500     if (!edge) {
    501         return NULL;
    502     }
    503     return edge->after_cmd_line;
    504 }
    505 
    506 char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge)
    507 {
    508     if (!edge) {
    509         return NULL;
    510     }
    511     return edge->before_cmd_line;
    512 }
    513 
    514 char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge)
    515 {
    516     if (!edge) {
    517         return NULL;
    518     }
    519     return edge->extra_device_opts;
    520 }
    521 
    522 char *qos_graph_edge_get_name(QOSGraphEdge *edge)
    523 {
    524     if (!edge) {
    525         return NULL;
    526     }
    527     return edge->edge_name;
    528 }
    529 
    530 bool qos_graph_has_edge(const char *start, const char *dest)
    531 {
    532     QOSGraphEdgeList *list = get_edgelist(start);
    533     QOSGraphEdge *e = search_list_edges(list, dest);
    534     return e != NULL;
    535 }
    536 
    537 QOSGraphNode *qos_graph_get_machine(const char *node)
    538 {
    539     return search_machine(node);
    540 }
    541 
    542 bool qos_graph_has_machine(const char *node)
    543 {
    544     QOSGraphNode *m = search_machine(node);
    545     return m != NULL;
    546 }
    547 
    548 void qos_print_graph(void)
    549 {
    550     qos_graph_foreach_test_path(qos_print_cb);
    551 }
    552 
    553 void qos_graph_init(void)
    554 {
    555     if (!node_table) {
    556         node_table = g_hash_table_new_full(g_str_hash, g_str_equal,
    557                                            destroy_string, destroy_node);
    558         create_node(QOS_ROOT, QNODE_DRIVER);
    559     }
    560 
    561     if (!edge_table) {
    562         edge_table = g_hash_table_new_full(g_str_hash, g_str_equal,
    563                                            destroy_string, destroy_edges);
    564     }
    565 }
    566 
    567 void qos_graph_destroy(void)
    568 {
    569     if (node_table) {
    570         g_hash_table_destroy(node_table);
    571     }
    572 
    573     if (edge_table) {
    574         g_hash_table_destroy(edge_table);
    575     }
    576 
    577     node_table = NULL;
    578     edge_table = NULL;
    579 }
    580 
    581 void qos_node_destroy(void *key)
    582 {
    583     g_hash_table_remove(node_table, key);
    584 }
    585 
    586 void qos_edge_destroy(void *key)
    587 {
    588     g_hash_table_remove(edge_table, key);
    589 }
    590 
    591 void qos_add_test(const char *name, const char *interface,
    592                   QOSTestFunc test_func, QOSGraphTestOptions *opts)
    593 {
    594     QOSGraphNode *node;
    595     char *test_name = g_strdup_printf("%s-tests/%s", interface, name);
    596     QOSGraphTestOptions def_opts = { };
    597 
    598     if (!opts) {
    599         opts = &def_opts;
    600     }
    601     node = create_node(test_name, QNODE_TEST);
    602     node->u.test.function = test_func;
    603     node->u.test.arg = opts->arg;
    604     assert(!opts->edge.arg);
    605     assert(!opts->edge.size_arg);
    606 
    607     node->u.test.before = opts->before;
    608     node->u.test.subprocess = opts->subprocess;
    609     node->available = true;
    610     add_edge(interface, test_name, QEDGE_CONSUMED_BY, &opts->edge);
    611     g_free(test_name);
    612 }
    613 
    614 void qos_node_create_machine(const char *name, QOSCreateMachineFunc function)
    615 {
    616     qos_node_create_machine_args(name, function, NULL);
    617 }
    618 
    619 void qos_node_create_machine_args(const char *name,
    620                                   QOSCreateMachineFunc function,
    621                                   const char *opts)
    622 {
    623     QOSGraphNode *node = create_node(name, QNODE_MACHINE);
    624     build_machine_cmd_line(node, opts);
    625     node->u.machine.constructor = function;
    626     add_edge(QOS_ROOT, name, QEDGE_CONTAINS, NULL);
    627 }
    628 
    629 void qos_node_create_driver(const char *name, QOSCreateDriverFunc function)
    630 {
    631     QOSGraphNode *node = create_node(name, QNODE_DRIVER);
    632     build_driver_cmd_line(node);
    633     node->u.driver.constructor = function;
    634 }
    635 
    636 void qos_node_create_driver_named(const char *name, const char *qemu_name,
    637                                   QOSCreateDriverFunc function)
    638 {
    639     QOSGraphNode *node = create_node(name, QNODE_DRIVER);
    640     node->qemu_name = g_strdup(qemu_name);
    641     build_driver_cmd_line(node);
    642     node->u.driver.constructor = function;
    643 }
    644 
    645 void qos_node_contains(const char *container, const char *contained,
    646                        QOSGraphEdgeOptions *opts, ...)
    647 {
    648     va_list va;
    649 
    650     if (opts == NULL) {
    651         add_edge(container, contained, QEDGE_CONTAINS, NULL);
    652         return;
    653     }
    654 
    655     va_start(va, opts);
    656     do {
    657         add_edge(container, contained, QEDGE_CONTAINS, opts);
    658         opts = va_arg(va, QOSGraphEdgeOptions *);
    659     } while (opts != NULL);
    660 
    661     va_end(va);
    662 }
    663 
    664 void qos_node_produces(const char *producer, const char *interface)
    665 {
    666     create_interface(interface);
    667     add_edge(producer, interface, QEDGE_PRODUCES, NULL);
    668 }
    669 
    670 void qos_node_consumes(const char *consumer, const char *interface,
    671                        QOSGraphEdgeOptions *opts)
    672 {
    673     create_interface(interface);
    674     add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts);
    675 }
    676 
    677 static void qos_graph_node_set_availability_explicit(const char *node, bool av)
    678 {
    679     QOSGraphEdgeList *elist;
    680     QOSGraphNode *n = search_node(node);
    681     QOSGraphEdge *e, *next;
    682     if (!n) {
    683         return;
    684     }
    685     n->available = av;
    686     elist = get_edgelist(node);
    687     if (!elist) {
    688         return;
    689     }
    690     QSLIST_FOREACH_SAFE(e, elist, edge_list, next) {
    691         if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES) {
    692             qos_graph_node_set_availability_explicit(e->dest, av);
    693         }
    694     }
    695 }
    696 
    697 /*
    698  * Behaves as qos_graph_node_set_availability_explicit(), except that the
    699  * former always matches by node name only, whereas this function matches both
    700  * by node name and node's optional 'qemu_name' field.
    701  */
    702 void qos_graph_node_set_availability(const char *node, bool av)
    703 {
    704     GList *l;
    705     QOSGraphEdgeList *elist;
    706     QOSGraphEdge *e, *next;
    707     QOSGraphNode *n;
    708     GList *keys = g_hash_table_get_keys(node_table);
    709 
    710     for (l = keys; l != NULL; l = l->next) {
    711         const gchar *key = l->data;
    712         n = g_hash_table_lookup(node_table, key);
    713         /*
    714          * node's 'qemu_name' is set if there is more than one device with
    715          * the same QEMU (QMP) device name
    716          */
    717         const char *node_name = n->qemu_name ?: n->name;
    718         if (g_strcmp0(node_name, node) == 0) {
    719             n->available = av;
    720             elist = get_edgelist(n->name);
    721             if (elist) {
    722                 QSLIST_FOREACH_SAFE(e, elist, edge_list, next) {
    723                     if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES)
    724                     {
    725                         qos_graph_node_set_availability_explicit(e->dest, av);
    726                     }
    727                 }
    728             }
    729         }
    730     }
    731     g_list_free(keys);
    732 }
    733 
    734 void qos_graph_foreach_test_path(QOSTestCallback fn)
    735 {
    736     QOSGraphNode *root = qos_graph_get_node(QOS_ROOT);
    737     qos_traverse_graph(root, fn);
    738 }
    739 
    740 QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts)
    741 {
    742     QOSGraphObject *obj;
    743 
    744     g_assert(node->type == QNODE_MACHINE);
    745     obj = node->u.machine.constructor(qts);
    746     obj->free = g_free;
    747     return obj;
    748 }
    749 
    750 QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
    751                                QGuestAllocator *alloc, void *arg)
    752 {
    753     QOSGraphObject *obj;
    754 
    755     g_assert(node->type == QNODE_DRIVER);
    756     obj = node->u.driver.constructor(parent, alloc, arg);
    757     obj->free = g_free;
    758     return obj;
    759 }
    760 
    761 void qos_object_destroy(QOSGraphObject *obj)
    762 {
    763     if (!obj) {
    764         return;
    765     }
    766     if (obj->destructor) {
    767         obj->destructor(obj);
    768     }
    769     if (obj->free) {
    770         obj->free(obj);
    771     }
    772 }
    773 
    774 void qos_object_queue_destroy(QOSGraphObject *obj)
    775 {
    776     g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj);
    777 }
    778 
    779 void qos_object_start_hw(QOSGraphObject *obj)
    780 {
    781     if (obj->start_hw) {
    782         obj->start_hw(obj);
    783     }
    784 }
    785 
    786 char *qos_get_machine_type(char *name)
    787 {
    788     while (*name != '\0' && *name != '/') {
    789         name++;
    790     }
    791 
    792     if (!*name || !name[1]) {
    793         fprintf(stderr, "Machine name has to be of the form <arch>/<machine>\n");
    794         abort();
    795     }
    796 
    797     return name + 1;
    798 }
    799 
    800 void qos_delete_cmd_line(const char *name)
    801 {
    802     QOSGraphNode *node = search_node(name);
    803     if (node) {
    804         g_free(node->command_line);
    805         node->command_line = NULL;
    806     }
    807 }
    808 
    809 void qos_dump_graph(void)
    810 {
    811     GList *keys;
    812     GList *l;
    813     QOSGraphEdgeList *list;
    814     QOSGraphEdge *e, *next;
    815     QOSGraphNode *dest_node, *node;
    816 
    817     qos_printf("ALL QGRAPH EDGES: {\n");
    818     keys = g_hash_table_get_keys(edge_table);
    819     for (l = keys; l != NULL; l = l->next) {
    820         const gchar *key = l->data;
    821         qos_printf("\t src='%s'\n", key);
    822         list = get_edgelist(key);
    823         QSLIST_FOREACH_SAFE(e, list, edge_list, next) {
    824             dest_node = g_hash_table_lookup(node_table, e->dest);
    825             qos_printf("\t\t|-> dest='%s' type=%d (node=%p)",
    826                        e->dest, e->type, dest_node);
    827             if (!dest_node) {
    828                 qos_printf_literal(" <------- ERROR !");
    829             }
    830             qos_printf_literal("\n");
    831         }
    832     }
    833     g_list_free(keys);
    834     qos_printf("}\n");
    835 
    836     qos_printf("ALL QGRAPH NODES: {\n");
    837     keys = g_hash_table_get_keys(node_table);
    838     for (l = keys; l != NULL; l = l->next) {
    839         const gchar *key = l->data;
    840         node = g_hash_table_lookup(node_table, key);
    841         qos_printf("\t name='%s' ", key);
    842         if (node->qemu_name) {
    843             qos_printf_literal("qemu_name='%s' ", node->qemu_name);
    844         }
    845         qos_printf_literal("type=%d cmd_line='%s' [%s]\n",
    846                            node->type, node->command_line,
    847                            node->available ? "available" : "UNAVAILABLE"
    848         );
    849     }
    850     g_list_free(keys);
    851     qos_printf("}\n");
    852 }