All of lore.kernel.org
 help / color / mirror / Atom feed
From: Thomas Huth <thuth@redhat.com>
To: Paolo Bonzini <pbonzini@redhat.com>, qemu-devel@nongnu.org
Cc: lvivier@redhat.com,
	Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
Subject: Re: [Qemu-devel] [PATCH 5/5] tests: qgraph API for the qtest driver framework
Date: Fri, 18 Jan 2019 17:39:41 +0100	[thread overview]
Message-ID: <a4424580-d38d-492f-e996-f58a58485e81@redhat.com> (raw)
In-Reply-To: <1547576349-13337-6-git-send-email-pbonzini@redhat.com>

On 2019-01-15 19:19, Paolo Bonzini wrote:
> From: Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
> 
> Add qgraph API that allows to add/remove nodes and edges from the graph,
> implementation of Depth First Search to discover the paths and basic unit
> test to check correctness of the API.
> Included also a main executable that takes care of starting the framework,
> create the nodes, set the available drivers/machines, discover the path and
> run tests.
> 
> graph.h provides the public API to manage the graph nodes/edges
> graph_extra.h provides a more private API used successively by the gtest integration part
> qos-test.c provides the main executable
> 
> Signed-off-by: Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
> [Paolo's changes compared to the Google Summer of Code submission:
>  * added subprocess to test options
>  * refactored object creation to support live migration tests
>  * removed driver .before callback (unused)
>  * removed test .after callbacks (replaced by GTest destruction queue)]
> 
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
[...]
> +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);
> +
> +    if (!list) {
> +        list = g_new0(QOSGraphEdgeList, 1);
> +        key = g_strdup(source);
> +        g_hash_table_insert(edge_table, key, list);
> +    }
> +
> +    if (!opts) {
> +        opts = &(QOSGraphEdgeOptions) { };

Not sure, but this part looks suspicious to me ... isn't that pointer
going to be invalid as soon as the if-statement scope is over in the
next line?

> +    }
> +
> +    QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1);
> +    edge->type = type;
> +    edge->dest = g_strdup(dest);
> +    edge->edge_name = g_strdup(opts->edge_name ? : dest);

I think I'd write the elvis operator rather as "?:", without the space
inbetween. (or does our checkpatch script dislike that?)

> +    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);
> +}
[...]
> +bool qos_graph_has_edge(const char *start, const char *dest)
> +{
> +    QOSGraphEdgeList *list = get_edgelist(start);
> +    QOSGraphEdge *e = search_list_edges(list, dest);
> +    if (e) {
> +        return TRUE;
> +    }
> +    return FALSE;
> +}

I'm surprised that TRUE and FALSE are also still available with capital
letters these days ... The spelling from stdbool.h is lowercase.

Anyway, maybe rather:

    return e != NULL;

?

[...]
> +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);;
> +
> +    if (!opts) {
> +        opts = &(QOSGraphTestOptions) { };

Same as above ... is the pointer still valid after the next curly brace?

> +    }
> +    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);
> +}
[...]
> + * 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 attibutes

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 attibutes 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.
[...]
> + * - destructor: Opposite to the node constructor, destroys the object.
> + * This function is called after the test has been executed, and performs
> + * a complete cleanup of each node allocated field. In case no constuctor

constructor

