Makefile: Rename targets for make recursion
We make a few sub-directories recursively, in particular
$(TARGET_DIRS).
For goal "all", we do it the nice way: "all" has a prerequisite
subdir-T for each T in $(TARGET_DIRS), and T's recipe runs make
recursively. Behaves nicely with -j and -k.
For other goals such as "clean" and "install", the recipe runs make
recursively in a for loop. Ignores -j and -k.
The next commit will fix that for "clean" and "install". This commit
prepares the ground by renaming the targets we use for "all" to
include the goal for the sub-make. This will permit reusing them for
goals other than "all".
Targets subdir-T for T in $(TARGET_DIRS) run "make all" in T. Rename
to T/all, and declare phony.
Targets romsubdir-R for R in $(ROMS) run "make" in pc-bios/R. Default
goal is "all" for all R. Rename to pc-bios/R/all, and declare phony.
The remainder are renamed just for consistency.
Target subdir-dtc runs "make libbft/libfdt.a" in dtc. Rename to
dtc/all, and declare phony.
Target subdir-capstone runs make $(BUILD_DIR)/capstone/$(LIBCAPSTONE)
in $(SRC_PATH)/capstone. Rename to capstone/all, and declare phony.
Target subdir-slirp runs "make" in $(SRC_PATH)/slirp. Default goal is
all, which builds $(BUILD_DIR)/libslirp.a. Rename to slirp/all, and
declare phony.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20190528082308.22032-4-armbru@redhat.com>
[Add compatibility gunk to keep make working across the rename]
2019-05-28 11:23:07 +03:00
|
|
|
# -*- Mode: makefile -*-
|
2017-08-10 11:50:25 +03:00
|
|
|
|
|
|
|
.PHONY: check-help
|
|
|
|
check-help:
|
|
|
|
@echo "Regression testing targets:"
|
|
|
|
@echo
|
2020-05-05 13:24:51 +03:00
|
|
|
@echo " $(MAKE) check Run block, qapi-schema, unit, softfloat, qtest and decodetree tests"
|
2018-11-09 18:07:10 +03:00
|
|
|
@echo
|
2017-11-21 12:55:10 +03:00
|
|
|
@echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target"
|
|
|
|
@echo " $(MAKE) check-qtest Run qtest tests"
|
|
|
|
@echo " $(MAKE) check-unit Run qobject tests"
|
|
|
|
@echo " $(MAKE) check-speed Run qobject speed tests"
|
|
|
|
@echo " $(MAKE) check-qapi-schema Run QAPI schema tests"
|
|
|
|
@echo " $(MAKE) check-block Run block tests"
|
2020-05-22 20:25:00 +03:00
|
|
|
ifeq ($(CONFIG_TCG),y)
|
2018-04-07 00:08:36 +03:00
|
|
|
@echo " $(MAKE) check-tcg Run TCG tests"
|
2019-01-07 20:25:44 +03:00
|
|
|
@echo " $(MAKE) check-softfloat Run FPU emulation tests"
|
2020-05-22 20:25:00 +03:00
|
|
|
endif
|
2018-10-18 18:31:33 +03:00
|
|
|
@echo " $(MAKE) check-acceptance Run all acceptance (functional) tests"
|
2018-11-09 18:07:10 +03:00
|
|
|
@echo
|
2019-12-11 23:44:27 +03:00
|
|
|
@echo " $(MAKE) check-report.tap Generates an aggregated TAP test report"
|
2018-10-18 18:31:32 +03:00
|
|
|
@echo " $(MAKE) check-venv Creates a Python venv for tests"
|
2018-11-09 18:07:10 +03:00
|
|
|
@echo " $(MAKE) check-clean Clean the tests and related data"
|
2017-08-10 11:50:25 +03:00
|
|
|
@echo
|
2020-07-01 16:56:51 +03:00
|
|
|
@echo "The following are useful for CI builds"
|
|
|
|
@echo " $(MAKE) check-build Build most test binaris"
|
2020-03-17 17:16:54 +03:00
|
|
|
@echo " $(MAKE) get-vm-images Downloads all images used by acceptance tests, according to configured targets (~350 MB each, 1.5 GB max)"
|
|
|
|
@echo
|
2017-08-10 11:50:25 +03:00
|
|
|
@echo
|
|
|
|
@echo "The variable SPEED can be set to control the gtester speed setting."
|
2017-11-21 12:55:10 +03:00
|
|
|
@echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be"
|
2017-08-10 11:50:25 +03:00
|
|
|
@echo "changed with variable GTESTER_OPTIONS."
|
|
|
|
|
|
|
|
ifneq ($(wildcard config-host.mak),)
|
2012-03-08 15:29:00 +04:00
|
|
|
export SRC_PATH
|
|
|
|
|
2018-02-26 22:48:58 +03:00
|
|
|
# TODO don't duplicate $(SRC_PATH)/Makefile's qapi-py here
|
2019-10-18 10:43:44 +03:00
|
|
|
qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi/commands.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi/common.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi/doc.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi/error.py \
|
2018-02-26 22:48:58 +03:00
|
|
|
$(SRC_PATH)/scripts/qapi/events.py \
|
2019-10-18 10:43:44 +03:00
|
|
|
$(SRC_PATH)/scripts/qapi/expr.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi/gen.py \
|
2018-02-26 22:48:58 +03:00
|
|
|
$(SRC_PATH)/scripts/qapi/introspect.py \
|
2019-10-18 10:43:44 +03:00
|
|
|
$(SRC_PATH)/scripts/qapi/parser.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi/schema.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi/source.py \
|
2018-02-26 22:48:58 +03:00
|
|
|
$(SRC_PATH)/scripts/qapi/types.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi/visit.py \
|
|
|
|
$(SRC_PATH)/scripts/qapi-gen.py
|
2015-09-02 13:35:52 +03:00
|
|
|
|
2014-01-23 20:22:59 +04:00
|
|
|
# Get the list of all supported sysemu targets
|
|
|
|
SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \
|
|
|
|
$(wildcard $(SRC_PATH)/default-configs/*-softmmu.mak)))
|
|
|
|
|
2018-10-11 17:47:51 +03:00
|
|
|
check-unit-y += tests/check-qdict$(EXESUF)
|
2018-09-26 15:23:09 +03:00
|
|
|
check-unit-y += tests/check-block-qdict$(EXESUF)
|
2017-06-07 19:35:58 +03:00
|
|
|
check-unit-y += tests/check-qnum$(EXESUF)
|
2012-03-28 17:42:01 +04:00
|
|
|
check-unit-y += tests/check-qstring$(EXESUF)
|
|
|
|
check-unit-y += tests/check-qlist$(EXESUF)
|
2016-04-29 00:45:21 +03:00
|
|
|
check-unit-y += tests/check-qnull$(EXESUF)
|
2017-11-14 21:01:28 +03:00
|
|
|
check-unit-y += tests/check-qobject$(EXESUF)
|
2012-03-28 17:42:01 +04:00
|
|
|
check-unit-y += tests/check-qjson$(EXESUF)
|
2017-08-25 13:59:09 +03:00
|
|
|
check-unit-y += tests/check-qlit$(EXESUF)
|
2016-09-30 17:45:27 +03:00
|
|
|
check-unit-y += tests/test-qobject-output-visitor$(EXESUF)
|
qapi: Add new clone visitor
We have a couple places in the code base that want to deep-clone
one QAPI object into another, and they were resorting to serializing
the struct out to QObject then reparsing it. A much more efficient
version can be done by adding a new clone visitor.
Since cloning is still relatively uncommon, expose the use of the
new visitor via a QAPI_CLONE() macro that takes care of type-punning
the underlying function pointer, rather than generating lots of
unused functions for types that won't be cloned. And yes, we're
relying on the compiler treating all pointers equally, even though
a strict C program cannot portably do so - but we're not the first
one in the qemu code base to expect it to work (hello, glib!).
The choice of adding a fourth visitor type deserves some explanation.
On the surface, the clone visitor is mostly an input visitor (it
takes arbitrary input - in this case, another QAPI object - and
creates a new QAPI object during the course of the visit). But
ever since commit da72ab0 consolidated enum visits based on the
visitor type, using VISITOR_INPUT would cause us to run
visit_type_str(), even though for cloning there is nothing to do
(we just copy the enum value across, without regards to its mapping
to strings). Also, since our input happens to be a QAPI object,
we can also satisfy the internal checks for VISITOR_OUTPUT. So in
the end, I settled with a new VISITOR_CLONE, and chose its value
such that many internal checks can use 'v->type & mask', sticking
to 'v->type == value' where the difference matters.
Note that we can only clone objects (including alternates) and lists,
not built-ins or enums. The visitor core hides integer width from
the actual visitor (since commit 04e070d), and as long as that's the
case, we can't clone top-level integers. Then again, those can
always be cloned by direct copy, since they are not objects with
deep pointers, so it's no real loss. And restricting cloning to
just objects and lists is cleaner than restricting it to non-integers.
As such, I documented that the clone visitor is for direct use only
by code internal to QAPI, and should not be used on incomplete objects
(other than a hack to work around the fact that we allow NULL in place
of "" in visit_type_str() in other output visitors). Note that as
written, the clone visitor will never fail on a complete object.
Scalars (including enums) not at the root of the clone copy just fine
with no additional effort while visiting the scalar, by virtue of a
g_memdup() each time we push another struct onto the stack. Cloning
a string requires deduplication of a pointer, which means it can also
provide the guarantee of an input visitor of never producing NULL
even when still accepting NULL in place of "" the way the QMP output
visitor does.
Cloning an 'any' type could be possible by incrementing the QObject
refcnt, but it's not obvious whether that is better than implementing
a QObject deep clone. So for now, we document it as unsupported,
and intentionally omit the .type_any() callback to let a developer
know their usage needs implementation.
Add testsuite coverage for several different clone situations, to
ensure that the code is working. I also tested that valgrind was
happy with the test.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-14-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:44 +03:00
|
|
|
check-unit-y += tests/test-clone-visitor$(EXESUF)
|
2016-09-30 17:45:27 +03:00
|
|
|
check-unit-y += tests/test-qobject-input-visitor$(EXESUF)
|
2020-05-22 20:25:01 +03:00
|
|
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-qmp-cmds$(EXESUF)
|
2012-03-28 17:42:01 +04:00
|
|
|
check-unit-y += tests/test-string-input-visitor$(EXESUF)
|
|
|
|
check-unit-y += tests/test-string-output-visitor$(EXESUF)
|
2014-06-18 10:43:29 +04:00
|
|
|
check-unit-y += tests/test-qmp-event$(EXESUF)
|
2013-08-20 02:35:40 +04:00
|
|
|
check-unit-y += tests/test-opts-visitor$(EXESUF)
|
2019-04-01 17:12:22 +03:00
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-coroutine$(EXESUF)
|
2012-02-22 07:05:07 +04:00
|
|
|
check-unit-y += tests/test-visitor-serialization$(EXESUF)
|
2020-05-22 20:25:01 +03:00
|
|
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-iov$(EXESUF)
|
2019-06-03 09:50:49 +03:00
|
|
|
check-unit-y += tests/test-bitmap$(EXESUF)
|
2019-04-01 17:12:22 +03:00
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-aio$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-aio-multithread$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-throttle$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-thread-pool$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-hbitmap$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-bdrv-drain$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-bdrv-graph-mod$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-blockjob$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-blockjob-txn$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-block-backend$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-block-iothread$(EXESUF)
|
2020-08-03 09:31:19 +03:00
|
|
|
ifeq ($(CONFIG_POSIX),y)
|
2019-04-01 17:12:22 +03:00
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-image-locking$(EXESUF)
|
2020-08-03 09:31:19 +03:00
|
|
|
endif
|
2013-01-23 21:58:27 +04:00
|
|
|
check-unit-y += tests/test-x86-cpuid$(EXESUF)
|
|
|
|
# all code tested by test-x86-cpuid is inside topology.h
|
2014-12-12 14:13:38 +03:00
|
|
|
ifeq ($(CONFIG_SOFTMMU),y)
|
2013-01-31 11:12:16 +04:00
|
|
|
check-unit-y += tests/test-xbzrle$(EXESUF)
|
2014-12-12 14:13:38 +03:00
|
|
|
check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF)
|
|
|
|
endif
|
2013-02-04 22:27:45 +04:00
|
|
|
check-unit-y += tests/test-cutils$(EXESUF)
|
2017-01-10 05:10:09 +03:00
|
|
|
check-unit-y += tests/test-shift128$(EXESUF)
|
2013-02-17 00:47:01 +04:00
|
|
|
check-unit-y += tests/test-mul64$(EXESUF)
|
2013-06-20 18:19:32 +04:00
|
|
|
check-unit-y += tests/test-int128$(EXESUF)
|
|
|
|
# all code tested by test-int128 is inside int128.h
|
2013-06-21 11:09:34 +04:00
|
|
|
check-unit-y += tests/rcutorture$(EXESUF)
|
2013-08-27 19:38:45 +04:00
|
|
|
check-unit-y += tests/test-rcu-list$(EXESUF)
|
2018-08-19 12:13:32 +03:00
|
|
|
check-unit-y += tests/test-rcu-simpleq$(EXESUF)
|
2018-08-19 12:13:33 +03:00
|
|
|
check-unit-y += tests/test-rcu-tailq$(EXESUF)
|
2020-02-20 13:38:28 +03:00
|
|
|
check-unit-y += tests/test-rcu-slist$(EXESUF)
|
2016-06-08 21:55:27 +03:00
|
|
|
check-unit-y += tests/test-qdist$(EXESUF)
|
2016-06-08 21:55:29 +03:00
|
|
|
check-unit-y += tests/test-qht$(EXESUF)
|
2019-01-14 17:54:38 +03:00
|
|
|
check-unit-y += tests/test-qht-par$(EXESUF)
|
2013-06-28 15:40:32 +04:00
|
|
|
check-unit-y += tests/test-bitops$(EXESUF)
|
2016-12-09 17:36:00 +03:00
|
|
|
check-unit-y += tests/test-bitcnt$(EXESUF)
|
2020-08-04 21:00:40 +03:00
|
|
|
check-unit-y += tests/test-qgraph$(EXESUF)
|
2013-12-21 01:14:40 +04:00
|
|
|
check-unit-y += tests/check-qom-interface$(EXESUF)
|
qom: Add object_new_with_props() / object_new_withpropv() helpers
It is reasonably common to want to create an object, set a
number of properties, register it in the hierarchy and then
mark it as complete (if a user creatable type). This requires
quite a lot of error prone, verbose, boilerplate code to achieve.
First a pair of functions object_set_props() / object_set_propv()
are added which allow for a list of objects to be set in
one single API call.
Then object_new_with_props() / object_new_with_propv() constructors
are added which simplify the sequence of calls to create an
object, populate properties, register in the object composition
tree and mark the object complete, into a single method call.
Usage would be:
Error *err = NULL;
Object *obj;
obj = object_new_with_propv(TYPE_MEMORY_BACKEND_FILE,
object_get_objects_root(),
"hostmem0",
&err,
"share", "yes",
"mem-path", "/dev/shm/somefile",
"prealloc", "yes",
"size", "1048576",
NULL);
Note all property values are passed in string form and will
be parsed into their required data types, using normal QOM
semantics for parsing from string format.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
2015-05-13 19:14:06 +03:00
|
|
|
check-unit-y += tests/check-qom-proplist$(EXESUF)
|
2014-05-20 01:53:55 +04:00
|
|
|
check-unit-y += tests/test-qemu-opts$(EXESUF)
|
keyval: New keyval_parse()
keyval_parse() parses KEY=VALUE,... into a QDict. Works like
qemu_opts_parse(), except:
* Returns a QDict instead of a QemuOpts (d'oh).
* Supports nesting, unlike QemuOpts: a KEY is split into key
fragments at '.' (dotted key convention; the block layer does
something similar on top of QemuOpts). The key fragments are QDict
keys, and the last one's value is updated to VALUE.
* Each key fragment may be up to 127 bytes long. qemu_opts_parse()
limits the entire key to 127 bytes.
* Overlong key fragments are rejected. qemu_opts_parse() silently
truncates them.
* Empty key fragments are rejected. qemu_opts_parse() happily
accepts empty keys.
* It does not store the returned value. qemu_opts_parse() stores it
in the QemuOptsList.
* It does not treat parameter "id" specially. qemu_opts_parse()
ignores all but the first "id", and fails when its value isn't
id_wellformed(), or duplicate (a QemuOpts with the same ID is
already stored). It also screws up when a value contains ",id=".
* Implied value is not supported. qemu_opts_parse() desugars "foo" to
"foo=on", and "nofoo" to "foo=off".
* An implied key's value can't be empty, and can't contain ','.
I intend to grow this into a saner replacement for QemuOpts. It'll
take time, though.
Note: keyval_parse() provides no way to do lists, and its key syntax
is incompatible with the __RFQDN_ prefix convention for downstream
extensions, because it blindly splits at '.', even in __RFQDN_. Both
issues will be addressed later in the series.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1488317230-26248-4-git-send-email-armbru@redhat.com>
2017-03-01 00:26:49 +03:00
|
|
|
check-unit-y += tests/test-keyval$(EXESUF)
|
2019-04-01 17:12:22 +03:00
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-write-threshold$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-hash$(EXESUF)
|
|
|
|
check-speed-$(CONFIG_BLOCK) += tests/benchmark-crypto-hash$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-hmac$(EXESUF)
|
|
|
|
check-speed-$(CONFIG_BLOCK) += tests/benchmark-crypto-hmac$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-cipher$(EXESUF)
|
|
|
|
check-speed-$(CONFIG_BLOCK) += tests/benchmark-crypto-cipher$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-secret$(EXESUF)
|
|
|
|
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_GNUTLS)) += tests/test-crypto-tlscredsx509$(EXESUF)
|
|
|
|
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_GNUTLS)) += tests/test-crypto-tlssession$(EXESUF)
|
2020-06-12 22:02:35 +03:00
|
|
|
ifndef CONFIG_TSAN
|
|
|
|
# Some tests: test-char, test-qdev-global-props, and test-qga,
|
|
|
|
# are not runnable under TSan due to a known issue.
|
|
|
|
# https://github.com/google/sanitizers/issues/1116
|
|
|
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-char$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-qdev-global-props$(EXESUF)
|
2019-07-18 14:19:02 +03:00
|
|
|
ifeq ($(CONFIG_GUEST_AGENT),y)
|
2019-01-29 11:38:07 +03:00
|
|
|
check-unit-$(call land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF)
|
2016-04-19 10:39:13 +03:00
|
|
|
endif
|
2020-06-12 22:02:35 +03:00
|
|
|
endif
|
2020-05-22 20:25:01 +03:00
|
|
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-timed-average$(EXESUF)
|
|
|
|
check-unit-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_INOTIFY1)) += tests/test-util-filemonitor$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-util-sockets$(EXESUF)
|
2019-04-01 17:12:22 +03:00
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-authz-simple$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-authz-list$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-authz-listfile$(EXESUF)
|
|
|
|
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_AUTH_PAM)) += tests/test-authz-pam$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-io-task$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-io-channel-socket$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-io-channel-file$(EXESUF)
|
|
|
|
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_GNUTLS)) += tests/test-io-channel-tls$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-io-channel-command$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-io-channel-buffer$(EXESUF)
|
2020-05-22 20:25:01 +03:00
|
|
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-base64$(EXESUF)
|
2019-04-01 17:12:22 +03:00
|
|
|
check-unit-$(call land,$(CONFIG_BLOCK),$(if $(CONFIG_NETTLE),y,$(CONFIG_GCRYPT))) += tests/test-crypto-pbkdf$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-ivgen$(EXESUF)
|
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-afsplit$(EXESUF)
|
2019-10-30 18:17:40 +03:00
|
|
|
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_QEMU_PRIVATE_XTS)) += tests/test-crypto-xts$(EXESUF)
|
2019-04-01 17:12:22 +03:00
|
|
|
check-unit-$(CONFIG_BLOCK) += tests/test-crypto-block$(EXESUF)
|
2016-03-15 17:30:20 +03:00
|
|
|
check-unit-y += tests/test-logging$(EXESUF)
|
2019-04-01 17:12:22 +03:00
|
|
|
check-unit-$(call land,$(CONFIG_BLOCK),$(CONFIG_REPLICATION)) += tests/test-replication$(EXESUF)
|
2020-05-22 20:25:01 +03:00
|
|
|
check-unit-$(CONFIG_SOFTMMU) += tests/test-bufferiszero$(EXESUF)
|
2016-09-21 07:27:24 +03:00
|
|
|
check-unit-y += tests/test-uuid$(EXESUF)
|
2016-10-17 21:22:17 +03:00
|
|
|
check-unit-y += tests/ptimer-test$(EXESUF)
|
2017-03-01 00:27:03 +03:00
|
|
|
check-unit-y += tests/test-qapi-util$(EXESUF)
|
2012-03-28 17:42:01 +04:00
|
|
|
|
2019-04-01 17:12:19 +03:00
|
|
|
generated-files-y += tests/test-qapi-types.h
|
|
|
|
generated-files-y += tests/include/test-qapi-types-sub-module.h
|
|
|
|
generated-files-y += tests/test-qapi-types-sub-sub-module.h
|
|
|
|
generated-files-y += tests/test-qapi-visit.h
|
|
|
|
generated-files-y += tests/include/test-qapi-visit-sub-module.h
|
|
|
|
generated-files-y += tests/test-qapi-visit-sub-sub-module.h
|
|
|
|
generated-files-y += tests/test-qapi-commands.h
|
2019-11-20 21:25:48 +03:00
|
|
|
generated-files-y += tests/test-qapi-init-commands.h
|
2019-04-01 17:12:19 +03:00
|
|
|
generated-files-y += tests/include/test-qapi-commands-sub-module.h
|
|
|
|
generated-files-y += tests/test-qapi-commands-sub-sub-module.h
|
2019-11-20 21:25:47 +03:00
|
|
|
generated-files-y += tests/test-qapi-emit-events.h
|
2019-04-01 17:12:19 +03:00
|
|
|
generated-files-y += tests/test-qapi-events.h
|
|
|
|
generated-files-y += tests/include/test-qapi-events-sub-module.h
|
|
|
|
generated-files-y += tests/test-qapi-events-sub-sub-module.h
|
|
|
|
generated-files-y += tests/test-qapi-introspect.h
|
2012-03-28 17:42:01 +04:00
|
|
|
|
2019-09-09 13:04:01 +03:00
|
|
|
QEMU_CFLAGS += -I$(SRC_PATH)/tests -I$(SRC_PATH)/tests/qtest
|
2012-03-28 17:42:01 +04:00
|
|
|
|
2015-09-02 13:35:52 +03:00
|
|
|
|
|
|
|
# Deps that are common to various different sets of tests below
|
2017-09-19 17:20:31 +03:00
|
|
|
test-util-obj-y = libqemuutil.a
|
2015-09-02 13:18:16 +03:00
|
|
|
test-qom-obj-y = $(qom-obj-y) $(test-util-obj-y)
|
2019-03-01 18:40:48 +03:00
|
|
|
test-qapi-obj-y = tests/test-qapi-types.o \
|
|
|
|
tests/include/test-qapi-types-sub-module.o \
|
|
|
|
tests/test-qapi-types-sub-sub-module.o \
|
|
|
|
tests/test-qapi-visit.o \
|
|
|
|
tests/include/test-qapi-visit-sub-module.o \
|
|
|
|
tests/test-qapi-visit-sub-sub-module.o \
|
qapi: Eliminate indirection through qmp_event_get_func_emit()
The qapi_event_send_FOO() functions emit events like this:
QMPEventFuncEmit emit;
emit = qmp_event_get_func_emit();
if (!emit) {
return;
}
qmp = qmp_event_build_dict("FOO");
[put event arguments into @qmp...]
emit(QAPI_EVENT_FOO, qmp);
The value of qmp_event_get_func_emit() depends only on the program:
* In qemu-system-FOO, it's always monitor_qapi_event_queue.
* In tests/test-qmp-event, it's always event_test_emit.
* In all other programs, it's always null.
This is exactly the kind of dependence the linker is supposed to
resolve; we don't actually need an indirection.
Note that things would fall apart if we linked more than one QAPI
schema into a single program: each set of qapi_event_send_FOO() uses
its own event enumeration, yet they share a single emit function.
Which takes the event enumeration as an argument. Which one if
there's more than one?
More seriously: how does this work even now? qemu-system-FOO wants
QAPIEvent, and passes a function taking that to
qmp_event_set_func_emit(). test-qmp-event wants test_QAPIEvent, and
passes a function taking that to qmp_event_set_func_emit().
It works by type trickery, of course:
typedef void (*QMPEventFuncEmit)(unsigned event, QDict *dict);
void qmp_event_set_func_emit(QMPEventFuncEmit emit);
QMPEventFuncEmit qmp_event_get_func_emit(void);
We use unsigned instead of the enumeration type. Relies on both
enumerations boiling down to unsigned, which happens to be true for
the compilers we use.
Clean this up as follows:
* Generate qapi_event_send_FOO() that call PREFIX_qapi_event_emit()
instead of the value of qmp_event_set_func_emit().
* Generate a prototype for PREFIX_qapi_event_emit() into
qapi-events.h.
* PREFIX_ is empty for qapi/qapi-schema.json, and test_ for
tests/qapi-schema/qapi-schema-test.json. It's qga_ for
qga/qapi-schema.json, and doc-good- for
tests/qapi-schema/doc-good.json, but those don't define any events.
* Rename monitor_qapi_event_queue() to qapi_event_emit() instead of
passing it to qmp_event_set_func_emit(). This takes care of
qemu-system-FOO.
* Rename event_test_emit() to test_qapi_event_emit() instead of
passing it to qmp_event_set_func_emit(). This takes care of
tests/test-qmp-event.
* Add a qapi_event_emit() that does nothing to stubs/monitor.c. This
takes care of all other programs that link code emitting QMP events.
* Drop qmp_event_set_func_emit(), qmp_event_get_func_emit().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20181218182234.28876-3-armbru@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
[Commit message typos fixed]
2018-12-18 21:22:21 +03:00
|
|
|
tests/test-qapi-introspect.o \
|
2015-09-02 13:35:52 +03:00
|
|
|
$(test-qom-obj-y)
|
2019-04-01 17:12:22 +03:00
|
|
|
benchmark-crypto-obj-$(CONFIG_BLOCK) = $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y)
|
|
|
|
test-crypto-obj-$(CONFIG_BLOCK) = $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y)
|
|
|
|
test-io-obj-$(CONFIG_BLOCK) = $(io-obj-y) $(test-crypto-obj-y)
|
|
|
|
test-authz-obj-$(CONFIG_BLOCK) = $(test-qom-obj-y) $(authz-obj-y)
|
|
|
|
test-block-obj-$(CONFIG_BLOCK) = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
|
2015-09-02 13:35:52 +03:00
|
|
|
|
2017-06-07 19:35:58 +03:00
|
|
|
tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y)
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
|
|
|
|
tests/check-qdict$(EXESUF): tests/check-qdict.o $(test-util-obj-y)
|
2018-06-14 22:14:29 +03:00
|
|
|
tests/check-block-qdict$(EXESUF): tests/check-block-qdict.o $(test-util-obj-y)
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/check-qlist$(EXESUF): tests/check-qlist.o $(test-util-obj-y)
|
2016-04-29 00:45:21 +03:00
|
|
|
tests/check-qnull$(EXESUF): tests/check-qnull.o $(test-util-obj-y)
|
2017-11-14 21:01:28 +03:00
|
|
|
tests/check-qobject$(EXESUF): tests/check-qobject.o $(test-util-obj-y)
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
|
2017-08-25 13:59:09 +03:00
|
|
|
tests/check-qlit$(EXESUF): tests/check-qlit.o $(test-util-obj-y)
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
|
|
|
|
tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
|
2016-10-22 12:53:00 +03:00
|
|
|
|
2019-09-06 18:17:24 +03:00
|
|
|
tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y) $(test-io-obj-y) $(chardev-obj-y) tests/socket-helpers.o
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
|
|
|
|
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
|
2017-02-13 16:52:19 +03:00
|
|
|
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
|
2017-12-05 16:05:02 +03:00
|
|
|
tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
|
2019-02-23 22:20:41 +03:00
|
|
|
tests/test-bdrv-graph-mod$(EXESUF): tests/test-bdrv-graph-mod.o $(test-block-obj-y) $(test-util-obj-y)
|
2016-07-29 17:31:41 +03:00
|
|
|
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
|
2015-11-06 02:13:20 +03:00
|
|
|
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
|
2018-02-16 19:50:14 +03:00
|
|
|
tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y)
|
block: Fix hangs in synchronous APIs with iothreads
In the block layer, synchronous APIs are often implemented by creating a
coroutine that calls the asynchronous coroutine-based implementation and
then waiting for completion with BDRV_POLL_WHILE().
For this to work with iothreads (more specifically, when the synchronous
API is called in a thread that is not the home thread of the block
device, so that the coroutine will run in a different thread), we must
make sure to call aio_wait_kick() at the end of the operation. Many
places are missing this, so that BDRV_POLL_WHILE() keeps hanging even if
the condition has long become false.
Note that bdrv_dec_in_flight() involves an aio_wait_kick() call. This
corresponds to the BDRV_POLL_WHILE() in the drain functions, but it is
generally not enough for most other operations because they haven't set
the return value in the coroutine entry stub yet. To avoid race
conditions there, we need to kick after setting the return value.
The race window is small enough that the problem doesn't usually surface
in the common path. However, it does surface and causes easily
reproducible hangs if the operation can return early before even calling
bdrv_inc/dec_in_flight, which many of them do (trivial error or no-op
success paths).
The bug in bdrv_truncate(), bdrv_check() and bdrv_invalidate_cache() is
slightly different: These functions even neglected to schedule the
coroutine in the home thread of the node. This avoids the hang, but is
obviously wrong, too. Fix those to schedule the coroutine in the right
AioContext in addition to adding aio_wait_kick() calls.
Cc: qemu-stable@nongnu.org
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
2019-01-07 15:02:48 +03:00
|
|
|
tests/test-block-iothread$(EXESUF): tests/test-block-iothread.o $(test-block-obj-y) $(test-util-obj-y)
|
2018-10-11 10:21:35 +03:00
|
|
|
tests/test-image-locking$(EXESUF): tests/test-image-locking.o $(test-block-obj-y) $(test-util-obj-y)
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
|
|
|
|
tests/test-iov$(EXESUF): tests/test-iov.o $(test-util-obj-y)
|
2017-06-28 15:05:25 +03:00
|
|
|
tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o $(test-util-obj-y) $(test-crypto-obj-y)
|
2019-06-03 09:50:49 +03:00
|
|
|
tests/test-bitmap$(EXESUF): tests/test-bitmap.o $(test-util-obj-y)
|
2013-01-23 21:58:27 +04:00
|
|
|
tests/test-x86-cpuid$(EXESUF): tests/test-x86-cpuid.o
|
2019-07-24 18:16:22 +03:00
|
|
|
tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o migration/libmigration.fa $(test-util-obj-y) \
|
|
|
|
$(test-io-obj-y)
|
2017-08-18 21:47:35 +03:00
|
|
|
tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o $(test-util-obj-y)
|
2013-06-20 18:19:32 +04:00
|
|
|
tests/test-int128$(EXESUF): tests/test-int128.o
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/rcutorture$(EXESUF): tests/rcutorture.o $(test-util-obj-y)
|
|
|
|
tests/test-rcu-list$(EXESUF): tests/test-rcu-list.o $(test-util-obj-y)
|
2018-08-19 12:13:32 +03:00
|
|
|
tests/test-rcu-simpleq$(EXESUF): tests/test-rcu-simpleq.o $(test-util-obj-y)
|
2018-08-19 12:13:33 +03:00
|
|
|
tests/test-rcu-tailq$(EXESUF): tests/test-rcu-tailq.o $(test-util-obj-y)
|
2020-02-20 13:38:28 +03:00
|
|
|
tests/test-rcu-slist$(EXESUF): tests/test-rcu-slist.o $(test-util-obj-y)
|
2016-06-08 21:55:27 +03:00
|
|
|
tests/test-qdist$(EXESUF): tests/test-qdist.o $(test-util-obj-y)
|
2016-06-08 21:55:29 +03:00
|
|
|
tests/test-qht$(EXESUF): tests/test-qht.o $(test-util-obj-y)
|
2016-06-08 21:55:31 +03:00
|
|
|
tests/test-qht-par$(EXESUF): tests/test-qht-par.o tests/qht-bench$(EXESUF) $(test-util-obj-y)
|
qht: add qht-bench, a performance benchmark
This serves as a performance benchmark as well as a stress test
for QHT. We can tweak quite a number of things, including the
number of resize threads and how frequently resizes are triggered.
A performance comparison of QHT vs CLHT[1] and ck_hs[2] using
this same benchmark program can be found here:
http://imgur.com/a/0Bms4
The tests are run on a 64-core AMD Opteron 6376, pinning threads
to cores favoring same-socket cores. For each run, qht-bench is
invoked with:
$ tests/qht-bench -d $duration -n $n -u $u -g $range
, where $duration is in seconds, $n is the number of threads,
$u is the update rate (0.0 to 100.0), and $range is the number
of keys.
Note that ck_hs's performance drops significantly as writes go
up, since it requires an external lock (I used a ck_spinlock)
around every write.
Also, note that CLHT instead of using a seqlock, relies on an
allocator that does not ever return the same address during the
same read-critical section. This gives it a slight performance
advantage over QHT on read-heavy workloads, since the seqlock
writes aren't there.
[1] CLHT: https://github.com/LPD-EPFL/CLHT
https://infoscience.epfl.ch/record/207109/files/ascy_asplos15.pdf
[2] ck_hs: http://concurrencykit.org/
http://backtrace.io/blog/blog/2015/03/13/workload-specialization/
A few of those plots are shown in text here, since that site
might not be online forever. Throughput is on Mops/s on the Y axis.
200K keys, 0 % updates
450 ++--+------+------+-------+-------+-------+-------+------+-------+--++
| + + + + + + + + +N+ |
400 ++ ---+E+ ++
| +++---- |
350 ++ 9 ++------+------++ --+E+ -+H+ ++
| | +H+- | -+N+---- ---- +++ |
300 ++ 8 ++ +E+ ++ -----+E+ --+H+ ++
| | +++ | -+N+-----+H+-- |
250 ++ 7 ++------+------++ +++-----+E+---- ++
200 ++ 1 -+E+-----+H+ ++
| ---- qht +-E--+ |
150 ++ -+E+ clht +-H--+ ++
| ---- ck +-N--+ |
100 ++ +E+ ++
| ---- |
50 ++ -+E+ ++
| +E+E+ + + + + + + + + |
0 ++--E------+------+-------+-------+-------+-------+------+-------+--++
1 8 16 24 32 40 48 56 64
Number of threads
200K keys, 1 % updates
350 ++--+------+------+-------+-------+-------+-------+------+-------+--++
| + + + + + + + + -+E+ |
300 ++ -----+H+ ++
| +E+-- |
| 9 ++------+------++ +++---- |
250 ++ | +E+ -- | -+E+ ++
| 8 ++ -- ++ ---- |
200 ++ | +++- | +++ ---+E+ ++
| 7 ++------N------++ -+E+-- qht +-E--+ |
| 1 +++---- clht +-H--+ |
150 ++ -+E+ ck +-N--+ ++
| ---- |
100 ++ +E+ ++
| ---- |
| -+E+ |
50 ++ +H+-+N+----+N+-----+N+------ ++
| +E+E+ + + + +N+-----+N+-----+N+----+N+-----+N+ |
0 ++--E------+------+-------+-------+-------+-------+------+-------+--++
1 8 16 24 32 40 48 56 64
Number of threads
200K keys, 20 % updates
300 ++--+------+------+-------+-------+-------+-------+------+-------+--++
| + + + + + + + + + |
| -+H+ |
250 ++ ---- ++
| 9 ++------+------++ --+H+ ---+E+ |
| 8 ++ +H+-- ++ -+H+----+E+-- |
200 ++ | +E+ --| -----+E+-- +++ ++
| 7 ++ + ---- ++ ---+H+---- +++ qht +-E--+ |
150 ++ 6 ++------N------++ -+H+-----+E+ clht +-H--+ ++
| 1 -----+E+-- ck +-N--+ |
| -+H+---- |
100 ++ -----+E+ ++
| +E+-- |
| ----+++ |
50 ++ -+E+ ++
| +E+ +++ |
| +E+N+-+N+-----+ + + + + + + |
0 ++--E------+------N-------N-------N-------N-------N------N-------N--++
1 8 16 24 32 40 48 56 64
Number of threads
200K keys, 100 % updates qht +-E--+
clht +-H--+
160 ++--+------+------+-------+-------+-------+-------+---ck-+-N-----+--++
| + + + + + + + + ----H |
140 ++ +H+-- -+E+ ++
| +++---- ---- |
120 ++ 8 ++------+------++ -+H+ +E+ ++
| 7 ++ +H+---- ++ ---- +++---- |
100 ++ | +E+ | +++ ---+H+ -+E+ ++
| 6 ++ +++ ++ -+H+-- +++---- |
80 ++ 5 ++------N----------+E+-----+E+ ++
| 1 -+H+---- +++ |
| -----+E+ |
60 ++ +H+---- +++ ++
| ----+E+ |
40 ++ +H+---- ++
| --+E+ |
20 ++ +E+ ++
| +EE+ + + + + + + + + |
0 ++--+N-N---N------N-------N-------N-------N-------N------N-------N--++
1 8 16 24 32 40 48 56 64
Number of threads
Signed-off-by: Emilio G. Cota <cota@braap.org>
Message-Id: <1465412133-3029-13-git-send-email-cota@braap.org>
Signed-off-by: Richard Henderson <rth@twiddle.net>
2016-06-08 21:55:30 +03:00
|
|
|
tests/qht-bench$(EXESUF): tests/qht-bench.o $(test-util-obj-y)
|
2016-08-29 21:46:16 +03:00
|
|
|
tests/test-bufferiszero$(EXESUF): tests/test-bufferiszero.o $(test-util-obj-y)
|
2016-06-27 22:02:05 +03:00
|
|
|
tests/atomic_add-bench$(EXESUF): tests/atomic_add-bench.o $(test-util-obj-y)
|
2018-09-11 02:27:43 +03:00
|
|
|
tests/atomic64-bench$(EXESUF): tests/atomic64-bench.o $(test-util-obj-y)
|
2013-06-21 11:09:34 +04:00
|
|
|
|
2019-08-15 14:24:58 +03:00
|
|
|
tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o hw/core/libhwcore.fa \
|
2015-09-02 13:35:52 +03:00
|
|
|
$(test-qapi-obj-y)
|
2019-07-24 18:16:22 +03:00
|
|
|
tests/test-vmstate$(EXESUF): tests/test-vmstate.o migration/libmigration.fa \
|
2016-04-27 13:05:08 +03:00
|
|
|
$(test-io-obj-y)
|
2017-02-13 16:52:18 +03:00
|
|
|
tests/test-timed-average$(EXESUF): tests/test-timed-average.o $(test-util-obj-y)
|
2017-09-19 17:20:31 +03:00
|
|
|
tests/test-base64$(EXESUF): tests/test-base64.o $(test-util-obj-y)
|
2017-09-19 17:18:42 +03:00
|
|
|
tests/ptimer-test$(EXESUF): tests/ptimer-test.o tests/ptimer-test-stubs.o hw/core/ptimer.o
|
2019-09-09 15:00:55 +03:00
|
|
|
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
|
|
|
|
tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) $(test-qapi-obj-y)
|
|
|
|
tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
|
|
|
|
tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y)
|
|
|
|
tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y)
|
2012-03-28 17:42:01 +04:00
|
|
|
|
2016-03-15 17:30:20 +03:00
|
|
|
tests/test-logging$(EXESUF): tests/test-logging.o $(test-util-obj-y)
|
|
|
|
|
2016-07-27 10:01:51 +03:00
|
|
|
tests/test-replication$(EXESUF): tests/test-replication.o $(test-util-obj-y) \
|
|
|
|
$(test-block-obj-y)
|
|
|
|
|
2018-02-26 22:48:58 +03:00
|
|
|
tests/test-qapi-types.c tests/test-qapi-types.h \
|
2019-03-01 18:40:48 +03:00
|
|
|
tests/include/test-qapi-types-sub-module.c \
|
|
|
|
tests/include/test-qapi-types-sub-module.h \
|
|
|
|
tests/test-qapi-types-sub-sub-module.c \
|
|
|
|
tests/test-qapi-types-sub-sub-module.h \
|
2018-02-26 22:48:58 +03:00
|
|
|
tests/test-qapi-visit.c tests/test-qapi-visit.h \
|
2019-03-01 18:40:48 +03:00
|
|
|
tests/include/test-qapi-visit-sub-module.c \
|
|
|
|
tests/include/test-qapi-visit-sub-module.h \
|
|
|
|
tests/test-qapi-visit-sub-sub-module.c \
|
|
|
|
tests/test-qapi-visit-sub-sub-module.h \
|
2018-02-11 12:36:05 +03:00
|
|
|
tests/test-qapi-commands.h tests/test-qapi-commands.c \
|
2019-03-01 18:40:48 +03:00
|
|
|
tests/include/test-qapi-commands-sub-module.h \
|
|
|
|
tests/include/test-qapi-commands-sub-module.c \
|
|
|
|
tests/test-qapi-commands-sub-sub-module.h \
|
|
|
|
tests/test-qapi-commands-sub-sub-module.c \
|
2019-11-20 21:25:47 +03:00
|
|
|
tests/test-qapi-emit-events.c tests/test-qapi-emit-events.h \
|
2018-02-11 12:36:05 +03:00
|
|
|
tests/test-qapi-events.c tests/test-qapi-events.h \
|
2019-11-20 21:25:48 +03:00
|
|
|
tests/test-qapi-init-commands.c \
|
|
|
|
tests/test-qapi-init-commands.h \
|
2019-03-01 18:40:48 +03:00
|
|
|
tests/include/test-qapi-events-sub-module.c \
|
|
|
|
tests/include/test-qapi-events-sub-module.h \
|
|
|
|
tests/test-qapi-events-sub-sub-module.c \
|
|
|
|
tests/test-qapi-events-sub-sub-module.h \
|
2018-02-11 12:36:05 +03:00
|
|
|
tests/test-qapi-introspect.c tests/test-qapi-introspect.h: \
|
2018-02-26 22:48:58 +03:00
|
|
|
tests/test-qapi-gen-timestamp ;
|
2019-03-01 18:40:48 +03:00
|
|
|
tests/test-qapi-gen-timestamp: \
|
|
|
|
$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json \
|
|
|
|
$(SRC_PATH)/tests/qapi-schema/include/sub-module.json \
|
|
|
|
$(SRC_PATH)/tests/qapi-schema/sub-sub-module.json \
|
|
|
|
$(qapi-py)
|
2018-06-18 20:59:58 +03:00
|
|
|
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
|
2018-02-26 22:48:58 +03:00
|
|
|
-o tests -p "test-" $<, \
|
|
|
|
"GEN","$(@:%-timestamp=%)")
|
2019-10-18 10:43:39 +03:00
|
|
|
@rm -f tests/test-qapi-doc.texi
|
2018-02-26 22:48:58 +03:00
|
|
|
@>$@
|
|
|
|
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o $(test-qapi-obj-y)
|
|
|
|
tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o $(test-qapi-obj-y)
|
2019-11-20 21:25:47 +03:00
|
|
|
tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) tests/test-qapi-emit-events.o tests/test-qapi-events.o
|
2016-09-30 17:45:27 +03:00
|
|
|
tests/test-qobject-output-visitor$(EXESUF): tests/test-qobject-output-visitor.o $(test-qapi-obj-y)
|
qapi: Add new clone visitor
We have a couple places in the code base that want to deep-clone
one QAPI object into another, and they were resorting to serializing
the struct out to QObject then reparsing it. A much more efficient
version can be done by adding a new clone visitor.
Since cloning is still relatively uncommon, expose the use of the
new visitor via a QAPI_CLONE() macro that takes care of type-punning
the underlying function pointer, rather than generating lots of
unused functions for types that won't be cloned. And yes, we're
relying on the compiler treating all pointers equally, even though
a strict C program cannot portably do so - but we're not the first
one in the qemu code base to expect it to work (hello, glib!).
The choice of adding a fourth visitor type deserves some explanation.
On the surface, the clone visitor is mostly an input visitor (it
takes arbitrary input - in this case, another QAPI object - and
creates a new QAPI object during the course of the visit). But
ever since commit da72ab0 consolidated enum visits based on the
visitor type, using VISITOR_INPUT would cause us to run
visit_type_str(), even though for cloning there is nothing to do
(we just copy the enum value across, without regards to its mapping
to strings). Also, since our input happens to be a QAPI object,
we can also satisfy the internal checks for VISITOR_OUTPUT. So in
the end, I settled with a new VISITOR_CLONE, and chose its value
such that many internal checks can use 'v->type & mask', sticking
to 'v->type == value' where the difference matters.
Note that we can only clone objects (including alternates) and lists,
not built-ins or enums. The visitor core hides integer width from
the actual visitor (since commit 04e070d), and as long as that's the
case, we can't clone top-level integers. Then again, those can
always be cloned by direct copy, since they are not objects with
deep pointers, so it's no real loss. And restricting cloning to
just objects and lists is cleaner than restricting it to non-integers.
As such, I documented that the clone visitor is for direct use only
by code internal to QAPI, and should not be used on incomplete objects
(other than a hack to work around the fact that we allow NULL in place
of "" in visit_type_str() in other output visitors). Note that as
written, the clone visitor will never fail on a complete object.
Scalars (including enums) not at the root of the clone copy just fine
with no additional effort while visiting the scalar, by virtue of a
g_memdup() each time we push another struct onto the stack. Cloning
a string requires deduplication of a pointer, which means it can also
provide the guarantee of an input visitor of never producing NULL
even when still accepting NULL in place of "" the way the QMP output
visitor does.
Cloning an 'any' type could be possible by incrementing the QObject
refcnt, but it's not obvious whether that is better than implementing
a QObject deep clone. So for now, we document it as unsupported,
and intentionally omit the .type_any() callback to let a developer
know their usage needs implementation.
Add testsuite coverage for several different clone situations, to
ensure that the code is working. I also tested that valgrind was
happy with the test.
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1465490926-28625-14-git-send-email-eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-06-09 19:48:44 +03:00
|
|
|
tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y)
|
2016-09-30 17:45:27 +03:00
|
|
|
tests/test-qobject-input-visitor$(EXESUF): tests/test-qobject-input-visitor.o $(test-qapi-obj-y)
|
2019-11-20 21:25:48 +03:00
|
|
|
tests/test-qmp-cmds$(EXESUF): tests/test-qmp-cmds.o tests/test-qapi-commands.o tests/test-qapi-init-commands.o $(test-qapi-obj-y)
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y)
|
|
|
|
tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y)
|
2012-02-09 14:21:03 +04:00
|
|
|
|
2017-01-10 05:10:09 +03:00
|
|
|
tests/test-shift128$(EXESUF): tests/test-shift128.o $(test-util-obj-y)
|
2015-09-02 13:35:52 +03:00
|
|
|
tests/test-mul64$(EXESUF): tests/test-mul64.o $(test-util-obj-y)
|
|
|
|
tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y)
|
2016-12-09 17:36:00 +03:00
|
|
|
tests/test-bitcnt$(EXESUF): tests/test-bitcnt.o $(test-util-obj-y)
|
2020-08-04 21:00:40 +03:00
|
|
|
tests/test-qgraph$(EXESUF): tests/test-qgraph.o tests/qtest/libqos/qgraph.o $(test-util-obj-y)
|
2015-09-02 12:57:27 +03:00
|
|
|
tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(test-crypto-obj-y)
|
2017-07-14 21:04:10 +03:00
|
|
|
tests/benchmark-crypto-hash$(EXESUF): tests/benchmark-crypto-hash.o $(test-crypto-obj-y)
|
2016-12-13 13:43:00 +03:00
|
|
|
tests/test-crypto-hmac$(EXESUF): tests/test-crypto-hmac.o $(test-crypto-obj-y)
|
2017-07-14 21:04:11 +03:00
|
|
|
tests/benchmark-crypto-hmac$(EXESUF): tests/benchmark-crypto-hmac.o $(test-crypto-obj-y)
|
2015-09-02 12:57:27 +03:00
|
|
|
tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(test-crypto-obj-y)
|
2017-07-14 21:04:09 +03:00
|
|
|
tests/benchmark-crypto-cipher$(EXESUF): tests/benchmark-crypto-cipher.o $(test-crypto-obj-y)
|
crypto: add QCryptoSecret object class for password/key handling
Introduce a new QCryptoSecret object class which will be used
for providing passwords and keys to other objects which need
sensitive credentials.
The new object can provide secret values directly as properties,
or indirectly via a file. The latter includes support for file
descriptor passing syntax on UNIX platforms. Ordinarily passing
secret values directly as properties is insecure, since they
are visible in process listings, or in log files showing the
CLI args / QMP commands. It is possible to use AES-256-CBC to
encrypt the secret values though, in which case all that is
visible is the ciphertext. For ad hoc developer testing though,
it is fine to provide the secrets directly without encryption
so this is not explicitly forbidden.
The anticipated scenario is that libvirtd will create a random
master key per QEMU instance (eg /var/run/libvirt/qemu/$VMNAME.key)
and will use that key to encrypt all passwords it provides to
QEMU via '-object secret,....'. This avoids the need for libvirt
(or other mgmt apps) to worry about file descriptor passing.
It also makes life easier for people who are scripting the
management of QEMU, for whom FD passing is significantly more
complex.
Providing data inline (insecure, only for ad hoc dev testing)
$QEMU -object secret,id=sec0,data=letmein
Providing data indirectly in raw format
printf "letmein" > mypasswd.txt
$QEMU -object secret,id=sec0,file=mypasswd.txt
Providing data indirectly in base64 format
$QEMU -object secret,id=sec0,file=mykey.b64,format=base64
Providing data with encryption
$QEMU -object secret,id=master0,file=mykey.b64,format=base64 \
-object secret,id=sec0,data=[base64 ciphertext],\
keyid=master0,iv=[base64 IV],format=base64
Note that 'format' here refers to the format of the ciphertext
data. The decrypted data must always be in raw byte format.
More examples are shown in the updated docs.
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2015-10-14 11:58:38 +03:00
|
|
|
tests/test-crypto-secret$(EXESUF): tests/test-crypto-secret.o $(test-crypto-obj-y)
|
2016-02-11 17:00:17 +03:00
|
|
|
tests/test-crypto-xts$(EXESUF): tests/test-crypto-xts.o $(test-crypto-obj-y)
|
2015-09-21 19:25:34 +03:00
|
|
|
|
2020-05-25 14:19:13 +03:00
|
|
|
ifeq ($(CONFIG_TEST_SECRET_KEYRING),y)
|
|
|
|
tests/test-crypto-secret.o-libs := -lkeyutils
|
|
|
|
endif
|
|
|
|
|
2015-09-21 19:25:34 +03:00
|
|
|
tests/crypto-tls-x509-helpers.o-cflags := $(TASN1_CFLAGS)
|
|
|
|
tests/crypto-tls-x509-helpers.o-libs := $(TASN1_LIBS)
|
|
|
|
tests/pkix_asn1_tab.o-cflags := $(TASN1_CFLAGS)
|
|
|
|
|
|
|
|
tests/test-crypto-tlscredsx509.o-cflags := $(TASN1_CFLAGS)
|
2015-04-13 16:01:39 +03:00
|
|
|
tests/test-crypto-tlscredsx509$(EXESUF): tests/test-crypto-tlscredsx509.o \
|
|
|
|
tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o $(test-crypto-obj-y)
|
2015-09-21 19:25:34 +03:00
|
|
|
|
|
|
|
tests/test-crypto-tlssession.o-cflags := $(TASN1_CFLAGS)
|
2015-03-02 20:23:31 +03:00
|
|
|
tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \
|
crypto: Implement TLS Pre-Shared Keys (PSK).
Pre-Shared Keys (PSK) is a simpler mechanism for enabling TLS
connections than using certificates. It requires only a simple secret
key:
$ mkdir -m 0700 /tmp/keys
$ psktool -u rjones -p /tmp/keys/keys.psk
$ cat /tmp/keys/keys.psk
rjones:d543770c15ad93d76443fb56f501a31969235f47e999720ae8d2336f6a13fcbc
The key can be secretly shared between clients and servers. Clients
must specify the directory containing the "keys.psk" file and a
username (defaults to "qemu"). Servers must specify only the
directory.
Example NBD client:
$ qemu-img info \
--object tls-creds-psk,id=tls0,dir=/tmp/keys,username=rjones,endpoint=client \
--image-opts \
file.driver=nbd,file.host=localhost,file.port=10809,file.tls-creds=tls0,file.export=/
Example NBD server using qemu-nbd:
$ qemu-nbd -t -x / \
--object tls-creds-psk,id=tls0,endpoint=server,dir=/tmp/keys \
--tls-creds tls0 \
image.qcow2
Example NBD server using nbdkit:
$ nbdkit -n -e / -fv \
--tls=on --tls-psk=/tmp/keys/keys.psk \
file file=disk.img
Signed-off-by: Richard W.M. Jones <rjones@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2018-07-03 11:03:03 +03:00
|
|
|
tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \
|
|
|
|
tests/crypto-tls-psk-helpers.o \
|
|
|
|
$(test-crypto-obj-y)
|
2018-06-08 19:24:57 +03:00
|
|
|
tests/test-util-filemonitor$(EXESUF): tests/test-util-filemonitor.o \
|
|
|
|
$(test-util-obj-y)
|
2017-12-21 15:55:20 +03:00
|
|
|
tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \
|
|
|
|
tests/socket-helpers.o $(test-util-obj-y)
|
2018-05-02 17:40:33 +03:00
|
|
|
tests/test-authz-simple$(EXESUF): tests/test-authz-simple.o $(test-authz-obj-y)
|
authz: add QAuthZList object type for an access control list
Add a QAuthZList object type that implements the QAuthZ interface. This
built-in implementation maintains a trivial access control list with a
sequence of match rules and a final default policy. This replicates the
functionality currently provided by the qemu_acl module.
To create an instance of this object via the QMP monitor, the syntax
used would be:
{
"execute": "object-add",
"arguments": {
"qom-type": "authz-list",
"id": "authz0",
"props": {
"rules": [
{ "match": "fred", "policy": "allow", "format": "exact" },
{ "match": "bob", "policy": "allow", "format": "exact" },
{ "match": "danb", "policy": "deny", "format": "glob" },
{ "match": "dan*", "policy": "allow", "format": "exact" },
],
"policy": "deny"
}
}
}
This sets up an authorization rule that allows 'fred', 'bob' and anyone
whose name starts with 'dan', except for 'danb'. Everyone unmatched is
denied.
It is not currently possible to create this via -object, since there is
no syntax supported to specify non-scalar properties for objects. This
is likely to be addressed by later support for using JSON with -object,
or an equivalent approach.
In any case the future "authz-listfile" object can be used from the
CLI and is likely a better choice, as it allows the ACL to be refreshed
automatically on change.
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2015-10-21 16:54:59 +03:00
|
|
|
tests/test-authz-list$(EXESUF): tests/test-authz-list.o $(test-authz-obj-y)
|
authz: add QAuthZListFile object type for a file access control list
Add a QAuthZListFile object type that implements the QAuthZ interface. This
built-in implementation is a proxy around the QAuthZList object type,
initializing it from an external file, and optionally, automatically
reloading it whenever it changes.
To create an instance of this object via the QMP monitor, the syntax
used would be:
{
"execute": "object-add",
"arguments": {
"qom-type": "authz-list-file",
"id": "authz0",
"props": {
"filename": "/etc/qemu/vnc.acl",
"refresh": true
}
}
}
If "refresh" is "yes", inotify is used to monitor the file,
automatically reloading changes. If an error occurs during reloading,
all authorizations will fail until the file is next successfully
loaded.
The /etc/qemu/vnc.acl file would contain a JSON representation of a
QAuthZList object
{
"rules": [
{ "match": "fred", "policy": "allow", "format": "exact" },
{ "match": "bob", "policy": "allow", "format": "exact" },
{ "match": "danb", "policy": "deny", "format": "glob" },
{ "match": "dan*", "policy": "allow", "format": "exact" },
],
"policy": "deny"
}
This sets up an authorization rule that allows 'fred', 'bob' and anyone
whose name starts with 'dan', except for 'danb'. Everyone unmatched is
denied.
The object can be loaded on the comand line using
-object authz-list-file,id=authz0,filename=/etc/qemu/vnc.acl,refresh=yes
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2018-05-11 14:19:59 +03:00
|
|
|
tests/test-authz-listfile$(EXESUF): tests/test-authz-listfile.o $(test-authz-obj-y)
|
authz: add QAuthZPAM object type for authorizing using PAM
Add an authorization backend that talks to PAM to check whether the user
identity is allowed. This only uses the PAM account validation facility,
which is essentially just a check to see if the provided username is permitted
access. It doesn't use the authentication or session parts of PAM, since
that's dealt with by the relevant part of QEMU (eg VNC server).
Consider starting QEMU with a VNC server and telling it to use TLS with
x509 client certificates and configuring it to use an PAM to validate
the x509 distinguished name. In this example we're telling it to use PAM
for the QAuthZ impl with a service name of "qemu-vnc"
$ qemu-system-x86_64 \
-object tls-creds-x509,id=tls0,dir=/home/berrange/security/qemutls,\
endpoint=server,verify-peer=yes \
-object authz-pam,id=authz0,service=qemu-vnc \
-vnc :1,tls-creds=tls0,tls-authz=authz0
This requires an /etc/pam/qemu-vnc file to be created with the auth
rules. A very simple file based whitelist can be setup using
$ cat > /etc/pam/qemu-vnc <<EOF
account requisite pam_listfile.so item=user sense=allow file=/etc/qemu/vnc.allow
EOF
The /etc/qemu/vnc.allow file simply contains one username per line. Any
username not in the file is denied. The usernames in this example are
the x509 distinguished name from the client's x509 cert.
$ cat > /etc/qemu/vnc.allow <<EOF
CN=laptop.berrange.com,O=Berrange Home,L=London,ST=London,C=GB
EOF
More interesting would be to configure PAM to use an LDAP backend, so
that the QEMU authorization check data can be centralized instead of
requiring each compute host to have file maintained.
The main limitation with this PAM module is that the rules apply to all
QEMU instances on the host. Setting up different rules per VM, would
require creating a separate PAM service name & config file for every
guest. An alternative approach for the future might be to not pass in
the plain username to PAM, but instead combine the VM name or UUID with
the username. This requires further consideration though.
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
2016-07-27 16:13:56 +03:00
|
|
|
tests/test-authz-pam$(EXESUF): tests/test-authz-pam.o $(test-authz-obj-y)
|
2015-03-18 20:25:45 +03:00
|
|
|
tests/test-io-task$(EXESUF): tests/test-io-task.o $(test-io-obj-y)
|
2015-02-27 19:19:33 +03:00
|
|
|
tests/test-io-channel-socket$(EXESUF): tests/test-io-channel-socket.o \
|
2017-12-22 14:45:24 +03:00
|
|
|
tests/io-channel-helpers.o tests/socket-helpers.o $(test-io-obj-y)
|
2015-02-27 21:25:25 +03:00
|
|
|
tests/test-io-channel-file$(EXESUF): tests/test-io-channel-file.o \
|
|
|
|
tests/io-channel-helpers.o $(test-io-obj-y)
|
2015-03-02 21:13:13 +03:00
|
|
|
tests/test-io-channel-tls$(EXESUF): tests/test-io-channel-tls.o \
|
|
|
|
tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \
|
|
|
|
tests/io-channel-helpers.o $(test-io-obj-y)
|
2015-08-27 18:25:30 +03:00
|
|
|
tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \
|
|
|
|
tests/io-channel-helpers.o $(test-io-obj-y)
|
2015-09-15 19:27:33 +03:00
|
|
|
tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \
|
|
|
|
tests/io-channel-helpers.o $(test-io-obj-y)
|
2015-10-14 15:14:04 +03:00
|
|
|
tests/test-crypto-pbkdf$(EXESUF): tests/test-crypto-pbkdf.o $(test-crypto-obj-y)
|
2015-10-15 14:35:28 +03:00
|
|
|
tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y)
|
2015-10-23 18:14:25 +03:00
|
|
|
tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y)
|
2015-10-24 13:44:13 +03:00
|
|
|
tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y)
|
2013-02-17 00:47:01 +04:00
|
|
|
|
tests: introduce a framework for testing migration performance
This introduces a moderately general purpose framework for
testing performance of migration.
The initial guest workload is provided by the included 'stress'
program, which is configured to spawn one thread per guest CPU
and run a maximally memory intensive workload. It will loop
over GB of memory, xor'ing each byte with data from a 4k array
of random bytes. This ensures heavy read and write load across
all of guest memory to stress the migration performance. While
running the 'stress' program will record how long it takes to
xor each GB of memory and print this data for later reporting.
The test engine will spawn a pair of QEMU processes, either on
the same host, or with the target on a remote host via ssh,
using the host kernel and a custom initrd built with 'stress'
as the /init binary. Kernel command line args are set to ensure
a fast kernel boot time (< 1 second) between launching QEMU and
the stress program starting execution.
None the less, the test engine will initially wait N seconds for
the guest workload to stablize, before starting the migration
operation. When migration is running, the engine will use pause,
post-copy, autoconverge, xbzrle compression and multithread
compression features, as well as downtime & bandwidth tuning
to encourage completion. If migration completes, the test engine
will wait N seconds again for the guest workooad to stablize on
the target host. If migration does not complete after a preset
number of iterations, it will be aborted.
While the QEMU process is running on the source host, the test
engine will sample the host CPU usage of QEMU as a whole, and
each vCPU thread. While migration is running, it will record
all the stats reported by 'query-migration'. Finally, it will
capture the output of the stress program running in the guest.
All the data produced from a single test execution is recorded
in a structured JSON file. A separate program is then able to
create interactive charts using the "plotly" python + javascript
libraries, showing the characteristics of the migration.
The data output provides visualization of the effect on guest
vCPU workloads from the migration process, the corresponding
vCPU utilization on the host, and the overall CPU hit from
QEMU on the host. This is correlated from statistics from the
migration process, such as downtime, vCPU throttling and iteration
number.
While the tests can be run individually with arbitrary parameters,
there is also a facility for producing batch reports for a number
of pre-defined scenarios / comparisons, in order to be able to
get standardized results across different hardware configurations
(eg TCP vs RDMA, or comparing different VCPU counts / memory
sizes, etc).
To use this, first you must build the initrd image
$ make tests/migration/initrd-stress.img
To run a a one-shot test with all default parameters
$ ./tests/migration/guestperf.py > result.json
This has many command line args for varying its behaviour.
For example, to increase the RAM size and CPU count and
bind it to specific host NUMA nodes
$ ./tests/migration/guestperf.py \
--mem 4 --cpus 2 \
--src-mem-bind 0 --src-cpu-bind 0,1 \
--dst-mem-bind 1 --dst-cpu-bind 2,3 \
> result.json
Using mem + cpu binding is strongly recommended on NUMA
machines, otherwise the guest performance results will
vary wildly between runs of the test due to lucky/unlucky
NUMA placement, making sensible data analysis impossible.
To make it run across separate hosts:
$ ./tests/migration/guestperf.py \
--dst-host somehostname > result.json
To request that post-copy is enabled, with switchover
after 5 iterations
$ ./tests/migration/guestperf.py \
--post-copy --post-copy-iters 5 > result.json
Once a result.json file is created, a graph of the data
can be generated, showing guest workload performance per
thread and the migration iteration points:
$ ./tests/migration/guestperf-plot.py --output result.html \
--migration-iters --split-guest-cpu result.json
To further include host vCPU utilization and overall QEMU
utilization
$ ./tests/migration/guestperf-plot.py --output result.html \
--migration-iters --split-guest-cpu \
--qemu-cpu --vcpu-cpu result.json
NB, the 'guestperf-plot.py' command requires that you have
the plotly python library installed. eg you must do
$ pip install --user plotly
Viewing the result.html file requires that you have the
plotly.min.js file in the same directory as the HTML
output. This js file is installed as part of the plotly
python library, so can be found in
$HOME/.local/lib/python2.7/site-packages/plotly/offline/plotly.min.js
The guestperf-plot.py program can accept multiple json files
to plot, enabling results from different configurations to
be compared.
Finally, to run the entire standardized set of comparisons
$ ./tests/migration/guestperf-batch.py \
--dst-host somehost \
--mem 4 --cpus 2 \
--src-mem-bind 0 --src-cpu-bind 0,1 \
--dst-mem-bind 1 --dst-cpu-bind 2,3
--output tcp-somehost-4gb-2cpu
will store JSON files from all scenarios in the directory
named tcp-somehost-4gb-2cpu
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-Id: <1469020993-29426-7-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-07-20 16:23:13 +03:00
|
|
|
tests/migration/stress$(EXESUF): tests/migration/stress.o
|
2016-10-04 19:27:21 +03:00
|
|
|
$(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@")
|
tests: introduce a framework for testing migration performance
This introduces a moderately general purpose framework for
testing performance of migration.
The initial guest workload is provided by the included 'stress'
program, which is configured to spawn one thread per guest CPU
and run a maximally memory intensive workload. It will loop
over GB of memory, xor'ing each byte with data from a 4k array
of random bytes. This ensures heavy read and write load across
all of guest memory to stress the migration performance. While
running the 'stress' program will record how long it takes to
xor each GB of memory and print this data for later reporting.
The test engine will spawn a pair of QEMU processes, either on
the same host, or with the target on a remote host via ssh,
using the host kernel and a custom initrd built with 'stress'
as the /init binary. Kernel command line args are set to ensure
a fast kernel boot time (< 1 second) between launching QEMU and
the stress program starting execution.
None the less, the test engine will initially wait N seconds for
the guest workload to stablize, before starting the migration
operation. When migration is running, the engine will use pause,
post-copy, autoconverge, xbzrle compression and multithread
compression features, as well as downtime & bandwidth tuning
to encourage completion. If migration completes, the test engine
will wait N seconds again for the guest workooad to stablize on
the target host. If migration does not complete after a preset
number of iterations, it will be aborted.
While the QEMU process is running on the source host, the test
engine will sample the host CPU usage of QEMU as a whole, and
each vCPU thread. While migration is running, it will record
all the stats reported by 'query-migration'. Finally, it will
capture the output of the stress program running in the guest.
All the data produced from a single test execution is recorded
in a structured JSON file. A separate program is then able to
create interactive charts using the "plotly" python + javascript
libraries, showing the characteristics of the migration.
The data output provides visualization of the effect on guest
vCPU workloads from the migration process, the corresponding
vCPU utilization on the host, and the overall CPU hit from
QEMU on the host. This is correlated from statistics from the
migration process, such as downtime, vCPU throttling and iteration
number.
While the tests can be run individually with arbitrary parameters,
there is also a facility for producing batch reports for a number
of pre-defined scenarios / comparisons, in order to be able to
get standardized results across different hardware configurations
(eg TCP vs RDMA, or comparing different VCPU counts / memory
sizes, etc).
To use this, first you must build the initrd image
$ make tests/migration/initrd-stress.img
To run a a one-shot test with all default parameters
$ ./tests/migration/guestperf.py > result.json
This has many command line args for varying its behaviour.
For example, to increase the RAM size and CPU count and
bind it to specific host NUMA nodes
$ ./tests/migration/guestperf.py \
--mem 4 --cpus 2 \
--src-mem-bind 0 --src-cpu-bind 0,1 \
--dst-mem-bind 1 --dst-cpu-bind 2,3 \
> result.json
Using mem + cpu binding is strongly recommended on NUMA
machines, otherwise the guest performance results will
vary wildly between runs of the test due to lucky/unlucky
NUMA placement, making sensible data analysis impossible.
To make it run across separate hosts:
$ ./tests/migration/guestperf.py \
--dst-host somehostname > result.json
To request that post-copy is enabled, with switchover
after 5 iterations
$ ./tests/migration/guestperf.py \
--post-copy --post-copy-iters 5 > result.json
Once a result.json file is created, a graph of the data
can be generated, showing guest workload performance per
thread and the migration iteration points:
$ ./tests/migration/guestperf-plot.py --output result.html \
--migration-iters --split-guest-cpu result.json
To further include host vCPU utilization and overall QEMU
utilization
$ ./tests/migration/guestperf-plot.py --output result.html \
--migration-iters --split-guest-cpu \
--qemu-cpu --vcpu-cpu result.json
NB, the 'guestperf-plot.py' command requires that you have
the plotly python library installed. eg you must do
$ pip install --user plotly
Viewing the result.html file requires that you have the
plotly.min.js file in the same directory as the HTML
output. This js file is installed as part of the plotly
python library, so can be found in
$HOME/.local/lib/python2.7/site-packages/plotly/offline/plotly.min.js
The guestperf-plot.py program can accept multiple json files
to plot, enabling results from different configurations to
be compared.
Finally, to run the entire standardized set of comparisons
$ ./tests/migration/guestperf-batch.py \
--dst-host somehost \
--mem 4 --cpus 2 \
--src-mem-bind 0 --src-cpu-bind 0,1 \
--dst-mem-bind 1 --dst-cpu-bind 2,3
--output tcp-somehost-4gb-2cpu
will store JSON files from all scenarios in the directory
named tcp-somehost-4gb-2cpu
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Message-Id: <1469020993-29426-7-git-send-email-berrange@redhat.com>
Signed-off-by: Amit Shah <amit.shah@redhat.com>
2016-07-20 16:23:13 +03:00
|
|
|
|
|
|
|
INITRD_WORK_DIR=tests/migration/initrd
|
|
|
|
|
|
|
|
tests/migration/initrd-stress.img: tests/migration/stress$(EXESUF)
|
|
|
|
mkdir -p $(INITRD_WORK_DIR)
|
|
|
|
cp $< $(INITRD_WORK_DIR)/init
|
|
|
|
(cd $(INITRD_WORK_DIR) && (find | cpio --quiet -o -H newc | gzip -9)) > $@
|
|
|
|
rm $(INITRD_WORK_DIR)/init
|
|
|
|
rmdir $(INITRD_WORK_DIR)
|
|
|
|
|
2019-07-18 14:19:02 +03:00
|
|
|
tests/test-qga$(EXESUF): qga/qemu-ga$(EXESUF)
|
2020-08-04 21:00:40 +03:00
|
|
|
tests/test-qga$(EXESUF): tests/test-qga.o tests/qtest/libqtest.o $(test-util-obj-y)
|
2020-01-17 15:26:48 +03:00
|
|
|
tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a
|
2020-03-06 19:57:51 +03:00
|
|
|
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
|
2015-10-02 15:58:18 +03:00
|
|
|
|
2012-03-28 17:42:01 +04:00
|
|
|
SPEED = quick
|
2012-01-10 23:10:43 +04:00
|
|
|
|
2012-03-28 17:42:01 +04:00
|
|
|
# gtester tests, possibly with verbose output
|
2018-11-29 20:45:31 +03:00
|
|
|
# do_test_tap runs all tests, even if some of them fail, while do_test_human
|
|
|
|
# stops at the first failure unless -k is given on the command line
|
|
|
|
|
|
|
|
define do_test_human_k
|
|
|
|
$(quiet-@)rc=0; $(foreach COMMAND, $1, \
|
|
|
|
$(call quiet-command-run, \
|
|
|
|
export MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} $2; \
|
|
|
|
$(COMMAND) -m=$(SPEED) -k --tap < /dev/null \
|
|
|
|
| ./scripts/tap-driver.pl --test-name="$(notdir $(COMMAND))" $(if $(V),, --show-failures-only) \
|
|
|
|
|| rc=$$?;, "TEST", "$@: $(COMMAND)")) exit $$rc
|
|
|
|
endef
|
|
|
|
define do_test_human_no_k
|
|
|
|
$(foreach COMMAND, $1, \
|
|
|
|
$(call quiet-command, \
|
|
|
|
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} $2 \
|
|
|
|
$(COMMAND) -m=$(SPEED) -k --tap < /dev/null \
|
|
|
|
| ./scripts/tap-driver.pl --test-name="$(notdir $(COMMAND))" $(if $(V),, --show-failures-only), \
|
|
|
|
"TEST", "$@: $(COMMAND)")
|
|
|
|
)
|
|
|
|
endef
|
|
|
|
do_test_human = \
|
|
|
|
$(if $(findstring k, $(MAKEFLAGS)), $(do_test_human_k), $(do_test_human_no_k))
|
|
|
|
|
|
|
|
define do_test_tap
|
|
|
|
$(call quiet-command, \
|
|
|
|
{ export MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} $2; \
|
|
|
|
$(foreach COMMAND, $1, \
|
|
|
|
$(COMMAND) -m=$(SPEED) -k --tap < /dev/null \
|
2020-06-29 00:30:46 +03:00
|
|
|
| sed "s/^\(not \)\?ok [0-9]* /&$(notdir $(COMMAND)) /" || true; ) } \
|
2018-11-29 20:45:31 +03:00
|
|
|
| ./scripts/tap-merge.pl | tee "$@" \
|
|
|
|
| ./scripts/tap-driver.pl $(if $(V),, --show-failures-only), \
|
|
|
|
"TAP","$@")
|
|
|
|
endef
|
2012-01-10 23:10:43 +04:00
|
|
|
|
2020-07-01 16:56:51 +03:00
|
|
|
build-unit: $(check-unit-y)
|
|
|
|
|
2018-11-29 20:45:31 +03:00
|
|
|
check-unit: $(check-unit-y)
|
|
|
|
$(call do_test_human, $^)
|
2012-01-10 23:10:43 +04:00
|
|
|
|
2018-11-29 20:45:31 +03:00
|
|
|
check-speed: $(check-speed-y)
|
|
|
|
$(call do_test_human, $^)
|
2012-03-08 15:29:00 +04:00
|
|
|
|
2018-11-29 20:45:31 +03:00
|
|
|
# gtester tests with TAP output
|
2012-03-28 17:42:05 +04:00
|
|
|
|
2018-11-29 20:45:31 +03:00
|
|
|
check-report-unit.tap: $(check-unit-y)
|
|
|
|
$(call do_test_tap,$^)
|
2012-03-09 16:37:40 +04:00
|
|
|
|
2018-04-07 00:08:36 +03:00
|
|
|
# Per guest TCG tests
|
|
|
|
|
2018-11-30 01:21:50 +03:00
|
|
|
BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(TARGET_DIRS))
|
|
|
|
CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(TARGET_DIRS))
|
|
|
|
RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(TARGET_DIRS))
|
2018-04-07 00:08:36 +03:00
|
|
|
|
2018-06-08 14:12:46 +03:00
|
|
|
# Probe for the Docker Builds needed for each build
|
2018-07-04 09:30:11 +03:00
|
|
|
$(foreach PROBE_TARGET,$(TARGET_DIRS), \
|
2019-08-07 17:35:22 +03:00
|
|
|
$(eval -include $(SRC_PATH)/tests/tcg/Makefile.prereqs))
|
2018-06-08 14:12:46 +03:00
|
|
|
|
2019-05-17 19:09:48 +03:00
|
|
|
build-tcg-tests-%: $(if $(CONFIG_PLUGIN),plugins)
|
2019-08-07 17:35:22 +03:00
|
|
|
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) \
|
|
|
|
-f $(SRC_PATH)/tests/tcg/Makefile.qemu \
|
|
|
|
SRC_PATH=$(SRC_PATH) \
|
2019-08-07 17:35:23 +03:00
|
|
|
V="$(V)" TARGET="$*" guest-tests, \
|
2018-06-15 21:20:55 +03:00
|
|
|
"BUILD", "TCG tests for $*")
|
2018-04-07 00:08:36 +03:00
|
|
|
|
2020-08-03 18:04:25 +03:00
|
|
|
run-tcg-tests-%: build-tcg-tests-% all
|
2019-08-07 17:35:22 +03:00
|
|
|
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) \
|
|
|
|
-f $(SRC_PATH)/tests/tcg/Makefile.qemu \
|
|
|
|
SRC_PATH=$(SRC_PATH) SPEED="$(SPEED)" \
|
2019-08-07 17:35:23 +03:00
|
|
|
V="$(V)" TARGET="$*" run-guest-tests, \
|
2018-06-15 21:20:55 +03:00
|
|
|
"RUN", "TCG tests for $*")
|
2018-04-07 00:08:36 +03:00
|
|
|
|
|
|
|
clean-tcg-tests-%:
|
2019-08-07 17:35:22 +03:00
|
|
|
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) \
|
|
|
|
-f $(SRC_PATH)/tests/tcg/Makefile.qemu \
|
2019-08-07 17:35:23 +03:00
|
|
|
SRC_PATH=$(SRC_PATH) TARGET="$*" clean-guest-tests, \
|
2019-09-10 15:09:31 +03:00
|
|
|
"CLEAN", "TCG tests for $*")
|
2018-04-07 00:08:36 +03:00
|
|
|
|
|
|
|
.PHONY: build-tcg
|
|
|
|
build-tcg: $(BUILD_TCG_TARGET_RULES)
|
|
|
|
|
|
|
|
.PHONY: check-tcg
|
2019-02-28 13:15:29 +03:00
|
|
|
check-tcg: $(RUN_TCG_TARGET_RULES)
|
2018-04-07 00:08:36 +03:00
|
|
|
|
|
|
|
.PHONY: clean-tcg
|
|
|
|
clean-tcg: $(CLEAN_TCG_TARGET_RULES)
|
2012-03-28 17:42:01 +04:00
|
|
|
|
|
|
|
|
2018-10-18 18:31:32 +03:00
|
|
|
# Python venv for running tests
|
|
|
|
|
2018-10-18 18:31:33 +03:00
|
|
|
.PHONY: check-venv check-acceptance
|
2018-10-18 18:31:32 +03:00
|
|
|
|
|
|
|
TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv
|
|
|
|
TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt
|
2018-10-18 18:31:33 +03:00
|
|
|
TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results
|
|
|
|
# Controls the output generated by Avocado when running tests.
|
|
|
|
# Any number of command separated loggers are accepted. For more
|
|
|
|
# information please refer to "avocado --help".
|
2019-03-12 20:18:05 +03:00
|
|
|
AVOCADO_SHOW=app
|
tests/acceptance: use "arch:" tag to filter target specific tests
Currently, some tests contains target architecture information, in the
form of a "x86_64" tag. But that tag is not respected in the default
execution, that is, "make check-acceptance" doesn't do anything with
it.
That said, even the target architecture handling currently present in
the "avocado_qemu.Test" class is pretty limited. For instance, by
default, it chooses a target based on the host architecture.
Because the original implementation of the tags feature in Avocado did
not include any time of namespace or "key:val" mechanism, no tag has
relation to another tag. The new implementation of the tags feature
from version 67.0 onwards, allows "key:val" tags, and because of that,
a test can be classified with a tag in a given key. For instance, the
new proposed version of the "boot_linux_console.py" test, which
downloads and attempts to run a x86_64 kernel, is now tagged as:
:avocado: tags=arch:x86_64
This means that it can be filtered (out) when no x86_64 target is
available. At the same time, tests that don't have a "arch:" tag,
will not be filtered out.
Signed-off-by: Cleber Rosa <crosa@redhat.com>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Message-Id: <20190312171824.5134-6-crosa@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-03-12 20:18:09 +03:00
|
|
|
AVOCADO_TAGS=$(patsubst %-softmmu,-t arch:%, $(filter %-softmmu,$(TARGET_DIRS)))
|
2018-10-18 18:31:32 +03:00
|
|
|
|
|
|
|
$(TESTS_VENV_DIR): $(TESTS_VENV_REQ)
|
|
|
|
$(call quiet-command, \
|
|
|
|
$(PYTHON) -m venv --system-site-packages $@, \
|
|
|
|
VENV, $@)
|
|
|
|
$(call quiet-command, \
|
|
|
|
$(TESTS_VENV_DIR)/bin/python -m pip -q install -r $(TESTS_VENV_REQ), \
|
|
|
|
PIP, $(TESTS_VENV_REQ))
|
|
|
|
$(call quiet-command, touch $@)
|
|
|
|
|
2018-10-18 18:31:33 +03:00
|
|
|
$(TESTS_RESULTS_DIR):
|
|
|
|
$(call quiet-command, mkdir -p $@, \
|
|
|
|
MKDIR, $@)
|
|
|
|
|
2018-10-18 18:31:32 +03:00
|
|
|
check-venv: $(TESTS_VENV_DIR)
|
|
|
|
|
2020-03-17 17:16:54 +03:00
|
|
|
FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(TARGETS))
|
|
|
|
FEDORA_31_ARCHES := x86_64 aarch64 ppc64le s390x
|
|
|
|
FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES))
|
|
|
|
|
|
|
|
# download one specific Fedora 31 image
|
|
|
|
get-vm-image-fedora-31-%: check-venv
|
|
|
|
$(call quiet-command, \
|
|
|
|
$(TESTS_VENV_DIR)/bin/python -m avocado vmimage get \
|
|
|
|
--distro=fedora --distro-version=31 --arch=$*, \
|
|
|
|
"AVOCADO", "Downloading acceptance tests VM image for $*")
|
|
|
|
|
|
|
|
# download all vm images, according to defined targets
|
|
|
|
get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD))
|
|
|
|
|
|
|
|
check-acceptance: check-venv $(TESTS_RESULTS_DIR) get-vm-images
|
2018-10-18 18:31:33 +03:00
|
|
|
$(call quiet-command, \
|
|
|
|
$(TESTS_VENV_DIR)/bin/python -m avocado \
|
|
|
|
--show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \
|
tests/acceptance: use "arch:" tag to filter target specific tests
Currently, some tests contains target architecture information, in the
form of a "x86_64" tag. But that tag is not respected in the default
execution, that is, "make check-acceptance" doesn't do anything with
it.
That said, even the target architecture handling currently present in
the "avocado_qemu.Test" class is pretty limited. For instance, by
default, it chooses a target based on the host architecture.
Because the original implementation of the tags feature in Avocado did
not include any time of namespace or "key:val" mechanism, no tag has
relation to another tag. The new implementation of the tags feature
from version 67.0 onwards, allows "key:val" tags, and because of that,
a test can be classified with a tag in a given key. For instance, the
new proposed version of the "boot_linux_console.py" test, which
downloads and attempts to run a x86_64 kernel, is now tagged as:
:avocado: tags=arch:x86_64
This means that it can be filtered (out) when no x86_64 target is
available. At the same time, tests that don't have a "arch:" tag,
will not be filtered out.
Signed-off-by: Cleber Rosa <crosa@redhat.com>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Message-Id: <20190312171824.5134-6-crosa@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2019-03-12 20:18:09 +03:00
|
|
|
--filter-by-tags-include-empty --filter-by-tags-include-empty-key \
|
|
|
|
$(AVOCADO_TAGS) \
|
2020-07-01 16:56:42 +03:00
|
|
|
$(if $(GITLAB_CI),,--failfast=on) tests/acceptance, \
|
2018-10-18 18:31:33 +03:00
|
|
|
"AVOCADO", "tests/acceptance")
|
|
|
|
|
2012-03-28 17:42:01 +04:00
|
|
|
# Consolidated targets
|
|
|
|
|
2020-08-04 21:00:40 +03:00
|
|
|
.PHONY: check-block check-unit check check-clean get-vm-images
|
2020-01-28 16:49:18 +03:00
|
|
|
check-block:
|
2020-08-04 21:00:40 +03:00
|
|
|
check-build: build-unit
|
2020-07-01 16:56:51 +03:00
|
|
|
|
2013-09-26 04:42:56 +04:00
|
|
|
check-clean:
|
2019-09-09 13:04:01 +03:00
|
|
|
rm -rf $(check-unit-y) tests/*.o tests/*/*.o $(QEMU_IOTESTS_HELPERS-y)
|
2018-02-26 22:48:58 +03:00
|
|
|
rm -f tests/test-qapi-gen-timestamp
|
2018-10-18 18:31:33 +03:00
|
|
|
rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR)
|
2013-09-26 04:42:56 +04:00
|
|
|
|
2020-01-28 16:49:18 +03:00
|
|
|
check: check-unit
|
2020-07-01 16:56:51 +03:00
|
|
|
|
2013-09-26 04:42:56 +04:00
|
|
|
clean: check-clean
|
2012-07-18 21:22:27 +04:00
|
|
|
|
2013-09-26 04:42:55 +04:00
|
|
|
# Build the help program automatically
|
|
|
|
|
2012-07-18 21:22:27 +04:00
|
|
|
-include $(wildcard tests/*.d)
|
2017-08-10 11:50:25 +03:00
|
|
|
|
|
|
|
endif
|