device-introspect-test: New, covering device introspection

The test doesn't check that the output makes any sense, only that QEMU
survives.  Useful since we've had an astounding number of crash bugs
around there.

In fact, we have a bunch of them right now: a few devices crash or
hang, and some leave dangling pointers behind.  The test skips testing
the broken parts.  The next commits will fix them up, and drop the
skipping.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <1443689999-12182-8-git-send-email-armbru@redhat.com>
This commit is contained in:
Markus Armbruster 2015-10-01 10:59:56 +02:00
parent 5fb48d9673
commit 2d1abb850f
2 changed files with 163 additions and 3 deletions

View File

@ -86,7 +86,8 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
# All QTests for now are POSIX-only, but the dependencies are
# really in libqtest, not in the testcases themselves.
check-qtest-generic-y =
check-qtest-generic-y = tests/device-introspect-test$(EXESUF)
gcov-files-generic-y = qdev-monitor.c qmp.c
gcov-files-ipack-y += hw/ipack/ipack.c
check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF)
@ -383,6 +384,7 @@ libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o
libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o
libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
tests/endianness-test$(EXESUF): tests/endianness-test.o
@ -490,7 +492,7 @@ $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y)
QTEST_QEMU_IMG=qemu-img$(EXESUF) \
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \
gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER $@")
$(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y); do \
$(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y) $(gcov-files-generic-y); do \
echo Gcov report for $$f:;\
$(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \
done,)
@ -501,7 +503,7 @@ $(patsubst %, check-%, $(check-unit-y)): check-%: %
$(call quiet-command, \
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \
gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER $*")
$(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y); do \
$(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y) $(gcov-files-generic-y); do \
echo Gcov report for $$f:;\
$(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \
done,)

View File

@ -0,0 +1,158 @@
/*
* Device introspection test cases
*
* Copyright (c) 2015 Red Hat Inc.
*
* Authors:
* Markus Armbruster <armbru@redhat.com>,
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
/*
* Covers QMP device-list-properties and HMP device_add help. We
* currently don't check that their output makes sense, only that QEMU
* survives. Useful since we've had an astounding number of crash
* bugs around here.
*/
#include <glib.h>
#include <stdarg.h>
#include "qemu-common.h"
#include "qapi/qmp/qstring.h"
#include "libqtest.h"
const char common_args[] = "-nodefaults -machine none";
static QList *device_type_list(bool abstract)
{
QDict *resp;
QList *ret;
resp = qmp("{'execute': 'qom-list-types',"
" 'arguments': {'implements': 'device', 'abstract': %i}}",
abstract);
g_assert(qdict_haskey(resp, "return"));
ret = qdict_get_qlist(resp, "return");
QINCREF(ret);
QDECREF(resp);
return ret;
}
static void test_one_device(const char *type)
{
QDict *resp;
char *help, *qom_tree;
/*
* Skip this part for the abstract device test case, because
* device-list-properties crashes for such devices.
* FIXME fix it not to crash
*/
if (strcmp(type, "device")) {
resp = qmp("{'execute': 'device-list-properties',"
" 'arguments': {'typename': %s}}",
type);
QDECREF(resp);
}
help = hmp("device_add \"%s,help\"", type);
g_free(help);
/*
* Some devices leave dangling pointers in QOM behind.
* "info qom-tree" has a good chance at crashing then
*/
qom_tree = hmp("info qom-tree");
g_free(qom_tree);
}
static void test_device_intro_list(void)
{
QList *types;
char *help;
qtest_start(common_args);
types = device_type_list(true);
QDECREF(types);
help = hmp("device_add help");
g_free(help);
qtest_end();
}
static void test_device_intro_none(void)
{
qtest_start(common_args);
test_one_device("nonexistent");
qtest_end();
}
static void test_device_intro_abstract(void)
{
qtest_start(common_args);
test_one_device("device");
qtest_end();
}
static bool blacklisted(const char *type)
{
static const char *blacklist[] = {
/* hang in object_unref(): */
"realview_pci", "versatile_pci",
/* create a CPU, thus use after free (see below): */
"allwinner-a10", "digic", "fsl,imx25", "fsl,imx31", "xlnx,zynqmp",
};
size_t len = strlen(type);
int i;
if (len >= 4 && !strcmp(type + len - 4, "-cpu")) {
/* use after free: cpu_exec_init() saves CPUState in cpus */
return true;
}
for (i = 0; i < ARRAY_SIZE(blacklist); i++) {
if (!strcmp(blacklist[i], type)) {
return true;
}
}
return false;
}
static void test_device_intro_concrete(void)
{
QList *types;
QListEntry *entry;
const char *type;
qtest_start(common_args);
types = device_type_list(false);
QLIST_FOREACH_ENTRY(types, entry) {
type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)),
"name");
g_assert(type);
if (blacklisted(type)) {
continue; /* FIXME broken device, skip */
}
test_one_device(type);
}
QDECREF(types);
qtest_end();
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("device/introspect/list", test_device_intro_list);
qtest_add_func("device/introspect/none", test_device_intro_none);
qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
qtest_add_func("device/introspect/concrete", test_device_intro_concrete);
return g_test_run();
}