> + * is provided, no destructor will be called.
> + *
> + */
> +struct QOSGraphObject {
> +    /* for produces edges, returns void * */
> +    QOSGetDriver get_driver;
> +    /* for contains edges, returns a QOSGraphObject * */
> +    QOSGetDevice get_device;
> +    /* start the hw, get ready for the test */
> +    QOSStartFunct start_hw;
> +    /* destroy this QOSGraphObject */
> +    QOSDestructorFunc destructor;
> +    /* free the memory associated to the QOSGraphObject and its contained
> +     * children */
> +    GDestroyNotify free;
> +};
[...]
> +static void apply_to_node(const char *name, bool is_machine, bool is_abstract)
> +{
> +    char *machine_name = NULL;
> +    if (is_machine) {
> +        const char *arch = qtest_get_arch();
> +        machine_name = g_strconcat(arch, "/", name, NULL);
> +        name = machine_name;
> +    }
> +    qos_graph_node_set_availability(name, TRUE);

true

> +    if (is_abstract) {
> +        qos_delete_cmd_line(name);
> +    }
> +    g_free(machine_name);
> +}
> +
> +/**
> + * apply_to_qlist(): using QMP queries QEMU for a list of
> + * machines and devices available, and sets the respective node
> + * as TRUE. If a node is found, also all its produced and contained
> + * child are marked available.
> + *
> + * See qos_graph_node_set_availability() for more info
> + */
> +static void apply_to_qlist(QList *list, bool is_machine)
> +{
> +    const QListEntry *p;
> +    const char *name;
> +    bool abstract;
> +    QDict *minfo;
> +    QObject *qobj;
> +    QString *qstr;
> +    QBool *qbool;
> +
> +    for (p = qlist_first(list); p; p = qlist_next(p)) {
> +        minfo = qobject_to(QDict, qlist_entry_obj(p));
> +        qobj = qdict_get(minfo, "name");
> +        qstr = qobject_to(QString, qobj);
> +        name = qstring_get_str(qstr);
> +
> +        qobj = qdict_get(minfo, "abstract");
> +        if (qobj) {
> +            qbool = qobject_to(QBool, qobj);
> +            abstract = qbool_get_bool(qbool);
> +        } else {
> +            abstract = false;
> +        }
> +
> +        apply_to_node(name, is_machine, abstract);
> +        qobj = qdict_get(minfo, "alias");
> +        if (qobj) {
> +            qstr = qobject_to(QString, qobj);
> +            name = qstring_get_str(qstr);
> +            apply_to_node(name, is_machine, abstract);
> +        }
> +    }
> +}
> +
> +/**
> + * qos_set_machines_devices_available(): sets availability of qgraph
> + * machines and devices.
> + *
> + * This function firstly starts QEMU with "-machine none" option,
> + * and then executes the QMP protocol asking for the list of devices
> + * and machines available.
> + *
> + * for each of these items, it looks up the corresponding qgraph node,
> + * setting it as available. The list currently returns all devices that
> + * are either machines or QEDGE_CONSUMED_BY other nodes.
> + * Therefore, in order to mark all other nodes, it recursively sets
> + * all its QEDGE_CONTAINS and QEDGE_PRODUCES child as available too.
> + */
> +static void qos_set_machines_devices_available(void)
> +{
> +    QDict *response;
> +    QDict *args = qdict_new();
> +    QList *list;
> +
> +    qtest_start("-machine none");
> +    response = qmp("{ 'execute': 'query-machines' }");
> +    list = qdict_get_qlist(response, "return");
> +
> +    apply_to_qlist(list, TRUE);

true

> +    qobject_unref(response);
> +
> +    qdict_put_bool(args, "abstract", TRUE);
true

> +    qdict_put_str(args, "implements", "device");
> +
> +    response = qmp("{'execute': 'qom-list-types',"
> +                   " 'arguments': %p }", args);
> +    g_assert(qdict_haskey(response, "return"));
> +    list = qdict_get_qlist(response, "return");
> +
> +    apply_to_qlist(list, FALSE);

false

> +    qtest_end();
> +    qobject_unref(response);
> +

Remove superfluous empty line?

> +}
> +
> +static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj)
> +{
> +    if (obj->get_driver) {
> +        return obj->get_driver(obj, "memory");
> +    } else {
> +        return NULL;
> +    }
> +}

Maybe remove the "else" like this:

   if (obj->get_driver) {
       return obj->get_driver(obj, "memory");
   }
   return NULL;

?

 Thomas

  reply	other threads:[~2019-01-18 16:39 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-15 18:19 [Qemu-devel] [PATCH 0/5] qtest driver framework (core only) Paolo Bonzini
2019-01-15 18:19 ` [Qemu-devel] [PATCH 1/5] tests/libqos: introduce virtio_start_device Paolo Bonzini
2019-01-16 17:16   ` Laurent Vivier
2019-01-18 11:46   ` Thomas Huth
2019-01-15 18:19 ` [Qemu-devel] [PATCH 2/5] tests/libqos: rename qpci_init_pc and qpci_init_spapr functions Paolo Bonzini
2019-01-16 19:55   ` Laurent Vivier
2019-01-18 12:23   ` Thomas Huth
2019-01-18 12:45     ` Paolo Bonzini
2019-01-15 18:19 ` [Qemu-devel] [PATCH 3/5] tests: remove rule for nonexisting qdev-monitor-test Paolo Bonzini
2019-01-16  5:43   ` Thomas Huth
2019-01-17  9:47   ` Laurent Vivier
2019-01-15 18:19 ` [Qemu-devel] [PATCH 4/5] tests/libqos: embed allocators instead of malloc-ing them separately Paolo Bonzini
2019-01-17 10:37   ` Laurent Vivier
2019-01-18 12:52   ` Thomas Huth
2019-01-18 12:59     ` Paolo Bonzini
2019-01-15 18:19 ` [Qemu-devel] [PATCH 5/5] tests: qgraph API for the qtest driver framework Paolo Bonzini
2019-01-18 16:39   ` Thomas Huth [this message]
2019-01-18 16:58     ` Eric Blake
2019-01-18 17:07     ` Paolo Bonzini

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a4424580-d38d-492f-e996-f58a58485e81@redhat.com \
    --to=thuth@redhat.com \
    --cc=e.emanuelegiuseppe@gmail.com \
    --cc=lvivier@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.