diff --git a/tests/Makefile.include b/tests/Makefile.include index 010369bd3acda..f34569002e57d 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -75,7 +75,7 @@ $(TCG_TESTS_TARGETS:%=distclean-tcg-tests-%): distclean-tcg-tests-%: build-tcg: $(BUILD_TCG_TARGET_RULES) .PHONY: check-tcg -.ninja-goals.check-tcg = all test-plugins +.ninja-goals.check-tcg = all test-plugins test-plugins-qpp check-tcg: $(RUN_TCG_TARGET_RULES) .PHONY: clean-tcg diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 2da70b2fcfaed..a12bfd34dc36f 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -146,7 +146,15 @@ ifeq ($(CONFIG_PLUGIN),y) PLUGIN_SRC=$(SRC_PATH)/tests/tcg/plugins PLUGIN_LIB=../plugins VPATH+=$(PLUGIN_LIB) -PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) + +ALL_PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) + +# Exclude libqpp_srv.so and libqpp_client.so from SINGLE_PLUGINS +SINGLE_PLUGINS=$(filter-out libqpp_srv.so libqpp_client.so, $(ALL_PLUGINS)) + +# Set PLUGINS to ALL_PLUGINS so that all plugins are built +PLUGINS=$(ALL_PLUGINS) + # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We @@ -155,9 +163,14 @@ PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) # add some special helpers the run-plugin- rules can use below. # In more, extra tests can be added using ADDITIONAL_PLUGINS_TESTS variable. +# We also add a -with-qpp runner for all tests which tests inter-plugin +# interactions. This is a special case where there isn't a one-to-one +# mapping between tests and plugins. + ifneq ($(MULTIARCH_TESTS),) -$(foreach p,$(PLUGINS), \ - $(foreach t,$(MULTIARCH_TESTS) $(ADDITIONAL_PLUGINS_TESTS),\ +$(foreach t,$(MULTIARCH_TESTS) $(ADDITIONAL_PLUGINS_TESTS),\ + $(eval RUN_TESTS+=run-plugin-$(t)-with-qpp) \ + $(foreach p,$(SINGLE_PLUGINS), \ $(eval run-plugin-$(t)-with-$(p): $t $p) \ $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) endif # MULTIARCH_TESTS @@ -168,6 +181,22 @@ extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1)) RUN_TESTS+=$(EXTRA_RUNS) +# The QPP test runs with two plugins, a client and server. It checks +# that they can interact with the QPP protocol. +run-plugin-%-with-qpp: CHECK_PLUGIN_OUTPUT_COMMAND = grep -q 'PASS' +run-plugin-%-with-qpp: hello libqpp_srv.so libqpp_client.so + $(call run-test, $@, \ + $(QEMU) -monitor none -display none \ + -chardev file$(COMMA)path=$@.out$(COMMA)id=output \ + -plugin $(PLUGIN_LIB)/libqpp_srv.so \ + -plugin $(PLUGIN_LIB)/libqpp_client.so \ + -d plugin -D $*.pout \ + $(QEMU_OPTS) $<) + $(if $(CHECK_PLUGIN_OUTPUT_COMMAND), \ + $(call quiet-command, $(CHECK_PLUGIN_OUTPUT_COMMAND) $*.pout, \ + TEST, check plugin output with $<)) + + # Some plugins need additional arguments above the default to fully # exercise things. We can define them on a per-test basis here. run-plugin-%-with-libmem.so: PLUGIN_ARGS=$(COMMA)inline=true diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index f847849b1b77b..8f7e3d6bbe98e 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -1,6 +1,6 @@ t = [] if get_option('plugins') - foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall'] + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall', 'qpp_client', 'qpp_srv'] if host_os == 'windows' t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', include_directories: '../../../include/qemu', diff --git a/tests/tcg/plugins/qpp_client.c b/tests/tcg/plugins/qpp_client.c new file mode 100644 index 0000000000000..c6066dbcc77c0 --- /dev/null +++ b/tests/tcg/plugins/qpp_client.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +QEMU_PLUGIN_EXPORT const char *qemu_plugin_name = "qpp_client"; +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +QEMU_PLUGIN_EXPORT const char *qemu_plugin_uses[] = {"qpp_srv", NULL}; + +#include "qpp_srv.h" +static bool pass = true; + +void my_cb_exit_callback(gpointer evdata, gpointer udata); + +QEMU_PLUGIN_EXPORT void my_cb_exit_callback(gpointer evdata, gpointer udata) +{ + // Function is called by qpp_srv, update the evdata to be our 'pass' var + *(bool *)evdata = pass; + + g_autoptr(GString) report = g_string_new("QPP client: my_on_exit callback triggered. "); + g_string_append_printf(report, "Setting result=%d\n",pass); + qemu_plugin_outs(report->str); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, char **argv) { + + // Use the QPP interface to run functions in qpp_srv + + // Target plugin is 'qpp_srv', we're calling the 'do_add' function and + // we append '_qpp' to the function name to identify it as a QPP function. + // This function is defined in qpp_srv.h + // Ensure that the return value is as expected + if (qpp_srv_do_add_qpp(3) == 4) { + pass &= true; + } + + // Now call the 'do_sub' method from qpp_srv and checking the result. + if (qpp_srv_do_sub_qpp(10) == 9) { + pass &= true; + } + + // Register a callback to run on a QPP callback provided by qpp_srv + qemu_plugin_reg_callback("qpp_srv", "my_on_exit", &my_cb_exit_callback); + + return 0; +} + diff --git a/tests/tcg/plugins/qpp_srv.c b/tests/tcg/plugins/qpp_srv.c new file mode 100644 index 0000000000000..9f1aceecc72ad --- /dev/null +++ b/tests/tcg/plugins/qpp_srv.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +QEMU_PLUGIN_EXPORT const char *qemu_plugin_name = "qpp_srv"; +#include "qpp_srv.h" + +static void plugin_exit(qemu_plugin_id_t id, void *p) +{ + qemu_plugin_outs("qpp_srv: exit triggered, running all registered" + " QPP callbacks\n"); + + // Trigger all callbacks registered with our custom on_exit callback + // We expect qpp_client to set qpp_client_passed if everything worked + + bool qpp_client_passed = false; + qemu_plugin_run_callback(id, "my_on_exit", &qpp_client_passed, NULL); + + if (qpp_client_passed) { + qemu_plugin_outs("PASS\n"); + } else { + qemu_plugin_outs("FAIL\n"); + } +} + +QEMU_PLUGIN_EXPORT int qpp_srv_do_add(int x) +{ + return x + 1; +} + +QEMU_PLUGIN_EXPORT int qpp_srv_do_sub(int x) +{ + return x - 1; +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, char **argv) { + qemu_plugin_create_callback(id, "my_on_exit"); + qemu_plugin_register_atexit_cb(id, plugin_exit, NULL); + return 0; +} diff --git a/tests/tcg/plugins/qpp_srv.h b/tests/tcg/plugins/qpp_srv.h new file mode 100644 index 0000000000000..16d9bc2df4e22 --- /dev/null +++ b/tests/tcg/plugins/qpp_srv.h @@ -0,0 +1,12 @@ +#ifndef QPP_SRV_H +#define QPP_SRV_H + + +/* + * Prototypes for the do_add and do_sub functions. Both return an int and + * take an int as an argument. + */ +QPP_FUN_PROTOTYPE(qpp_srv, int, qpp_srv_do_add, int); +QPP_FUN_PROTOTYPE(qpp_srv, int, qpp_srv_do_sub, int); + +#endif /* QPP_SRV_H */