KTF has already been available for a while as a separate git repository with means to facilitate use with any kernel version.
KTF can be used both for "pure" unit testing and for more pragmatic approaches to component testing. Apart from some useful features that KTF uses from the kernel toolbox (such as kallsyms, kprobes), KTF does not depend on any special environment such as UML or on a large set of mocked up APIs to be useful. KTF basically allows test code to be inserted and managed as separate kernel modules, while providing the tests convenient access to almost the full range of kernel APIs, both exposed and private. And once a KTF test set exists, it should be fairly easy to compile and run it against older versions of the kernel as well.
This series proposes a non-intrusive integration of KTF into the kernel hopefully presented in digestable pieces. For convenience, the patch set is also available on top of v5.2 at https://github.com/knuto/linux/pull/new/ktf_v1 .
The high level structure of the KTF code is as follows:
External dependencies for the user land side: * libnl3 for netlink communication * googletest for test runner, reporting and test selection support.
Kernel components: * Simple core test and test suite related abstractions: core data structures ktf_case, ktf_test, an assertion macro "infrastructure" with ASSERT_* and EXPECT_* macros and helper functions * Bookkeeping data structures: - ktf_map - a (key, value) mapping container used to implement management of instances of the higher level abstraction needs, such as ktf_handle and ktf_context. - ktf_handle: A global environment that a test runs within. - ktf_context: a test suite specific abstraction to allow a test to execute within one or more contexts. An example use of context can be a device instance: A test can be instantiated to run on all available such devices, according to test suite defined criteria. * A generic netlink protocol (ktf_nl, ktf_unlproto) which defines operations to allow a user space part of a test to query the kernel for test information, invoke tests and get feedback reports about results. * An alternative debugfs interface to allow examining and executing kernel-only tests without a user level program. * Support for overriding and modifying behaviour of kernel calls.
User mode components: * A test executor based on and integrated with Googletest. Googletest is one of several mature user land unit test suites for C/C++. The choice allowed us to focus on kernel specific functionality rather than having to reinvent too many wheels. * Tools to aid in creating new test modules (suites): To facilitate a developer friendly way of testing internals of a module or the kernel itself, one of the important features of KTF, we often need to access symbols deliberately not exposed from a module. KTF contains a script used to create definitions based on kallsyms lookup for easy access to symbols not exposed by a module or the kernel. The user just provides a simple text file with a list of the symbols by module.
This series is an attempt to address feedback from several people that having the functionality readily available within the kernel repository is desired.
An in-tree KTF allows test suites to be provided with the kernel, which makes it easy to use KTF based suites as part of the kernel selftests and driver test suites. Having the ability to still build and run the latest versions of test suites against older kernels should be of great value to stable maintainers, distributions and driver maintainers, who would want to have an easy path, with minimal backporting efforts to make sure that criterias implemented by new test logic is also valid for these kernels.
Our definite goal moving forward is to try to satisfy both needs in a transparent way. The plan is to let the standalone KTF repository follow the in-kernel one, and to allow test suites to be maintained similarly, and to support maintenance by proper tooling.
Mode of integration into the kernel ===================================
One feature of KTF is that it allows tests to work, look and feel similar whether they execute entirely in user mode, entirely in kernel mode, or half and half (hybrid tests). KTF consist of both user space and kernel code. Unlike e.g. kselftest, KTF in the Github version does not attempt to address the test runner aspects of testing.
Due to the need for building modules, KTF requires access to kernel module build facilities (obj-m). But KTF also has nontrivial needs for user land building, and we think it is good to keep the build structure in a way that allows KTF to be built both in-tree and out-of-tree without necessarily having to reconfigure the kernel.
This first version of kernel integration of KTF solves this challenge by co-locating everything associated with KTF under ktf/ as in the github version, but use the little used hostprogs-y and hostlibs-y features to build the user space side. The first patch in the series is fixes to make it work in a natural way to suit our needs.
Positioning for natural building within the kernel tree =======================================================
Currently we find significant amount of C level tests within the following paths:
tools/testing/selftests/ (kselftests, almost entirely user space so far) lib/ (various kernel level mostly unit tests)
and in the making::
kunit/ (kernel only (UML))
So all kernel code are currently located directly within the kernel build paths, accessed from the top level Makefile, to allow everything to be controlled by config and from the main build targets for the kernel. But this also poses challenges, in that .config has to be modified to build tests. And once a .config is changed, we no longer in principle logically operate on the same kernel.
A better approach seems to be to follow the principle taken by kselftest: To have all the logic associated with the test inside the test tree, and make it available for building separately from the kernel itself. This require us to have a means to build kernel modules from within the test tree, separately from the main kernel paths. Currently this seems to only by supported via the M= option used to build out-of-tree modules. This was also easy to get to work for the kernel parts, based on the Github version of KTF, where we already do this. With the additional need to compile user land code, using the corresponding hostprogs-y and hostNNlibs-y seemed natural, but this has been challenging: The build macros does not really support hostprogs-y etc as "first class citizens" so some amount of hacking is in there in this first draft version. Using hostprogs-y etc is also a different approach that what is used for C code in kselftest today, but we imagine that there's room for unification here to get the best of both worlds, with the help of the wider Kbuild community.
As an initial proposal, we have positioned ktf as an additional kselftest target, under tools/testing/selftests/ktf, and the recommended:
make TARGETS="ktf" kselftest
way of building and running should work, even from normal user accounts, if the user running it has sudo rights (for the kernel module insertion and removal). This will run the selftests for KTF itself, and should be a good starting point for adding more test cases. We also have had activities going to take some of the existing test suites under lib/ and convert them into KTF based test suites, and will get back to this later.
A trimmed down output from the above make target would look like this:
... CC [M] tools/testing/selftests/ktf/kernel/ktf_override.o LD [M] tools/testing/selftests/ktf/kernel/ktf.o HOSTCC -fPIC tools/testing/selftests/ktf/lib/ktf_unlproto.o HOSTCXX -fPIC tools/testing/selftests/ktf/lib/ktf_int.o KTFSYMS tools/testing/selftests/ktf/selftest/ktf_syms.h CC [M] tools/testing/selftests/ktf/selftest/self.o LD [M] tools/testing/selftests/ktf/selftest/selftest.o HOSTCXX tools/testing/selftests/ktf/user/ktftest.o HOSTCXX tools/testing/selftests/ktf/user/hybrid.o HOSTLD tools/testing/selftests/ktf/user/ktftest Building modules, stage 2. MODPOST 7 modules LD [M] tools/testing/selftests/ktf/kernel/ktf.ko LD [M] tools/testing/selftests/ktf/selftest/selftest.ko running tests make BUILD=/net/abi/local/abi/build/kernel/ktf/tools/testing/selftests -f scripts/runtests.mk run_tests TAP version 13 1..1 ... ok 1 selftests: ktf: runtests.sh
We're looking forward to feedback on this, and also to more discussion around unit testing at the testing & fuzzing workshop at LPC!
Alan Maguire (3): ktf: Implementation of ktf support for overriding function entry and return. ktf: A simple debugfs interface to test results ktf: Simple coverage support
Knut Omang (16): kbuild: Fixes to rules for host-cshlib and host-cxxshlib ktf: Introduce the main part of the kernel side of ktf ktf: Introduce a generic netlink protocol for test result communication ktf: An implementation of a generic associative array container ktf: Configurable context support for network info setup ktf: resolve: A helper utility to aid in exposing private kernel symbols to KTF tests. ktf: Add documentation for Kernel Test Framework (KTF) ktf: Add a small test suite with a few tests to test KTF itself ktf: Main part of user land library for executing tests ktf: Integration logic for running ktf tests from googletest ktf: Internal debugging facilities ktf: Some simple examples ktf: Some user applications to run tests ktf: Toplevel ktf Makefile/makefile includes and scripts to run from kselftest kselftests: Enable building ktf Documentation/dev-tools: Add index entry for KTF documentation
Documentation/dev-tools/index.rst | 1 +- Documentation/dev-tools/ktf/concepts.rst | 242 +++- Documentation/dev-tools/ktf/debugging.rst | 248 +++- Documentation/dev-tools/ktf/examples.rst | 26 +- Documentation/dev-tools/ktf/features.rst | 307 ++++- Documentation/dev-tools/ktf/implementation.rst | 70 +- Documentation/dev-tools/ktf/index.rst | 14 +- Documentation/dev-tools/ktf/installation.rst | 73 +- Documentation/dev-tools/ktf/introduction.rst | 134 ++- Documentation/dev-tools/ktf/progref.rst | 144 ++- scripts/Makefile.host | 17 +- tools/testing/selftests/Makefile | 1 +- tools/testing/selftests/ktf/Makefile | 21 +- tools/testing/selftests/ktf/examples/Makefile | 17 +- tools/testing/selftests/ktf/examples/h2.c | 45 +- tools/testing/selftests/ktf/examples/h3.c | 84 +- tools/testing/selftests/ktf/examples/h4.c | 62 +- tools/testing/selftests/ktf/examples/hello.c | 38 +- tools/testing/selftests/ktf/examples/kgdemo.c | 61 +- tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 +++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++- tools/testing/selftests/ktf/kernel/ktf_cov.c | 690 ++++++++- tools/testing/selftests/ktf/kernel/ktf_cov.h | 94 +- tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 +- tools/testing/selftests/ktf/kernel/ktf_map.c | 261 +++- tools/testing/selftests/ktf/kernel/ktf_map.h | 154 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.c | 132 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.h | 64 +- tools/testing/selftests/ktf/kernel/ktf_nl.c | 516 ++++++- tools/testing/selftests/ktf/kernel/ktf_nl.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_override.c | 45 +- tools/testing/selftests/ktf/kernel/ktf_override.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++- tools/testing/selftests/ktf/kernel/ktf_unlproto.h | 105 +- tools/testing/selftests/ktf/lib/Makefile | 21 +- tools/testing/selftests/ktf/lib/ktf.h | 114 +- tools/testing/selftests/ktf/lib/ktf_debug.cc | 20 +- tools/testing/selftests/ktf/lib/ktf_debug.h | 59 +- tools/testing/selftests/ktf/lib/ktf_int.cc | 1031 ++++++++++++- tools/testing/selftests/ktf/lib/ktf_int.h | 84 +- tools/testing/selftests/ktf/lib/ktf_run.cc | 177 ++- tools/testing/selftests/ktf/lib/ktf_unlproto.c | 21 +- tools/testing/selftests/ktf/scripts/ktf_syms.mk | 16 +- tools/testing/selftests/ktf/scripts/resolve | 188 ++- tools/testing/selftests/ktf/scripts/runtests.mk | 3 +- tools/testing/selftests/ktf/scripts/runtests.sh | 100 +- tools/testing/selftests/ktf/scripts/top_make.mk | 14 +- tools/testing/selftests/ktf/selftest/Makefile | 17 +- tools/testing/selftests/ktf/selftest/context.c | 149 ++- tools/testing/selftests/ktf/selftest/context.h | 15 +- tools/testing/selftests/ktf/selftest/context_self.h | 34 +- tools/testing/selftests/ktf/selftest/hybrid.c | 35 +- tools/testing/selftests/ktf/selftest/hybrid.h | 24 +- tools/testing/selftests/ktf/selftest/hybrid_self.h | 27 +- tools/testing/selftests/ktf/selftest/ktf_syms.txt | 17 +- tools/testing/selftests/ktf/selftest/self.c | 661 ++++++++- tools/testing/selftests/ktf/user/Makefile | 26 +- tools/testing/selftests/ktf/user/hybrid.cc | 39 +- tools/testing/selftests/ktf/user/ktfcov.cc | 68 +- tools/testing/selftests/ktf/user/ktfrun.cc | 20 +- tools/testing/selftests/ktf/user/ktftest.cc | 46 +- 64 files changed, 8909 insertions(+), 9 deletions(-) create mode 100644 Documentation/dev-tools/ktf/concepts.rst create mode 100644 Documentation/dev-tools/ktf/debugging.rst create mode 100644 Documentation/dev-tools/ktf/examples.rst create mode 100644 Documentation/dev-tools/ktf/features.rst create mode 100644 Documentation/dev-tools/ktf/implementation.rst create mode 100644 Documentation/dev-tools/ktf/index.rst create mode 100644 Documentation/dev-tools/ktf/installation.rst create mode 100644 Documentation/dev-tools/ktf/introduction.rst create mode 100644 Documentation/dev-tools/ktf/progref.rst create mode 100644 tools/testing/selftests/ktf/Makefile create mode 100644 tools/testing/selftests/ktf/examples/Makefile create mode 100644 tools/testing/selftests/ktf/examples/h2.c create mode 100644 tools/testing/selftests/ktf/examples/h3.c create mode 100644 tools/testing/selftests/ktf/examples/h4.c create mode 100644 tools/testing/selftests/ktf/examples/hello.c create mode 100644 tools/testing/selftests/ktf/examples/kgdemo.c create mode 100644 tools/testing/selftests/ktf/kernel/Makefile create mode 100644 tools/testing/selftests/ktf/kernel/ktf.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_context.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_cov.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_cov.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_map.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_map.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_netctx.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_netctx.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_nl.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_nl.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_override.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_override.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_unlproto.h create mode 100644 tools/testing/selftests/ktf/lib/Makefile create mode 100644 tools/testing/selftests/ktf/lib/ktf.h create mode 100644 tools/testing/selftests/ktf/lib/ktf_debug.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_debug.h create mode 100644 tools/testing/selftests/ktf/lib/ktf_int.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_int.h create mode 100644 tools/testing/selftests/ktf/lib/ktf_run.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_unlproto.c create mode 100644 tools/testing/selftests/ktf/scripts/ktf_syms.mk create mode 100755 tools/testing/selftests/ktf/scripts/resolve create mode 100644 tools/testing/selftests/ktf/scripts/runtests.mk create mode 100755 tools/testing/selftests/ktf/scripts/runtests.sh create mode 100644 tools/testing/selftests/ktf/scripts/top_make.mk create mode 100644 tools/testing/selftests/ktf/selftest/Makefile create mode 100644 tools/testing/selftests/ktf/selftest/context.c create mode 100644 tools/testing/selftests/ktf/selftest/context.h create mode 100644 tools/testing/selftests/ktf/selftest/context_self.h create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.c create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.h create mode 100644 tools/testing/selftests/ktf/selftest/hybrid_self.h create mode 100644 tools/testing/selftests/ktf/selftest/ktf_syms.txt create mode 100644 tools/testing/selftests/ktf/selftest/self.c create mode 100644 tools/testing/selftests/ktf/user/Makefile create mode 100644 tools/testing/selftests/ktf/user/hybrid.cc create mode 100644 tools/testing/selftests/ktf/user/ktfcov.cc create mode 100644 tools/testing/selftests/ktf/user/ktfrun.cc create mode 100644 tools/testing/selftests/ktf/user/ktftest.cc
base-commit: 0ecfebd2b52404ae0c54a878c872bb93363ada36
C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects.
Also fix rules for both C and C++ shared libraries: - C++ shared libraries depended on .c instead of .cc files - Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*.
Signed-off-by: Knut Omang knut.omang@oracle.com --- scripts/Makefile.host | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/scripts/Makefile.host b/scripts/Makefile.host index b6a54bd..4e9bb21 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -46,8 +46,10 @@ host-cxxmulti := $(foreach m,$(__hostprogs),$(if $($(m)-cxxobjs),$(m))) host-cxxobjs := $(sort $(foreach m,$(host-cxxmulti),$($(m)-cxxobjs)))
# Object (.o) files used by the shared libaries -host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-objs)))) -host-cxxshobjs := $(sort $(foreach m,$(host-cxxshlib),$($(m:.so=-objs)))) +# Note: C++ libraries may contain both C and C++ objects, compiled differently: +host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-cshobjs)))) +host-cshobjs += $(sort $(foreach m,$(host-cxxshlib),$($(m:.so=-cshobjs)))) +host-cxxshobjs := $(sort $(foreach m,$(host-cxxshlib),$($(m:.so=-cxxshobjs))))
host-csingle := $(addprefix $(obj)/,$(host-csingle)) host-cmulti := $(addprefix $(obj)/,$(host-cmulti)) @@ -130,14 +132,10 @@ quiet_cmd_host-cshobjs = HOSTCC -fPIC $@ $(host-cshobjs): $(obj)/%.o: $(src)/%.c FORCE $(call if_changed_dep,host-cshobjs)
-# Compile .c file, create position independent .o file -# Note that plugin capable gcc versions can be either C or C++ based -# therefore plugin source files have to be compilable in both C and C++ mode. -# This is why a C++ compiler is invoked on a .c file. -# host-cxxshobjs -> .o +# Compile .cc (C++) file, create position independent .o file quiet_cmd_host-cxxshobjs = HOSTCXX -fPIC $@ cmd_host-cxxshobjs = $(HOSTCXX) $(hostcxx_flags) -fPIC -c -o $@ $< -$(host-cxxshobjs): $(obj)/%.o: $(src)/%.c FORCE +$(host-cxxshobjs): $(obj)/%.o: $(src)/%.cc FORCE $(call if_changed_dep,host-cxxshobjs)
# Link a shared library, based on position independent .o files @@ -154,7 +152,8 @@ $(call multi_depend, $(host-cshlib), .so, -objs) # *.o -> .so shared library (host-cxxshlib) quiet_cmd_host-cxxshlib = HOSTLLD -shared $@ cmd_host-cxxshlib = $(HOSTCXX) $(KBUILD_HOSTLDFLAGS) -shared -o $@ \ - $(addprefix $(obj)/,$($(@F:.so=-objs))) \ + $(addprefix $(obj)/,$($(@F:.so=-cshobjs))) \ + $(addprefix $(obj)/,$($(@F:.so=-cxxshobjs))) \ $(KBUILD_HOSTLDLIBS) $(HOSTLDLIBS_$(@F)) $(host-cxxshlib): FORCE $(call if_changed,host-cxxshlib)
On Tue, Aug 13, 2019 at 3:13 PM Knut Omang knut.omang@oracle.com wrote:
C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects.
Also fix rules for both C and C++ shared libraries:
- C++ shared libraries depended on .c instead of .cc files
- Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*.
Signed-off-by: Knut Omang knut.omang@oracle.com
How is this patch related to the rest of this series?
This patch breaks GCC-plugins. Did you really compile-test this patch before the submission?
-- Best Regards
Masahiro Yamada
On Tue, 2019-08-13 at 23:01 +0900, Masahiro Yamada wrote:
On Tue, Aug 13, 2019 at 3:13 PM Knut Omang knut.omang@oracle.com wrote:
C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects.
Also fix rules for both C and C++ shared libraries:
- C++ shared libraries depended on .c instead of .cc files
- Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*.
Signed-off-by: Knut Omang knut.omang@oracle.com
How is this patch related to the rest of this series?
This is just my (likely naive) way I to get what I had working using autotools in the Github version of KTF) translated into something comparable using kbuild only. We need to build a shared library consisting of a few C++ files and a very simple C file, and a couple of simple binaries, and the rule in there does seem to take .c files and subject them to the C++ compiler, which makes this difficult to achieve?
This patch breaks GCC-plugins. Did you really compile-test this patch before the submission?
Sorry for my ignorance here: I ran through the kernel build and installed the resulting kernel on a VM that I used to test this, if that's what you are asking about?
Do I need some unusual .config options or run a special make target to trigger the problem you see?
I used a recent Fedora config with default values for new options, and ran the normal default make target (also with O=) and make selftests to test the patch itself.
Thanks, Knut
-- Best Regards
Masahiro Yamada
Hi Knut,
On Wed, Aug 14, 2019 at 1:19 AM Knut Omang knut.omang@oracle.com wrote:
On Tue, 2019-08-13 at 23:01 +0900, Masahiro Yamada wrote:
On Tue, Aug 13, 2019 at 3:13 PM Knut Omang knut.omang@oracle.com wrote:
C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects.
Also fix rules for both C and C++ shared libraries:
- C++ shared libraries depended on .c instead of .cc files
- Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*.
Signed-off-by: Knut Omang knut.omang@oracle.com
How is this patch related to the rest of this series?
This is just my (likely naive) way I to get what I had working using autotools in the Github version of KTF) translated into something comparable using kbuild only. We need to build a shared library consisting of a few C++ files and a very simple C file, and a couple of simple binaries, and the rule in there does seem to take .c files and subject them to the C++ compiler, which makes this difficult to achieve?
Looking at the diff stat of the cover-letter, the rest of this patch series is touching only Documentation/ and tools/testing/kselftests/.
So, this one is unused by the rest of the changes, isn't it? Am I missing something?
This patch breaks GCC-plugins. Did you really compile-test this patch before the submission?
Sorry for my ignorance here: I ran through the kernel build and installed the resulting kernel on a VM that I used to test this, if that's what you are asking about?
Do I need some unusual .config options or run a special make target to trigger the problem you see?
I used a recent Fedora config with default values for new options, and ran the normal default make target (also with O=) and make selftests to test the patch itself.
I just built allmodconfig for arm.
(The 0-day bot tests allmodconfig for most of architectures, so you may receive error reports anyway.)
With your patch, I got the following:
masahiro@grover:~/ref/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- allmodconfig all HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/confdata.o HOSTCC scripts/kconfig/expr.o LEX scripts/kconfig/lexer.lex.c YACC scripts/kconfig/parser.tab.h HOSTCC scripts/kconfig/lexer.lex.o YACC scripts/kconfig/parser.tab.c HOSTCC scripts/kconfig/parser.tab.o HOSTCC scripts/kconfig/preprocess.o HOSTCC scripts/kconfig/symbol.o HOSTLD scripts/kconfig/conf scripts/kconfig/conf --allmodconfig Kconfig # # configuration written to .config # SYSHDR arch/arm/include/generated/uapi/asm/unistd-common.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-oabi.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-eabi.h HOSTCC scripts/dtc/dtc.o HOSTCC scripts/dtc/flattree.o HOSTCC scripts/dtc/fstree.o HOSTCC scripts/dtc/data.o HOSTCC scripts/dtc/livetree.o HOSTCC scripts/dtc/treesource.o HOSTCC scripts/dtc/srcpos.o HOSTCC scripts/dtc/checks.o HOSTCC scripts/dtc/util.o LEX scripts/dtc/dtc-lexer.lex.c YACC scripts/dtc/dtc-parser.tab.h HOSTCC scripts/dtc/dtc-lexer.lex.o YACC scripts/dtc/dtc-parser.tab.c HOSTCC scripts/dtc/dtc-parser.tab.o HOSTCC scripts/dtc/yamltree.o HOSTLD scripts/dtc/dtc CC scripts/gcc-plugins/latent_entropy_plugin.o cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/structleak_plugin.so ./scripts/gcc-plugins/structleak_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/latent_entropy_plugin.so ./scripts/gcc-plugins/latent_entropy_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/randomize_layout_plugin.so ./scripts/gcc-plugins/randomize_layout_plugin.so: cannot open shared object file: No such file or directory make[3]: *** [scripts/Makefile.build;281: scripts/gcc-plugins/latent_entropy_plugin.o] Error 1 make[2]: *** [scripts/Makefile.build;497: scripts/gcc-plugins] Error 2 make[1]: *** [Makefile;1097: scripts] Error 2 make: *** [Makefile;330: __build_one_by_one] Error 2
On Wed, 2019-08-14 at 11:02 +0900, Masahiro Yamada wrote:
Hi Knut,
On Wed, Aug 14, 2019 at 1:19 AM Knut Omang knut.omang@oracle.com wrote:
On Tue, 2019-08-13 at 23:01 +0900, Masahiro Yamada wrote:
On Tue, Aug 13, 2019 at 3:13 PM Knut Omang knut.omang@oracle.com wrote:
C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects.
Also fix rules for both C and C++ shared libraries:
- C++ shared libraries depended on .c instead of .cc files
- Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*.
Signed-off-by: Knut Omang knut.omang@oracle.com
How is this patch related to the rest of this series?
This is just my (likely naive) way I to get what I had working using autotools in the Github version of KTF) translated into something comparable using kbuild only. We need to build a shared library consisting of a few C++ files and a very simple C file, and a couple of simple binaries, and the rule in there does seem to take .c files and subject them to the C++ compiler, which makes this difficult to achieve?
Looking at the diff stat of the cover-letter, the rest of this patch series is touching only Documentation/ and tools/testing/kselftests/.
So, this one is unused by the rest of the changes, isn't it? Am I missing something?
What I am trying to do is to build kernel **and** user mode components under tools/testing/selftests/ktf and to keep the kernel test code out of the tree. The only way I was able to do this was by means of the module build, eg. 'make TARGETS="ktf" kselftest' will cause a toplevel 'make M=tools/testing/selftests/ktf' which is what builds the lot. One can argue that this is a misuse of that mechanism, but as I argue in the cover letter, I think there is a need to make it possible to have kernel code that is not going to be part of the installed kernel somewhere else than in the normal build path of the kernel, which is what I am trying to achieve.
The toplevel Makefile logic for this is in patch 17 of the series in tools/testing/selftests/ktf/Makefile and for ktf/kernel/Makefile and ktf/lib/Makefile and ktf/user/Makefile in patches 2, 12 and 16 respectively.
It is a bit of a hack still, since the hostcxxprogs-y and hostcxxlib-y targets in ktf/user and ktf/lib do not get invoked "automatically" from __build like the obj-m targets. I haven't been able to figure out yet how to achieve that, and was hoping I could get help on it :-)
Thanks! Knut
This patch breaks GCC-plugins. Did you really compile-test this patch before the submission?
Sorry for my ignorance here: I ran through the kernel build and installed the resulting kernel on a VM that I used to test this, if that's what you are asking about?
Do I need some unusual .config options or run a special make target to trigger the problem you see?
I used a recent Fedora config with default values for new options, and ran the normal default make target (also with O=) and make selftests to test the patch itself.
I just built allmodconfig for arm.
(The 0-day bot tests allmodconfig for most of architectures, so you may receive error reports anyway.)
With your patch, I got the following:
masahiro@grover:~/ref/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- allmodconfig all HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/confdata.o HOSTCC scripts/kconfig/expr.o LEX scripts/kconfig/lexer.lex.c YACC scripts/kconfig/parser.tab.h HOSTCC scripts/kconfig/lexer.lex.o YACC scripts/kconfig/parser.tab.c HOSTCC scripts/kconfig/parser.tab.o HOSTCC scripts/kconfig/preprocess.o HOSTCC scripts/kconfig/symbol.o HOSTLD scripts/kconfig/conf scripts/kconfig/conf --allmodconfig Kconfig # # configuration written to .config # SYSHDR arch/arm/include/generated/uapi/asm/unistd-common.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-oabi.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-eabi.h HOSTCC scripts/dtc/dtc.o HOSTCC scripts/dtc/flattree.o HOSTCC scripts/dtc/fstree.o HOSTCC scripts/dtc/data.o HOSTCC scripts/dtc/livetree.o HOSTCC scripts/dtc/treesource.o HOSTCC scripts/dtc/srcpos.o HOSTCC scripts/dtc/checks.o HOSTCC scripts/dtc/util.o LEX scripts/dtc/dtc-lexer.lex.c YACC scripts/dtc/dtc-parser.tab.h HOSTCC scripts/dtc/dtc-lexer.lex.o YACC scripts/dtc/dtc-parser.tab.c HOSTCC scripts/dtc/dtc-parser.tab.o HOSTCC scripts/dtc/yamltree.o HOSTLD scripts/dtc/dtc CC scripts/gcc-plugins/latent_entropy_plugin.o cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/structleak_plugin.so ./scripts/gcc-plugins/structleak_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/latent_entropy_plugin.so ./scripts/gcc-plugins/latent_entropy_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/randomize_layout_plugin.so ./scripts/gcc-plugins/randomize_layout_plugin.so: cannot open shared object file: No such file or directory make[3]: *** [scripts/Makefile.build;281: scripts/gcc-plugins/latent_entropy_plugin.o] Error 1 make[2]: *** [scripts/Makefile.build;497: scripts/gcc-plugins] Error 2 make[1]: *** [Makefile;1097: scripts] Error 2 make: *** [Makefile;330: __build_one_by_one] Error 2
On Wed, 2019-08-14 at 11:02 +0900, Masahiro Yamada wrote:
Hi Knut,
On Wed, Aug 14, 2019 at 1:19 AM Knut Omang knut.omang@oracle.com wrote:
On Tue, 2019-08-13 at 23:01 +0900, Masahiro Yamada wrote:
On Tue, Aug 13, 2019 at 3:13 PM Knut Omang knut.omang@oracle.com wrote:
C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects.
Also fix rules for both C and C++ shared libraries:
- C++ shared libraries depended on .c instead of .cc files
- Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*.
Signed-off-by: Knut Omang knut.omang@oracle.com
How is this patch related to the rest of this series?
This is just my (likely naive) way I to get what I had working using autotools in the Github version of KTF) translated into something comparable using kbuild only. We need to build a shared library consisting of a few C++ files and a very simple C file, and a couple of simple binaries, and the rule in there does seem to take .c files and subject them to the C++ compiler, which makes this difficult to achieve?
Looking at the diff stat of the cover-letter, the rest of this patch series is touching only Documentation/ and tools/testing/kselftests/.
So, this one is unused by the rest of the changes, isn't it? Am I missing something?
This patch breaks GCC-plugins. Did you really compile-test this patch before the submission?
Sorry for my ignorance here: I ran through the kernel build and installed the resulting kernel on a VM that I used to test this, if that's what you are asking about?
Do I need some unusual .config options or run a special make target to trigger the problem you see?
I used a recent Fedora config with default values for new options, and ran the normal default make target (also with O=) and make selftests to test the patch itself.
I just built allmodconfig for arm.
(The 0-day bot tests allmodconfig for most of architectures, so you may receive error reports anyway.)
With your patch, I got the following:
masahiro@grover:~/ref/linux$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- allmodconfig all HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/confdata.o HOSTCC scripts/kconfig/expr.o LEX scripts/kconfig/lexer.lex.c YACC scripts/kconfig/parser.tab.h HOSTCC scripts/kconfig/lexer.lex.o YACC scripts/kconfig/parser.tab.c HOSTCC scripts/kconfig/parser.tab.o HOSTCC scripts/kconfig/preprocess.o HOSTCC scripts/kconfig/symbol.o HOSTLD scripts/kconfig/conf scripts/kconfig/conf --allmodconfig Kconfig # # configuration written to .config # SYSHDR arch/arm/include/generated/uapi/asm/unistd-common.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-oabi.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-eabi.h HOSTCC scripts/dtc/dtc.o HOSTCC scripts/dtc/flattree.o HOSTCC scripts/dtc/fstree.o HOSTCC scripts/dtc/data.o HOSTCC scripts/dtc/livetree.o HOSTCC scripts/dtc/treesource.o HOSTCC scripts/dtc/srcpos.o HOSTCC scripts/dtc/checks.o HOSTCC scripts/dtc/util.o LEX scripts/dtc/dtc-lexer.lex.c YACC scripts/dtc/dtc-parser.tab.h HOSTCC scripts/dtc/dtc-lexer.lex.o YACC scripts/dtc/dtc-parser.tab.c HOSTCC scripts/dtc/dtc-parser.tab.o HOSTCC scripts/dtc/yamltree.o HOSTLD scripts/dtc/dtc CC scripts/gcc-plugins/latent_entropy_plugin.o cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/structleak_plugin.so ./scripts/gcc-plugins/structleak_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/latent_entropy_plugin.so ./scripts/gcc-plugins/latent_entropy_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/randomize_layout_plugin.so ./scripts/gcc-plugins/randomize_layout_plugin.so: cannot open shared object file: No such file or directory make[3]: *** [scripts/Makefile.build;281: scripts/gcc-plugins/latent_entropy_plugin.o] Error 1 make[2]: *** [scripts/Makefile.build;497: scripts/gcc-plugins] Error 2 make[1]: *** [Makefile;1097: scripts] Error 2 make: *** [Makefile;330: __build_one_by_one] Error 2
Ok, I see!
I'll recall this target and look into it!
Thanks! Knut
On Wed, 2019-08-14 at 07:52 +0200, Knut Omang wrote:
On Wed, 2019-08-14 at 11:02 +0900, Masahiro Yamada wrote:
Hi Knut,
On Wed, Aug 14, 2019 at 1:19 AM Knut Omang knut.omang@oracle.com wrote:
On Tue, 2019-08-13 at 23:01 +0900, Masahiro Yamada wrote:
On Tue, Aug 13, 2019 at 3:13 PM Knut Omang knut.omang@oracle.com wrote:
C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects.
Also fix rules for both C and C++ shared libraries:
- C++ shared libraries depended on .c instead of .cc files
- Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*.
Signed-off-by: Knut Omang knut.omang@oracle.com
How is this patch related to the rest of this series?
This is just my (likely naive) way I to get what I had working using autotools in the Github version of KTF) translated into something comparable using kbuild only. We need to build a shared library consisting of a few C++ files and a very simple C file, and a couple of simple binaries, and the rule in there does seem to take .c files and subject them to the C++ compiler, which makes this difficult to achieve?
Looking at the diff stat of the cover-letter, the rest of this patch series is touching only Documentation/ and tools/testing/kselftests/.
So, this one is unused by the rest of the changes, isn't it? Am I missing something?
This patch breaks GCC-plugins. Did you really compile-test this patch before the submission?
Sorry for my ignorance here: I ran through the kernel build and installed the resulting kernel on a VM that I used to test this, if that's what you are asking about?
Do I need some unusual .config options or run a special make target to trigger the problem you see?
I used a recent Fedora config with default values for new options, and ran the normal default make target (also with O=) and make selftests to test the patch itself.
I just built allmodconfig for arm.
(The 0-day bot tests allmodconfig for most of architectures, so you may receive error reports anyway.)
With your patch, I got the following:
masahiro@grover:~/ref/linux$ make ARCH=arm CROSS_COMPILE=- allmodconfig all HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/confdata.o HOSTCC scripts/kconfig/expr.o LEX scripts/kconfig/lexer.lex.c YACC scripts/kconfig/parser.tab.h HOSTCC scripts/kconfig/lexer.lex.o YACC scripts/kconfig/parser.tab.c HOSTCC scripts/kconfig/parser.tab.o HOSTCC scripts/kconfig/preprocess.o HOSTCC scripts/kconfig/symbol.o HOSTLD scripts/kconfig/conf scripts/kconfig/conf --allmodconfig Kconfig # # configuration written to .config # SYSHDR arch/arm/include/generated/uapi/asm/unistd-common.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-oabi.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-eabi.h HOSTCC scripts/dtc/dtc.o HOSTCC scripts/dtc/flattree.o HOSTCC scripts/dtc/fstree.o HOSTCC scripts/dtc/data.o HOSTCC scripts/dtc/livetree.o HOSTCC scripts/dtc/treesource.o HOSTCC scripts/dtc/srcpos.o HOSTCC scripts/dtc/checks.o HOSTCC scripts/dtc/util.o LEX scripts/dtc/dtc-lexer.lex.c YACC scripts/dtc/dtc-parser.tab.h HOSTCC scripts/dtc/dtc-lexer.lex.o YACC scripts/dtc/dtc-parser.tab.c HOSTCC scripts/dtc/dtc-parser.tab.o HOSTCC scripts/dtc/yamltree.o HOSTLD scripts/dtc/dtc CC scripts/gcc-plugins/latent_entropy_plugin.o cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/structleak_plugin.so ./scripts/gcc-plugins/structleak_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/latent_entropy_plugin.so ./scripts/gcc-plugins/latent_entropy_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/randomize_layout_plugin.so ./scripts/gcc-plugins/randomize_layout_plugin.so: cannot open shared object file: No such file or directory make[3]: *** [scripts/Makefile.build;281: scripts/gcc-plugins/latent_entropy_plugin.o] Error 1 make[2]: *** [scripts/Makefile.build;497: scripts/gcc-plugins] Error 2 make[1]: *** [Makefile;1097: scripts] Error 2 make: *** [Makefile;330: __build_one_by_one] Error 2
Ok, I see!
I'll recall this target and look into it!
Ok, so I have tried installing the arm-linux-gnueabihf cross compiler and compiled the kernel for arm, but allmodconfig does not seem to enable any GCC plugins per default even on ARM and I haven't been able to figure out how to enable any.
A plain allmodconfig generated config compiles perfectly for me both native x86 and w/arm cross compile, but it doesn't seem to enable any gcc plugins.
Anyway, maybe I am getting this wrong anyway: Having played with cross compile, it starts to become clear to me that HOSTCC rules might not be the right rules to use, as it will generate host user land binaries as opposed to target user land binaries (in a cross compile world obviously these differ)
Now, I started off with using the rules in the selftests makefiles for this, but they do not play that well with kernel module building. My goal is to be able to do both user land and kernel module **target** compiles from the same subtree. Any hints on how to accomplish this appreciated :-)
Thanks, Knut
On Wed, Aug 14, 2019 at 9:53 PM Knut Omang knut.omang@oracle.com wrote:
On Wed, 2019-08-14 at 07:52 +0200, Knut Omang wrote:
On Wed, 2019-08-14 at 11:02 +0900, Masahiro Yamada wrote:
Hi Knut,
On Wed, Aug 14, 2019 at 1:19 AM Knut Omang knut.omang@oracle.com wrote:
On Tue, 2019-08-13 at 23:01 +0900, Masahiro Yamada wrote:
On Tue, Aug 13, 2019 at 3:13 PM Knut Omang knut.omang@oracle.com wrote:
C++ libraries interfacing to C APIs might sometimes need some glue logic more easily written in C. Allow a C++ library to also contain 0 or more C objects.
Also fix rules for both C and C++ shared libraries:
- C++ shared libraries depended on .c instead of .cc files
- Rules were referenced as -objs instead of the intended -cobjs and -cxxobjs following the pattern from hostprogs*.
Signed-off-by: Knut Omang knut.omang@oracle.com
How is this patch related to the rest of this series?
This is just my (likely naive) way I to get what I had working using autotools in the Github version of KTF) translated into something comparable using kbuild only. We need to build a shared library consisting of a few C++ files and a very simple C file, and a couple of simple binaries, and the rule in there does seem to take .c files and subject them to the C++ compiler, which makes this difficult to achieve?
Looking at the diff stat of the cover-letter, the rest of this patch series is touching only Documentation/ and tools/testing/kselftests/.
So, this one is unused by the rest of the changes, isn't it? Am I missing something?
This patch breaks GCC-plugins. Did you really compile-test this patch before the submission?
Sorry for my ignorance here: I ran through the kernel build and installed the resulting kernel on a VM that I used to test this, if that's what you are asking about?
Do I need some unusual .config options or run a special make target to trigger the problem you see?
I used a recent Fedora config with default values for new options, and ran the normal default make target (also with O=) and make selftests to test the patch itself.
I just built allmodconfig for arm.
(The 0-day bot tests allmodconfig for most of architectures, so you may receive error reports anyway.)
With your patch, I got the following:
masahiro@grover:~/ref/linux$ make ARCH=arm CROSS_COMPILE=- allmodconfig all HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/confdata.o HOSTCC scripts/kconfig/expr.o LEX scripts/kconfig/lexer.lex.c YACC scripts/kconfig/parser.tab.h HOSTCC scripts/kconfig/lexer.lex.o YACC scripts/kconfig/parser.tab.c HOSTCC scripts/kconfig/parser.tab.o HOSTCC scripts/kconfig/preprocess.o HOSTCC scripts/kconfig/symbol.o HOSTLD scripts/kconfig/conf scripts/kconfig/conf --allmodconfig Kconfig # # configuration written to .config # SYSHDR arch/arm/include/generated/uapi/asm/unistd-common.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-oabi.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-eabi.h HOSTCC scripts/dtc/dtc.o HOSTCC scripts/dtc/flattree.o HOSTCC scripts/dtc/fstree.o HOSTCC scripts/dtc/data.o HOSTCC scripts/dtc/livetree.o HOSTCC scripts/dtc/treesource.o HOSTCC scripts/dtc/srcpos.o HOSTCC scripts/dtc/checks.o HOSTCC scripts/dtc/util.o LEX scripts/dtc/dtc-lexer.lex.c YACC scripts/dtc/dtc-parser.tab.h HOSTCC scripts/dtc/dtc-lexer.lex.o YACC scripts/dtc/dtc-parser.tab.c HOSTCC scripts/dtc/dtc-parser.tab.o HOSTCC scripts/dtc/yamltree.o HOSTLD scripts/dtc/dtc CC scripts/gcc-plugins/latent_entropy_plugin.o cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/structleak_plugin.so ./scripts/gcc-plugins/structleak_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/latent_entropy_plugin.so ./scripts/gcc-plugins/latent_entropy_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/randomize_layout_plugin.so ./scripts/gcc-plugins/randomize_layout_plugin.so: cannot open shared object file: No such file or directory make[3]: *** [scripts/Makefile.build;281: scripts/gcc-plugins/latent_entropy_plugin.o] Error 1 make[2]: *** [scripts/Makefile.build;497: scripts/gcc-plugins] Error 2 make[1]: *** [Makefile;1097: scripts] Error 2 make: *** [Makefile;330: __build_one_by_one] Error 2
Ok, I see!
I'll recall this target and look into it!
Ok, so I have tried installing the arm-linux-gnueabihf cross compiler and compiled the kernel for arm, but allmodconfig does not seem to enable any GCC plugins per default even on ARM and I haven't been able to figure out how to enable any.
Linaro toolchain supports gcc plugins.
https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux...
kernel.org one supports it as well.
https://mirrors.edge.kernel.org/pub/tools/crosstool/
A plain allmodconfig generated config compiles perfectly for me both native x86 and w/arm cross compile, but it doesn't seem to enable any gcc plugins.
Anyway, maybe I am getting this wrong anyway: Having played with cross compile, it starts to become clear to me that HOSTCC rules might not be the right rules to use, as it will generate host user land binaries as opposed to target user land binaries (in a cross compile world obviously these differ)
Now, I started off with using the rules in the selftests makefiles for this, but they do not play that well with kernel module building. My goal is to be able to do both user land and kernel module **target** compiles from the same subtree. Any hints on how to accomplish this appreciated :-)
tools/ is out of scope of kbuild because it adopted a different (more adhoc) build system. I have no idea. Please talk to the kselftest maintainer.
Thanks, Knut
On Wed, 2019-08-21 at 10:47 +0900, Masahiro Yamada wrote:
On Wed, Aug 14, 2019 at 9:53 PM Knut Omang knut.omang@oracle.com wrote:
On Wed, 2019-08-14 at 07:52 +0200, Knut Omang wrote:
On Wed, 2019-08-14 at 11:02 +0900, Masahiro Yamada wrote:
Hi Knut,
On Wed, Aug 14, 2019 at 1:19 AM Knut Omang knut.omang@oracle.com wrote:
On Tue, 2019-08-13 at 23:01 +0900, Masahiro Yamada wrote:
On Tue, Aug 13, 2019 at 3:13 PM Knut Omang knut.omang@oracle.com wrote: > C++ libraries interfacing to C APIs might sometimes need some glue > logic more easily written in C. > Allow a C++ library to also contain 0 or more C objects. > > Also fix rules for both C and C++ shared libraries: > - C++ shared libraries depended on .c instead of .cc files > - Rules were referenced as -objs instead of the intended > -cobjs and -cxxobjs following the pattern from hostprogs*. > > Signed-off-by: Knut Omang knut.omang@oracle.com
How is this patch related to the rest of this series?
This is just my (likely naive) way I to get what I had working using autotools in the Github version of KTF) translated into something comparable using kbuild only. We need to build a shared library consisting of a few C++ files and a very simple C file, and a couple of simple binaries, and the rule in there does seem to take .c files and subject them to the C++ compiler, which makes this difficult to achieve?
Looking at the diff stat of the cover-letter, the rest of this patch series is touching only Documentation/ and tools/testing/kselftests/.
So, this one is unused by the rest of the changes, isn't it? Am I missing something?
This patch breaks GCC-plugins. Did you really compile-test this patch before the submission?
Sorry for my ignorance here: I ran through the kernel build and installed the resulting kernel on a VM that I used to test this, if that's what you are asking about?
Do I need some unusual .config options or run a special make target to trigger the problem you see?
I used a recent Fedora config with default values for new options, and ran the normal default make target (also with O=) and make selftests to test the patch itself.
I just built allmodconfig for arm.
(The 0-day bot tests allmodconfig for most of architectures, so you may receive error reports anyway.)
With your patch, I got the following:
masahiro@grover:~/ref/linux$ make ARCH=arm CROSS_COMPILE=- allmodconfig all HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/confdata.o HOSTCC scripts/kconfig/expr.o LEX scripts/kconfig/lexer.lex.c YACC scripts/kconfig/parser.tab.h HOSTCC scripts/kconfig/lexer.lex.o YACC scripts/kconfig/parser.tab.c HOSTCC scripts/kconfig/parser.tab.o HOSTCC scripts/kconfig/preprocess.o HOSTCC scripts/kconfig/symbol.o HOSTLD scripts/kconfig/conf scripts/kconfig/conf --allmodconfig Kconfig # # configuration written to .config # SYSHDR arch/arm/include/generated/uapi/asm/unistd-common.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-oabi.h SYSHDR arch/arm/include/generated/uapi/asm/unistd-eabi.h HOSTCC scripts/dtc/dtc.o HOSTCC scripts/dtc/flattree.o HOSTCC scripts/dtc/fstree.o HOSTCC scripts/dtc/data.o HOSTCC scripts/dtc/livetree.o HOSTCC scripts/dtc/treesource.o HOSTCC scripts/dtc/srcpos.o HOSTCC scripts/dtc/checks.o HOSTCC scripts/dtc/util.o LEX scripts/dtc/dtc-lexer.lex.c YACC scripts/dtc/dtc-parser.tab.h HOSTCC scripts/dtc/dtc-lexer.lex.o YACC scripts/dtc/dtc-parser.tab.c HOSTCC scripts/dtc/dtc-parser.tab.o HOSTCC scripts/dtc/yamltree.o HOSTLD scripts/dtc/dtc CC scripts/gcc-plugins/latent_entropy_plugin.o cc1: error: cannot load plugin ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so ./scripts/gcc-plugins/arm_ssp_per_task_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/structleak_plugin.so ./scripts/gcc-plugins/structleak_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/latent_entropy_plugin.so ./scripts/gcc-plugins/latent_entropy_plugin.so: cannot open shared object file: No such file or directory cc1: error: cannot load plugin ./scripts/gcc-plugins/randomize_layout_plugin.so ./scripts/gcc-plugins/randomize_layout_plugin.so: cannot open shared object file: No such file or directory make[3]: *** [scripts/Makefile.build;281: scripts/gcc-plugins/latent_entropy_plugin.o] Error 1 make[2]: *** [scripts/Makefile.build;497: scripts/gcc-plugins] Error 2 make[1]: *** [Makefile;1097: scripts] Error 2 make: *** [Makefile;330: __build_one_by_one] Error 2
Ok, I see!
I'll recall this target and look into it!
Ok, so I have tried installing the arm-linux-gnueabihf cross compiler and compiled the
kernel for arm,
but allmodconfig does not seem to enable any GCC plugins per default even on ARM and I
haven't been able
to figure out how to enable any.
Linaro toolchain supports gcc plugins.
https://releases.linaro.org/components/toolchain/binaries/latest-7/arm-linux...
kernel.org one supports it as well.
https://mirrors.edge.kernel.org/pub/tools/crosstool/
A plain allmodconfig generated config compiles perfectly for me both native x86 and
w/arm cross compile,
but it doesn't seem to enable any gcc plugins.
Anyway, maybe I am getting this wrong anyway: Having played with cross compile, it starts to become clear to me that HOSTCC rules might not be the right rules to use, as it will generate host user land binaries as
opposed to
target user land binaries (in a cross compile world obviously these differ)
Now, I started off with using the rules in the selftests makefiles for this, but they
do not play that well with
kernel module building. My goal is to be able to do both user land and kernel module
**target** compiles
from the same subtree. Any hints on how to accomplish this appreciated :-)
tools/ is out of scope of kbuild because it adopted a different (more adhoc) build system.
That resonates with my understanding, thanks! I think that's what I am trying to understand how to improve:
To sum up, my conclusion so far is that there's a need for a set of more integrated Kbuild rules for target platform binaries and libraries similar to the host* rules. The rules in tools/ could then be modified to use that generic support instead of the current adhoc approach. That would allow the needs of tests that have both user space and kernel space code to be more nicely co-located, with several benefits IMHO.
Any further insights appreciated!
I have no idea. Please talk to the kselftest maintainer.
She's on the Cc:-list, I was hoping to get a some of the challenges/choices highlighted here ;-)
Thanks! Knut
The ktf module itself and basic data structures for management of test cases and tests and contexts for tests. Also contains the top level include file for kernel clients in ktf.h.
More elaborate documentation follows towards the end of the patch set.
This patch set contains both user level and kernel code, we'll provide the full implementation of ktf on the kernel side in this and forthcoming patches, then the user space code to execute tests within the kernel and report results, then documentation before introducing a small self test suite of tests to test ktf itself, and some very simple additional example tests.
ktf.h: Defines the KTF user API for kernel clients ktf_test.c: Kernel side code for tracking and reporting ktf test results
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++++++++- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++++++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++++++++- 5 files changed, 1806 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/Makefile create mode 100644 tools/testing/selftests/ktf/kernel/ktf.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_context.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.h
diff --git a/tools/testing/selftests/ktf/kernel/Makefile b/tools/testing/selftests/ktf/kernel/Makefile new file mode 100644 index 0000000..0897ae2 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/Makefile @@ -0,0 +1,15 @@ +# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 +# +# Implementation of the kernel part of Kernel Test Framework (KTF), +# a framework for running unit test like tests within the kernel. +# + +obj-m := ktf.o + +include $(srctree)/$(src)/../scripts/ktf_syms.mk + +ktf-y := ktf_context.o ktf_nl.o ktf_map.o ktf_test.o ktf_debugfs.o ktf_cov.o \ + ktf_override.o ktf_netctx.o + diff --git a/tools/testing/selftests/ktf/kernel/ktf.h b/tools/testing/selftests/ktf/kernel/ktf.h new file mode 100644 index 0000000..ea270e7 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf.h @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf.h: Defines the KTF user API for kernel clients + */ +#ifndef _KTF_H +#define _KTF_H + +#include <linux/completion.h> +#include <linux/kprobes.h> +#include <linux/kthread.h> +#include <linux/ptrace.h> +#include "ktf_test.h" +#include "ktf_override.h" +#include "ktf_map.h" +#include "ktf_unlproto.h" + +#define KTF_MAX_LOG 2048 + +/* Type for an optional configuration callback for contexts. + * Implementations should copy and store data into their private + * extensions of the context structure. The data pointer is + * only valid inside the callback: + */ +typedef int (*ktf_config_cb)(struct ktf_context *ctx, const void* data, size_t data_sz); +typedef void (*ktf_context_cb)(struct ktf_context *ctx); + +struct ktf_context_type; + +struct ktf_context { + struct ktf_map_elem elem; /* Linkage for ctx_map in handle */ + char name[KTF_MAX_KEY]; /* Context name used in map */ + struct ktf_handle *handle; /* Owner of this context */ + ktf_config_cb config_cb; /* Optional configuration callback */ + ktf_context_cb cleanup; /* Optional callback upon context release */ + int config_errno; /* If config_cb set: state of configuration */ + struct ktf_context_type *type; /* Associated type, must be set */ +}; + +typedef struct ktf_context* (*ktf_context_alloc)(struct ktf_context_type *ct); + +struct ktf_context_type { + struct ktf_map_elem elem; /* Linkage for map in handle */ + char name[KTF_MAX_KEY]; /* Context type name */ + struct ktf_handle *handle; /* Owner of this context type */ + ktf_context_alloc alloc; /* Allocate a new context of this type */ + ktf_config_cb config_cb; /* Configuration callback */ + ktf_context_cb cleanup; /* Optional callback upon context release */ +}; + +#include "ktf_netctx.h" + +/* type for a test function */ +struct ktf_test; + +/* state of running test, used to pass to threads spawned by test. */ +struct ktf_test_state; + +struct ktf_thread { + int (*func)(void *); + const char *name; + struct task_struct *task; + struct ktf_test_state state; + struct completion started; + struct completion completed; +}; + +typedef void (*ktf_test_adder)(void); + +/* Generic setup function for client modules */ +void ktf_add_tests(ktf_test_adder f); +int ktf_context_add(struct ktf_handle *handle, struct ktf_context* ctx, + const char* name, ktf_config_cb cfg_cb, const char *type); +struct ktf_context *ktf_context_add_from(struct ktf_handle *handle, const char *name, + struct ktf_context_type *ct); +const char *ktf_context_name(struct ktf_context *ctx); +struct ktf_context* ktf_find_context(struct ktf_handle *handle, const char* name); +struct ktf_context *ktf_find_first_context(struct ktf_handle *handle); +struct ktf_context *ktf_find_next_context(struct ktf_context* ctx); +void ktf_context_remove(struct ktf_context *ctx); +size_t ktf_has_contexts(struct ktf_handle *handle); +void ktf_context_remove_all(struct ktf_handle *handle); + +/* Called by framework when a configuration is supplied, + * returns the return value of the configuration callback. + */ +int ktf_context_set_config(struct ktf_context *ctx, const void* data, size_t data_sz); + +struct ktf_context *ktf_find_create_context(struct ktf_handle *handle, const char *name, + const char *type_name); +int ktf_handle_add_ctx_type(struct ktf_handle *handle, struct ktf_context_type *ct); +struct ktf_context_type *ktf_handle_get_ctx_type(struct ktf_handle *handle, + const char *type_name); + +/* Declare the implicit __test_handle as extern for .c files that use it + * when adding tests with ADD_TEST but where definition is in another .c file: + */ +extern struct ktf_handle __test_handle; + +/* Add/remove/find a context to/from the default handle */ +#define KTF_CONTEXT_ADD(__context, name) \ + ktf_context_add(&__test_handle, __context, name, NULL, "default") +#define KTF_CONTEXT_ADD_CFG(__context, name, __cb, __type_name) \ + ktf_context_add(&__test_handle, __context, name, __cb, __type_name) +#define KTF_CONTEXT_FIND(name) ktf_find_context(&__test_handle, name) +#define KTF_CONTEXT_GET(name, type) \ + container_of(KTF_CONTEXT_FIND(name), type, k) + +/* Add/remove/find a context to/from a given handle */ +#define KTF_CONTEXT_ADD_TO(__handle, __context, name) \ + ktf_context_add(&__handle, __context, name, NULL, "default") +#define KTF_CONTEXT_ADD_TO_CFG(__handle, __context, name, __cb, __type_name) \ + ktf_context_add(&__handle, __context, name, __cb, __type_name) +#define KTF_CONTEXT_FIND_IN(__handle, name) ktf_find_context(&__handle, name) +#define KTF_CONTEXT_GET_IN(__handle, name, type) \ + container_of(KTF_CONTEXT_FIND_IN(__handle, name), type, k) + +/* check if a context has been configured (if needed) */ +#define KTF_CONTEXT_CFG_OK(__context) \ + (__context->config_cb && !__context->config_errno) +#define KTF_CONTEXT_REMOVE(__context) ktf_context_remove(__context) + +/* Part of KTF support for hybrid tests: Safe get the out-of-band user data + * Silently return (ignoring the test) if no data is available. + * This is to avoid failing if a generic user program without + * specific support for the hybrid test attempts to run the test. + * Fail if an object of an unexpected size is provided. + */ +#define KTF_USERDATA(__kt_ptr, __priv_datatype, __priv_data) \ + struct __priv_datatype *__priv_data = (struct __priv_datatype *)__kt_ptr->data; \ + if (!__priv_data) return; \ + ASSERT_LONG_EQ(__kt_ptr->data_sz, sizeof(struct __priv_datatype)) + +/* KTF support for entry/return probes (via kprobes kretprobes). We use + * kretprobes as they support entry/return and do not induce panics when + * mixed with gkdb usage. + */ + +#if (defined(CONFIG_KPROBES) && defined(CONFIG_KRETPROBES) && \ + (defined(CONFIG_X86_64) || defined(CONFIG_ARM) || \ + defined(CONFIG_ARM64) || defined(CONFIG_SPARC))) +#define KTF_PROBE_SUPPORT +#else +static inline int ktf_no_probe_support(void) +{ + twarn("No support for k[ret]probes, or platform not supported.") + return -ENOTSUPP); +} +#endif + +/* Entry/return probe - type is handler type (entry_handler for entry, + * handler for return), func is function to be probed; probehandler is name + * of probe handling function we will invoke on entry/return. + */ +#define KTF_PROBE(type, func, probehandler) \ + static int probehandler(struct kretprobe_instance *, struct pt_regs *);\ + static struct kretprobe __ktf_##type##_##probehandler = { \ + .type = probehandler, \ + .data_size = 0, \ + .maxactive = 0, \ + .kp = { \ + .symbol_name = #func, \ + }, \ + }; \ + static int probehandler(struct kretprobe_instance *ri, \ + struct pt_regs *regs) + +#ifdef KTF_PROBE_SUPPORT +#define KTF_REGISTER_PROBE(type, func, probehandler) \ + register_kretprobe(&__ktf_##type##_##probehandler) +#else +#define KTF_REGISTER_PROBE(type, func, probehandler) \ + ktf_no_probe_support() +#endif + +/* Note on the complexity below - to re-use a statically-defined kretprobe for + * registration, we need to clean up state in the struct kretprobe. Hence + * we zero out the kretprobe and re-set the symbol name/handler. Not doing + * this means that re-registering fails with -EINVAL. + */ +#define KTF_UNREGISTER_PROBE(type, func, probehandler) \ + do { \ + unregister_kretprobe(&__ktf_##type##_##probehandler); \ + memset(&__ktf_##type##_##probehandler, 0, \ + sizeof(struct kretprobe)); \ + __ktf_##type##_##probehandler.kp.symbol_name = #func; \ + __ktf_##type##_##probehandler.type = probehandler; \ + } while (0) + +#define KTF_ENTRY_PROBE(func, probehandler) \ + KTF_PROBE(entry_handler, func, probehandler) + +#define KTF_REGISTER_ENTRY_PROBE(func, probehandler) \ + KTF_REGISTER_PROBE(entry_handler, func, probehandler) + +/* arch-specific calling conventions for kretprobes entry handlers. Define + * more args/architectures if needed. + */ +#ifdef KTF_PROBE_SUPPORT +#ifdef CONFIG_X86_64 +#define KTF_ENTRY_PROBE_ARG0 (regs->di) +#define KTF_ENTRY_PROBE_ARG1 (regs->si) +#endif /* CONFIG_X86_64 */ +#ifdef CONFIG_ARM +#define KTF_ENTRY_PROBE_ARG0 (regs->regs[0]) +#define KTF_ENTRY_PROBE_ARG1 (regs->regs[1]) +#endif /* CONFIG_ARM */ +#ifdef CONFIG_ARM64 +#define KTF_ENTRY_PROBE_ARG0 (regs->regs[0]) +#define KTF_ENTRY_PROBE_ARG1 (regs->regs[1]) +#endif /* CONFIG_ARM64 */ +#ifdef CONFIG_SPARC +#define KTF_ENTRY_PROBE_ARG0 (regs->u_regs[UREG_I0]) +#define KTF_ENTRY_PROBE_ARG1 (regs->u_regs[UREG_I1]) +#endif /* CONFIG_SPARC */ +#endif /* KTF_PROBE_SUPPORT */ + +/* For unsupported platforms. */ +#ifndef KTF_ENTRY_PROBE_ARG0 +#define KTF_ENTRY_PROBE_ARG0 (0) +#define KTF_ENTRY_PROBE_ARG1 (1) +#endif + +#define KTF_ENTRY_PROBE_RETURN(retval) \ + do { \ + return retval; \ + } while (0) + +#define KTF_UNREGISTER_ENTRY_PROBE(func, probehandler) \ + KTF_UNREGISTER_PROBE(entry_handler, func, probehandler) + +/* KTF support for return probes (via kprobes kretprobes) */ +#define KTF_RETURN_PROBE(func, probehandler) \ + KTF_PROBE(handler, func, probehandler) + +#define KTF_REGISTER_RETURN_PROBE(func, probehandler) \ + KTF_REGISTER_PROBE(handler, func, probehandler) + +/* KTF_*RETURN_VALUE() definitions for use within KTF_RETURN_PROBE() {} only. */ + +#define KTF_RETURN_VALUE() regs_return_value(regs) + +#ifdef KTF_PROBE_SUPPORT +#ifdef CONFIG_X86_64 +#define KTF_SET_RETURN_VALUE(value) regs->ax = (value) +#endif /* CONFIG_X86_64 */ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) +#define KTF_SET_RETURN_VALUE(value) regs->regs[0] = (value) +#endif /* CONFIG_ARM[64] */ +#if defined(CONFIG_SPARC) +#define KTF_SET_RETURN_VALUE(value) regs->u_regs[UREG_I0] = (value) +#endif /* CONFIG_SPARC */ +#endif /* KTF_PROBE_SUPPORT */ + +/* For unsupported platforms. */ +#ifndef KTF_PROBE_SUPPORT +#define KTF_SET_RETURN_VALUE(value) do { } while (0) +#endif /* KTF_PROBE_SUPPORT */ + +#define KTF_UNREGISTER_RETURN_PROBE(func, probehandler) \ + KTF_UNREGISTER_PROBE(handler, func, probehandler) + +#define KTF_OVERRIDE(func, probehandler) \ + static int probehandler(struct kprobe *, struct pt_regs *);\ + static struct kprobe __ktf_override_##probehandler = { \ + .symbol_name = #func, \ + .pre_handler = probehandler, \ + .post_handler = ktf_post_handler, \ + .fault_handler = NULL, \ + .flags = 0, \ + }; \ + static int probehandler(struct kprobe *kp, struct pt_regs *regs) + +#ifdef KTF_PROBE_SUPPORT +#define KTF_REGISTER_OVERRIDE(func, probehandler) \ + ktf_register_override(&__ktf_override_##probehandler) +#else +#define KTF_REGISTER_OVERRIDE(func, probehandler) \ + ktf_no_probe_support() +#endif + +#define KTF_UNREGISTER_OVERRIDE(func, probehandler) \ + do { \ + unregister_kprobe(&__ktf_override_##probehandler); \ + memset(&__ktf_override_##probehandler, 0, \ + sizeof(struct kprobe)); \ + __ktf_override_##probehandler.symbol_name = #func; \ + __ktf_override_##probehandler.pre_handler = probehandler; \ + __ktf_override_##probehandler.post_handler = ktf_post_handler; \ + } while (0) + + +#define KTF_OVERRIDE_RETURN \ + do { \ + ktf_override_function_with_return(regs); \ + return 1; \ + } while (0) + +#ifdef KTF_PROBE_SUPPORT +#define KTF_SET_INSTRUCTION_POINTER(regs, value) \ + instruction_pointer_set(regs, (value)) +#else +#define KTF_SET_INSTRUCTION_POINTER(regs, value) do { } while (0) +#endif + +/* Interfaces for creating kthreads in tests. */ +#define KTF_THREAD_INIT(threadname, t) \ + do { \ + (t)->func = threadname; \ + (t)->name = #threadname; \ + (t)->state.self = self; \ + (t)->state.ctx = ctx; \ + (t)->state.iter = _i; \ + (t)->state.value = _value; \ + init_completion(&((t)->started)); \ + init_completion(&((t)->completed)); \ + } while (0) + +#define KTF_THREAD_RUN(t) \ + ((t)->task = kthread_run((t)->func, t, (t)->name)) + +#define KTF_THREAD_STOP(t) \ + do { \ + if ((t)->task) \ + kthread_stop((t)->task); \ + } while (0) + +/* Wraps thread execution to supply same variables as test case - this allows + * us to define assertions etc in thread context. + */ +#define KTF_THREAD(name) \ + static void __##name(struct ktf_thread *thread, struct ktf_test *self, \ + struct ktf_context *ctx, int _i, u32 _value); \ + static int name(void *data) \ + { \ + struct ktf_thread *t = data; \ + complete(&t->started); \ + __##name(t, t->state.self, t->state.ctx, t->state.iter, \ + t->state.value); \ + complete(&t->completed); \ + return 0; \ + } \ + static void __##name(struct ktf_thread *_thread, struct ktf_test *self,\ + struct ktf_context *ctx, int _i, u32 _value) + +#define KTF_THREAD_WAIT_STARTED(t) (wait_for_completion(&((t)->started))) +#define KTF_THREAD_WAIT_COMPLETED(t) (wait_for_completion(&((t)->completed))) + +u32 ktf_get_assertion_count(void); + +/** + * ASSERT_TRUE() - fail and return if @C evaluates to false + * @C: Boolean expression to evaluate + * + */ +#define ASSERT_TRUE(C) do { \ + if (!ktf_assert((C))) return; \ + } while (0) + +/** + * ASSERT_FALSE() - fail and return if @C evaluates to true + * @C: Boolean expression to evaluate + */ +#define ASSERT_FALSE(C) do { \ + if (!ktf_assert(!(C))) return; \ + } while (0) + +/** + * ASSERT_TRUE_GOTO() - fail and jump to @_lbl if @C evaluates to false + * @C: Boolean expression to evaluate + * @_lbl: Label to jump to in case of failure + */ +#define ASSERT_TRUE_GOTO(C,_lbl) { \ + if (!ktf_assert((C))) goto _lbl;\ +} + +/** + * ASSERT_FALSE_GOTO() - fail and jump to @_lbl if @C evaluates to true + * @C: Boolean expression to evaluate + * @_lbl: Label to jump to in case of failure + */ +#define ASSERT_FALSE_GOTO(C,_lbl) { \ + if (!ktf_assert(!(C))) goto _lbl;\ +} + +/** + * ASSERT_TRUE_RETVAL() - fail and return @V if @C evaluates to false + * @C: Boolean expression to evaluate + * @V: Value to return on failure + */ +#define ASSERT_TRUE_RETVAL(C, V) do { \ + if (!ktf_assert((C))) return V; \ +} while (0) + +/** + * ASSERT_FALSE() - fail and return @V if @C evaluates to true + * @C: Boolean expression to evaluate + * @V: Value to return on failure + */ +#define ASSERT_FALSE_RETVAL(C, V) do { \ + if (!ktf_assert(!(C))) return V; \ +} while (0) + +/** + * ASSERT_TRUE_CONT() - fail and continue if @C evaluates to false + * @C: Boolean expression to evaluate + */ +#define ASSERT_TRUE_CONT(C) { \ + if (!ktf_assert((C))) continue; \ +} + +/** + * ASSERT_FALSE_CONT() - fail and continue if @C evaluates to true + * @C: Boolean expression to evaluate + */ +#define ASSERT_FALSE_CONT(C) { \ + if (!ktf_assert(!(C))) continue; \ +} + +/** + * ASSERT_TRUE_BREAK() - fail and break if @C evaluates to false + * @C: Boolean expression to evaluate + */ +#define ASSERT_TRUE_BREAK(C) { \ + if (!ktf_assert((C))) break; \ +} + +/** + * ASSERT_FALSE_BREAK() - fail and break if @C evaluates to true + * @C: Boolean expression to evaluate + */ +#define ASSERT_FALSE_BREAK(C) { \ + if (!ktf_assert(!(C))) break; \ +} + +/** + * ASSERT_LONG_EQ() - compare two longs, fail and return if @X != @Y + * @X: Expected value + * @Y: Actual value + */ +#define ASSERT_LONG_EQ(X, Y) \ + ktf_assert_long_ret(X, ==, Y); + +#define ASSERT_LONG_NE(X, Y) \ + ktf_assert_long_ret(X, !=, Y); + +#define ASSERT_ADDR_EQ(X, Y) \ + ktf_assert_long_ret((u64)(X), ==, (u64)(Y)); + +#define ASSERT_ADDR_NE(X, Y) \ + ktf_assert_long_ret((u64)(X), !=, (u64)(Y)); + +#define ASSERT_INT_EQ(X, Y) \ + ktf_assert_int_ret(X, ==, Y); + +#define ASSERT_INT_GT(X, Y) \ + ktf_assert_int_ret(X, >, Y); + +/** + * ASSERT_LONG_EQ() - compare two longs, jump to @_lbl if @X != @Y + * @X: Expected value + * @Y: Actual value + * @_lbl: Label to jump to in case of failure + */ +#define ASSERT_LONG_EQ_GOTO(X, Y, _lbl) \ + ktf_assert_long_goto(X, ==, Y, _lbl) + +#define ASSERT_LONG_NE_GOTO(X, Y, _lbl) \ + ktf_assert_long_goto(X, !=, Y, _lbl) + +#define ASSERT_ADDR_EQ_GOTO(X, Y, _lbl) \ + ktf_assert_long_goto((u64)(X), ==, (u64)(Y), _lbl) + +#define ASSERT_ADDR_NE_GOTO(X, Y, _lbl) \ + ktf_assert_long_goto((u64)(X), !=, (u64)(Y), _lbl) + +#define ASSERT_INT_EQ_GOTO(X, Y, _lbl) \ + ktf_assert_int_goto(X, ==, Y, _lbl) + +#define ASSERT_INT_GE_GOTO(X, Y, _lbl) \ + ktf_assert_int_goto(X, >=, Y, _lbl) + +#define ASSERT_INT_GT_GOTO(X, Y, _lbl) \ + ktf_assert_int_goto(X, >, Y, _lbl) + +#define ASSERT_INT_LT_GOTO(X, Y, _lbl) \ + ktf_assert_int_goto(X, <, Y, _lbl) + +#define ASSERT_INT_NE(X,Y) \ + ktf_assert_int_ret(X, !=, Y); + +#define ASSERT_INT_NE_GOTO(X,Y,_lbl) \ + ktf_assert_int_goto(X, !=, Y, _lbl); + +/** + * EXPECT_TRUE() - fail if @C evaluates to false but allow test to continue + * @C: Boolean expression to evaluate + * + */ +#define EXPECT_TRUE(C) ktf_assert(C) +#define EXPECT_FALSE(C) ktf_assert(!(C)) + +#define OK_ADDR(X) (X && !IS_ERR(X)) + +/* Valid kernel address check */ +#define EXPECT_OK_ADDR(X) \ + ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X)) + +#define ASSERT_OK_ADDR(X) do { \ + if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - value 0x%Lx", (X))) \ + return; \ + } while (0) +#define ASSERT_OK_ADDR_GOTO(X,_lbl) do { \ + if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X))) \ + goto _lbl; \ + } while (0) + +#define ASSERT_OK_ADDR_BREAK(X) do { \ + if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X))) \ + break; \ + } while (0) + +#define EXPECT_INT_EQ(X,Y) ktf_assert_int(X, ==, Y) +#define EXPECT_INT_GT(X,Y) ktf_assert_int(X, >, Y) +#define EXPECT_INT_GE(X,Y) ktf_assert_int(X, >=, Y) +#define EXPECT_INT_LE(X,Y) ktf_assert_int(X, <=, Y) +#define EXPECT_INT_LT(X,Y) ktf_assert_int(X, <, Y) +#define EXPECT_INT_NE(X,Y) ktf_assert_int(X, !=, Y) + +#define EXPECT_LONG_EQ(X, Y) ktf_assert_long(X, ==, Y) +#define EXPECT_LONG_NE(X, Y) ktf_assert_long(X, !=, Y) +#define EXPECT_ADDR_EQ(X, Y) ktf_assert_long((u64)(X), ==, (u64)(Y)) +#define EXPECT_ADDR_NE(X, Y) ktf_assert_long((u64)(X), !=, (u64)(Y)) +#define EXPECT_LONG_GT(X, Y) ktf_assert_long(X, >, Y) +#define EXPECT_LONG_GE(X, Y) ktf_assert_long(X, >=, Y) +#define EXPECT_LONG_LE(X, Y) ktf_assert_long(X, <=, Y) +#define EXPECT_LONG_LT(X, Y) ktf_assert_long(X, <, Y) + +#define EXPECT_STREQ(X, Y) ktf_assert_str_eq(X, Y) +#define EXPECT_STRNE(X, Y) ktf_assert_str_ne(X, Y) + +extern ulong ktf_debug_mask; + +/* Defined debug bits - higher values should represent more + * verbose categories: + */ +#define T_INFO 0x1 +#define T_LIST 0x2 +#define T_INTR 0x200 +#define T_INFO_V 0x800 +#define T_DEBUG 0x1000 +#define T_MCAST 0x2000 +#define T_TRACE 0x100000 +#define T_DEBUG_V 0x200000 + +#define tlog(class, format, arg...) \ + do { \ + if (unlikely((ktf_debug_mask) & (class))) \ + printk(KERN_INFO \ + "ktf pid [%d] " "%s: " format "\n", \ + current->pid, __func__, \ + ## arg); \ + } while (0) +#define twarn(format, arg...) \ + do { \ + printk(KERN_WARNING \ + "ktf pid [%d] " "%s: " format "\n", \ + current->pid, __func__, \ + ## arg); \ + } while (0) +#define terr(format, arg...) \ + do { \ + printk(KERN_ERR \ + "ktf pid [%d] " "%s: " format "\n", \ + current->pid, __func__, \ + ## arg); \ + } while (0) +#define tlogs(class, stmt_list) \ + do { \ + if (unlikely((ktf_debug_mask) & (class))) { \ + stmt_list;\ + } \ + } while (0) + + +/* Look up the current address of a potentially local symbol - to allow testing + * against it. NB! This is a hack for unit testing internal unexposed interfaces and + * violates the module boundaries and has no fw/bw comp gauarantees, but are + * still very useful for detailed unit testing complex logic: + */ +void* ktf_find_symbol(const char *mod, const char *sym); + +unsigned long ktf_symbol_size(unsigned long addr); + +#define ktf_resolve_symbol(mname, sname) \ + do { \ + sname = ktf_find_symbol(#mname, #sname); \ + if (!sname) \ + return -ENOENT; \ + } while (0) +#endif diff --git a/tools/testing/selftests/ktf/kernel/ktf_context.c b/tools/testing/selftests/ktf/kernel/ktf_context.c new file mode 100644 index 0000000..5ff57a9 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_context.c @@ -0,0 +1,409 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * kft_context.c: Main part of ktf kernel module that implements a generic + * unit test framework for tests written in kernel code, with support for + * gtest (googletest) user space tools for invocation and reporting. + */ + +#include <linux/module.h> +#include <linux/kallsyms.h> +#include <rdma/ib_verbs.h> +#include "ktf.h" +#include "ktf_test.h" +#include "ktf_debugfs.h" +#include "ktf_nl.h" + +MODULE_LICENSE("GPL"); + +ulong ktf_debug_mask = T_INFO; +EXPORT_SYMBOL(ktf_debug_mask); + +static unsigned int ktf_context_maxid; + +/* The role of context_lock is to synchronize modifications to + * the global list of context handles (handles that have contexts + * associated with them) and the context map. + * The map object has it's own locking, but must be kept in sync + * with changes to the global context list: + */ +static DEFINE_SPINLOCK(context_lock); + +/* global linked list of all ktf_handle objects that have contexts */ +LIST_HEAD(context_handles); + +module_param_named(debug_mask, ktf_debug_mask, ulong, 0644); + +static int __ktf_handle_add_ctx_type(struct ktf_handle *handle, + struct ktf_context_type *ct, + bool generic) +{ + unsigned long flags; + int ret; + + if (generic && !(ct->alloc && ct->config_cb)) { + terr("Mandatory configuration callbacks or values missing!"); + return -EINVAL; + } + + ct->handle = handle; + ktf_map_elem_init(&ct->elem, ct->name); + + spin_lock_irqsave(&context_lock, flags); + ret = ktf_map_insert(&handle->ctx_type_map, &ct->elem); + spin_unlock_irqrestore(&context_lock, flags); + return ret; +} + +static int __ktf_context_add(struct ktf_handle *handle, struct ktf_context *ctx, + const char *name, ktf_config_cb cfg_cb, + struct ktf_context_type *ct) +{ + unsigned long flags; + int ret; + + ktf_map_elem_init(&ctx->elem, name); + strncpy(ctx->name, name, KTF_MAX_NAME); + ctx->config_cb = cfg_cb; + ctx->config_errno = ENOENT; /* 0 here means configuration is ok */ + ctx->type = ct; + ctx->cleanup = ct->cleanup; + + spin_lock_irqsave(&context_lock, flags); + ret = ktf_map_insert(&handle->ctx_map, &ctx->elem); + if (!ret) { + ctx->handle = handle; + if (ktf_map_size(&handle->ctx_map) == 1) { + handle->id = ++ktf_context_maxid; + INIT_LIST_HEAD(&handle->handle_list); + list_add(&handle->handle_list, &context_handles); + } + } + spin_unlock_irqrestore(&context_lock, flags); + if (!ret) + tlog(T_DEBUG, "added %scontext %s with type %s", + (cfg_cb ? "configurable " : ""), name, ct->name); + return ret; +} + +int ktf_context_add(struct ktf_handle *handle, struct ktf_context *ctx, + const char *name, ktf_config_cb cfg_cb, + const char *type_name) +{ + struct ktf_context_type *ct = ktf_handle_get_ctx_type(handle, type_name); + int ret; + + if (!ct) { + ct = kzalloc(sizeof(*ct), GFP_KERNEL); + if (!ct) + return -ENOMEM; + strncpy(ct->name, type_name, KTF_MAX_KEY); + ret = __ktf_handle_add_ctx_type(handle, ct, false); + if (ret) { + kfree(ct); + return ret; + } + } + return __ktf_context_add(handle, ctx, name, cfg_cb, ct); +} +EXPORT_SYMBOL(ktf_context_add); + +struct ktf_context *ktf_context_add_from(struct ktf_handle *handle, const char *name, + struct ktf_context_type *ct) +{ + struct ktf_context *ctx; + int ret; + + if (!ct->alloc) { + terr("No alloc function supplied!"); + return NULL; + } + ctx = ct->alloc(ct); + if (!ctx) + return NULL; + ret = __ktf_context_add(handle, ctx, name, ct->config_cb, ct); + if (ret) + goto fail; + + ctx->cleanup = ct->cleanup; + return ctx; +fail: + kfree(ctx); + return NULL; +} +EXPORT_SYMBOL(ktf_context_add_from); + +int ktf_context_set_config(struct ktf_context *ctx, const void *data, size_t data_sz) +{ + int ret; + + if (ctx->config_cb) { + ret = ctx->config_cb(ctx, data, data_sz); + ctx->config_errno = ret; + } + return ctx->config_errno; +} +EXPORT_SYMBOL(ktf_context_set_config); + +const char *ktf_context_name(struct ktf_context *ctx) +{ + return ctx->elem.key; +} +EXPORT_SYMBOL(ktf_context_name); + +void ktf_context_remove(struct ktf_context *ctx) +{ + struct ktf_handle *handle; + unsigned long flags = 0; + + if (!ctx) { + terr("A test case tried to remove an invalid context!"); + return; + } + handle = ctx->handle; + + spin_lock_irqsave(&context_lock, flags); + ktf_map_remove(&handle->ctx_map, ctx->elem.key); + if (!ktf_has_contexts(handle)) + list_del(&handle->handle_list); + spin_unlock_irqrestore(&context_lock, flags); + + tlog(T_DEBUG, "removed context %s at %p", ctx->elem.key, ctx); + + if (ctx->cleanup) + ctx->cleanup(ctx); + /* Note: ctx may be freed here! */ +} +EXPORT_SYMBOL(ktf_context_remove); + +struct ktf_context *ktf_find_first_context(struct ktf_handle *handle) +{ + struct ktf_map_elem *elem = ktf_map_find_first(&handle->ctx_map); + + if (elem) + return container_of(elem, struct ktf_context, elem); + return NULL; +} + +struct ktf_context *ktf_find_context(struct ktf_handle *handle, const char *name) +{ + struct ktf_map_elem *elem; + + if (!name) + return NULL; + elem = ktf_map_find(&handle->ctx_map, name); + if (!elem) + return NULL; + return container_of(elem, struct ktf_context, elem); +} +EXPORT_SYMBOL(ktf_find_context); + +struct ktf_context *ktf_find_create_context(struct ktf_handle *handle, const char *name, + const char *type_name) +{ + struct ktf_context *ctx = ktf_find_context(handle, name); + + if (!ctx) { + struct ktf_context_type *ct = ktf_handle_get_ctx_type(handle, type_name); + + tlog(T_DEBUG, "type = %s, ct = %p", type_name, ct); + if (ct) + ctx = ktf_context_add_from(handle, name, ct); + } + return ctx; +} + +struct ktf_context *ktf_find_next_context(struct ktf_context *ctx) +{ + struct ktf_map_elem *elem = ktf_map_find_next(&ctx->elem); + + return container_of(elem, struct ktf_context, elem); +} + +size_t ktf_has_contexts(struct ktf_handle *handle) +{ + return ktf_map_size(&handle->ctx_map) > 0; +} +EXPORT_SYMBOL(ktf_has_contexts); + +void ktf_context_remove_all(struct ktf_handle *handle) +{ + struct ktf_context *curr; + + if (!ktf_has_contexts(handle)) + return; + + for (;;) { + curr = ktf_find_first_context(handle); + if (!curr) + break; + ktf_context_remove(curr); + } +} +EXPORT_SYMBOL(ktf_context_remove_all); + +/* Find the handle associated with handle id hid */ +struct ktf_handle *ktf_handle_find(int hid) +{ + struct ktf_handle *handle = NULL; + + list_for_each_entry(handle, &context_handles, handle_list) { + if (handle->id == hid) + break; + } + return handle; +} + +/* Allow user space to create new contexts of certain types + * based on configuration types. This allocates a new, uniquely named + * context type to enable it for user space usage. Caller must allocate and populate + * @ct with appropriate callbacks and value for the context type. + */ + +int ktf_handle_add_ctx_type(struct ktf_handle *handle, + struct ktf_context_type *ct) +{ + return __ktf_handle_add_ctx_type(handle, ct, true); +} +EXPORT_SYMBOL(ktf_handle_add_ctx_type); + +struct ktf_context_type *ktf_handle_get_ctx_type(struct ktf_handle *handle, + const char *type_name) +{ + struct ktf_map_elem *elem = ktf_map_find(&handle->ctx_type_map, type_name); + + tlog(T_DEBUG, "Lookup %s in map size %lu = %p\n", type_name, + ktf_map_size(&handle->ctx_type_map), elem); + if (!elem) + return NULL; + return container_of(elem, struct ktf_context_type, elem); +} + +void ktf_handle_cleanup_check(struct ktf_handle *handle) +{ + struct ktf_context *curr; + unsigned long flags; + + if (!ktf_has_contexts(handle)) + return; + + spin_lock_irqsave(&context_lock, flags); + + for (curr = ktf_find_first_context(handle); + curr; + curr = ktf_find_next_context(curr)) { + twarn("context %s found during handle %p cleanup", curr->elem.key, handle); + } + spin_unlock_irqrestore(&context_lock, flags); +} +EXPORT_SYMBOL(ktf_handle_cleanup_check); + +struct ktf_kernel_internals { + /* From module.h: Look up a module symbol - supports syntax module:name */ + unsigned long (*module_kallsyms_lookup_name)(const char *name); + /* From kallsyms.h: Look up a symbol w/size and offset */ + unsigned long (*kallsyms_lookup_size_offset)(unsigned long addr, + unsigned long *symbolsize, + unsigned long *offset); +}; + +static struct ktf_kernel_internals ki; + +static int __init ktf_init(void) +{ + int ret; + char *ks = "module_kallsyms_lookup_name"; + + /* We rely on being able to resolve this symbol for looking up module + * specific internal symbols (multiple modules may define the same symbol): + */ + ki.module_kallsyms_lookup_name = (void *)kallsyms_lookup_name(ks); + if (!ki.module_kallsyms_lookup_name) { + terr("Unable to look up "%s" in kallsyms - maybe interface has changed?", + ks); + return -EINVAL; + } + ks = "kallsyms_lookup_size_offset"; + ki.kallsyms_lookup_size_offset = (void *)kallsyms_lookup_name(ks); + if (!ki.kallsyms_lookup_size_offset) { + terr("Unable to look up "%s" in kallsyms - maybe interface has changed?", + ks); + return -EINVAL; + } + + ktf_debugfs_init(); + ret = ktf_nl_register(); + if (ret) { + terr("Unable to register protocol with netlink"); + ktf_debugfs_cleanup(); + goto failure; + } + + return 0; +failure: + return ret; +} + +static void __exit ktf_exit(void) +{ + ktf_nl_unregister(); + ktf_cleanup(); +} + +/* Generic setup function for client modules */ +void ktf_add_tests(ktf_test_adder f) +{ + f(); +} +EXPORT_SYMBOL(ktf_add_tests); + +/* Support for looking up kernel/module internal symbols to enable testing. + * A NULL mod means either we want the kernel-internal symbol or don't care + * which module the symbol is in. + */ +void *ktf_find_symbol(const char *mod, const char *sym) +{ + char sm[200]; + const char *symref; + unsigned long addr = 0; + + if (mod) { + sprintf(sm, "%s:%s", mod, sym); + symref = sm; + } else { + /* Try for kernel-internal symbol first; fall back to modules + * if that fails. + */ + symref = sym; + addr = kallsyms_lookup_name(symref); + } + if (!addr) + addr = ki.module_kallsyms_lookup_name(symref); + if (addr) { + tlog(T_DEBUG, "Found %s at %0lx\n", sym, addr); + } else { +#ifndef CONFIG_KALLSYMS_ALL + twarn("CONFIG_KALLSYMS_ALL is not set, so non-exported symbols are not available\n"); +#endif + tlog(T_INFO, "Fatal error: %s not found\n", sym); + return NULL; + } + return (void *)addr; +} +EXPORT_SYMBOL(ktf_find_symbol); + +unsigned long ktf_symbol_size(unsigned long addr) +{ + unsigned long size = 0; + + (void)ki.kallsyms_lookup_size_offset(addr, &size, NULL); + + return size; +} +EXPORT_SYMBOL(ktf_symbol_size); + +module_init(ktf_init); +module_exit(ktf_exit); diff --git a/tools/testing/selftests/ktf/kernel/ktf_test.c b/tools/testing/selftests/ktf/kernel/ktf_test.c new file mode 100644 index 0000000..1e287d0 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_test.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_test.c: Kernel side code for tracking and reporting ktf test results + */ +#include <linux/module.h> +#include <linux/time.h> +#include "ktf_test.h" +#include <net/netlink.h> +#include <net/genetlink.h> + +#include "ktf_nl.h" +#include "ktf_unlproto.h" +#include "ktf.h" +#include "ktf_cov.h" +#include "ktf_debugfs.h" + +#define MAX_PRINTF 4096 + +/* Versioning check: + * For MAJOR or MINOR changes, both sides are required to + * have the same version. + * If MICRO has changed, some new functionality may have been added, but the + * old functionality should work as before. + * With only BUILD changes, the two versions are still compatible, + * but one might have bug fixes or minor enhancements. + */ +int ktf_version_check(u64 version) +{ + if (version != KTF_VERSION_LATEST) { + if (KTF_VERSION(MAJOR, version) == KTF_VERSION(MAJOR, KTF_VERSION_LATEST) && + KTF_VERSION(MINOR, version) == KTF_VERSION(MINOR, KTF_VERSION_LATEST)) + return 0; + terr("KTF version mismatch - expected %llu.%llu.%llu.%llu, got %llu.%llu.%llu.%llu", + KTF_VERSION(MAJOR, KTF_VERSION_LATEST), + KTF_VERSION(MINOR, KTF_VERSION_LATEST), + KTF_VERSION(MICRO, KTF_VERSION_LATEST), + KTF_VERSION(BUILD, KTF_VERSION_LATEST), + KTF_VERSION(MAJOR, version), + KTF_VERSION(MINOR, version), + KTF_VERSION(MICRO, version), + KTF_VERSION(BUILD, version)); + return -EINVAL; + } + return 0; +} + +static int ktf_handle_version_check(struct ktf_handle *th) +{ + return ktf_version_check(th->version); +} + +/* Function called when global references to test case reach 0. */ +static void ktf_case_free(struct ktf_map_elem *elem) +{ + struct ktf_case *tc = container_of(elem, struct ktf_case, kmap); + + kfree(tc); +} + +void ktf_case_get(struct ktf_case *tc) +{ + ktf_map_elem_get(&tc->kmap); +} + +void ktf_case_put(struct ktf_case *tc) +{ + ktf_map_elem_put(&tc->kmap); +} + +/* The global map from name to ktf_case */ +DEFINE_KTF_MAP(test_cases, NULL, ktf_case_free); + +/* a lock to protect this datastructure */ +static DEFINE_MUTEX(tc_lock); + +/* Current total number of test cases defined */ +size_t ktf_case_count(void) +{ + return ktf_map_size(&test_cases); +} + +const char *ktf_case_name(struct ktf_case *tc) +{ + return tc->kmap.key; +} + +static size_t ktf_case_test_count(struct ktf_case *tc) +{ + return ktf_map_size(&tc->tests); +} + +/* Called when test refcount reaches 0. */ +static void ktf_test_free(struct ktf_map_elem *elem) +{ + struct ktf_test *t = container_of(elem, struct ktf_test, kmap); + + kfree(t->log); + kfree(t); +} + +void ktf_test_get(struct ktf_test *t) +{ + ktf_map_elem_get(&t->kmap); +} + +void ktf_test_put(struct ktf_test *t) +{ + ktf_map_elem_put(&t->kmap); +} + +static struct ktf_case *ktf_case_create(const char *name) +{ + struct ktf_case *tc = kmalloc(sizeof(*tc), GFP_KERNEL); + int ret; + + if (!tc) + return tc; + + /* Initialize test case map of tests. */ + ktf_map_init(&tc->tests, NULL, ktf_test_free); + ret = ktf_map_elem_init(&tc->kmap, name); + if (ret) { + kfree(tc); + return NULL; + } + ktf_debugfs_create_testset(tc); + tlog(T_DEBUG, "ktf: Added test set %s\n", name); + return tc; +} + +struct ktf_case *ktf_case_find(const char *name) +{ + return ktf_map_find_entry(&test_cases, name, struct ktf_case, kmap); +} + +/* Returns with case refcount increased. Called with tc_lock held. */ +static struct ktf_case *ktf_case_find_create(const char *name) +{ + struct ktf_case *tc; + int ret = 0; + + tc = ktf_case_find(name); + if (!tc) { + tc = ktf_case_create(name); + if (tc) { + ret = ktf_map_insert(&test_cases, &tc->kmap); + if (ret) { + kfree(tc); + tc = NULL; + } + } + } + return tc; +} + +static atomic_t assert_cnt = ATOMIC_INIT(0); + +void flush_assert_cnt(struct ktf_test *self) +{ + if (atomic_read(&assert_cnt)) { + tlog(T_DEBUG, "update: %d asserts", atomic_read(&assert_cnt)); + if (self->skb) + nla_put_u32(self->skb, KTF_A_STAT, atomic_read(&assert_cnt)); + atomic_set(&assert_cnt, 0); + } +} + +u32 ktf_get_assertion_count(void) +{ + return atomic_read(&assert_cnt); +} +EXPORT_SYMBOL(ktf_get_assertion_count); + +static DEFINE_SPINLOCK(assert_lock); + +long _ktf_assert(struct ktf_test *self, int result, const char *file, + int line, const char *fmt, ...) +{ + int len; + va_list ap; + char *buf; + char bufprefix[256]; + unsigned long flags; + + if (result) { + atomic_inc(&assert_cnt); + } else { + flush_assert_cnt(self); + buf = kmalloc(MAX_PRINTF, GFP_KERNEL); + if (!buf) { + terr("file %s line %d: Unable to allocate memory for the error report!", + file, line); + goto out; + } + va_start(ap, fmt); + len = vsnprintf(buf, MAX_PRINTF - 1, fmt, ap); + buf[len] = 0; + va_end(ap); + if (self->skb) { + nla_put_u32(self->skb, KTF_A_STAT, result); + nla_put_string(self->skb, KTF_A_FILE, file); + nla_put_u32(self->skb, KTF_A_NUM, line); + nla_put_string(self->skb, KTF_A_STR, buf); + } + (void)snprintf(bufprefix, sizeof(bufprefix) - 1, + "file %s line %d: result %d: ", file, line, + result); + terr("%s%s", bufprefix, buf); + + /* Multiple threads may try to update log */ + spin_lock_irqsave(&assert_lock, flags); + (void)strncat(self->log, bufprefix, KTF_MAX_LOG); + (void)strncat(self->log, buf, KTF_MAX_LOG); + spin_unlock_irqrestore(&assert_lock, flags); + kfree(buf); + } +out: + return result; +} +EXPORT_SYMBOL(_ktf_assert); + +/* Add a test to a testcase: + * Tests are represented by ktf_test objects that are linked into + * a per-test case map TCase:tests map. + */ +void _ktf_add_test(struct __test_desc td, struct ktf_handle *th, + int _signal, int allowed_exit_value, + int start, int end) +{ + struct ktf_case *tc = NULL; + struct ktf_test *t; + char *log; + + if (ktf_handle_version_check(th)) + return; + + log = kzalloc(KTF_MAX_LOG, GFP_KERNEL); + if (!log) + return; + + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) { + kfree(log); + return; + } + t->tclass = td.tclass; + t->name = td.name; + t->fun = td.fun; + t->start = start; + t->end = end; + t->handle = th; + t->log = log; + + mutex_lock(&tc_lock); + tc = ktf_case_find_create(td.tclass); + if (!tc || ktf_map_elem_init(&t->kmap, td.name) || + ktf_map_insert(&tc->tests, &t->kmap)) { + terr("Failed to add test %s from %s to test case "%s"", + td.name, td.file, td.tclass); + if (tc) + ktf_case_put(tc); + mutex_unlock(&tc_lock); + kfree(log); + kfree(t); + return; + } + + ktf_debugfs_create_test(t); + + tlog(T_LIST, "Added test "%s.%s" start = %d, end = %d\n", + td.tclass, td.name, start, end); + + /* Now since we no longer reference tc/t outside of global map of test + * cases and per-testcase map of tests, drop their refcounts. This + * is safe to do as refcounts are > 0 due to references for map + * storage and debugfs. + */ + ktf_test_put(t); + ktf_case_put(tc); + mutex_unlock(&tc_lock); +} +EXPORT_SYMBOL(_ktf_add_test); + +void ktf_run_hook(struct sk_buff *skb, struct ktf_context *ctx, + struct ktf_test *t, u32 value, + void *oob_data, size_t oob_data_sz) +{ + int i; + + t->log[0] = '\0'; + t->skb = skb; + t->data = oob_data; + t->data_sz = oob_data_sz; + for (i = t->start; i < t->end; i++) { + if (!ctx && t->handle->require_context) { + terr("Test %s.%s requires a context, but none configured!", + t->tclass, t->name); + continue; + } + /* No need to bump refcnt, this is just for debugging. Nothing + * should reference the testcase via the handle's current test + * pointer. + */ + t->handle->current_test = t; + tlogs(T_DEBUG, + printk(KERN_INFO "Running test %s.%s", t->tclass, t->name); + if (ctx) + printk("_%s", ktf_context_name(ctx)); + printk("[%d:%d]\n", t->start, t->end); + ); + getnstimeofday(&t->lastrun); + t->fun(t, ctx, i, value); + flush_assert_cnt(t); + } + t->handle->current_test = NULL; +} + +/* Clean up all tests associated with a ktf_handle */ + +void ktf_test_cleanup(struct ktf_handle *th) +{ + struct ktf_test *t; + struct ktf_case *tc; + + /* Clean up tests which are associated with this handle. + * It's possible multiple modules contribute tests to a test case, + * so we can't just do this on a per-testcase basis. + */ + mutex_lock(&tc_lock); + + tc = ktf_map_first_entry(&test_cases, struct ktf_case, kmap); + while (tc) { + /* FIXME - this is inefficient. */ + t = ktf_map_first_entry(&tc->tests, struct ktf_test, kmap); + while (t) { + if (t->handle == th) { + tlog(T_DEBUG, "ktf: delete test %s.%s", + t->tclass, t->name); + /* removes ref for debugfs */ + ktf_debugfs_destroy_test(t); + /* removes ref for testset map of tests */ + ktf_map_remove_elem(&tc->tests, &t->kmap); + /* now remove our reference which we get + * from ktf_map_[first|next]_entry(). + * This final reference should result in + * the test being freed. + */ + ktf_test_put(t); + /* Need to reset to root */ + t = ktf_map_first_entry(&tc->tests, + struct ktf_test, kmap); + } else { + t = ktf_map_next_entry(t, kmap); + } + } + /* If no modules have tests for this test case, we can + * free resources safely. + */ + if (ktf_case_test_count(tc) == 0) { + ktf_debugfs_destroy_testset(tc); + ktf_map_remove_elem(&test_cases, &tc->kmap); + ktf_case_put(tc); + tc = ktf_map_first_entry(&test_cases, struct ktf_case, + kmap); + } else { + tc = ktf_map_next_entry(tc, kmap); + } + } + mutex_unlock(&tc_lock); +} +EXPORT_SYMBOL(ktf_test_cleanup); + +int ktf_cleanup(void) +{ + struct ktf_test *t; + struct ktf_case *tc; + + ktf_cov_cleanup(); + + /* Unloading of dependencies means we should have no testcases/tests. */ + mutex_lock(&tc_lock); + ktf_for_each_testcase(tc) { + twarn("(memory leak) test set %s still active at unload!", ktf_case_name(tc)); + ktf_testcase_for_each_test(t, tc) { + twarn("(memory leak) test set %s still active with test %s at unload!", + ktf_case_name(tc), t->name); + } + return -EBUSY; + } + ktf_debugfs_cleanup(); + mutex_unlock(&tc_lock); + return 0; +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_test.h b/tools/testing/selftests/ktf/kernel/ktf_test.h new file mode 100644 index 0000000..9769664 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_test.h @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2001, 2002, Arien Malec + * Copyright (C) 2011, 2017, Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.1 + * + * This file originates from check.h from the Check C unit test + * framework, adapted by Knut Omang to build with the linux kernel. + */ + +#ifndef KTF_TEST_H +#define KTF_TEST_H + +#include <net/netlink.h> +#include <linux/version.h> +#include "ktf_map.h" +#include "ktf_unlproto.h" + +/* A test context is an extendable object that a test client module + * can supply, and that all tests will be invoked with as an implicit + * 'ctx' argument: + */ +struct ktf_context; + +struct ktf_test; + +typedef void (*ktf_test_fun) (struct ktf_test *, struct ktf_context* tdev, int, u32); + +struct ktf_debugfs { + struct dentry *debugfs_results_testset; + struct dentry *debugfs_results_test; + struct dentry *debugfs_run_testset; + struct dentry *debugfs_run_test; +}; + +struct ktf_test { + struct ktf_map_elem kmap; /* linkage for test case list */ + const char* tclass; /* test class name */ + const char* name; /* Name of the test */ + ktf_test_fun fun; + int start; /* Start and end value to argument to fun */ + int end; /* Defines number of iterations */ + struct sk_buff *skb; /* sk_buff for recording assertion results */ + char *log; /* per-test log */ + void *data; /* Test specific out-of-band data */ + size_t data_sz; /* Size of the data element, if set */ + struct timespec lastrun; /* last time test was run */ + struct ktf_debugfs debugfs; /* debugfs info for test */ + struct ktf_handle *handle; /* Handler for owning module */ +}; + +struct ktf_case { + struct ktf_map_elem kmap; /* Linkage for ktf_map */ + struct ktf_map tests; /* List of tests to run */ + struct ktf_debugfs debugfs; /* debugfs handles for testset */ +}; + +/* Used for tests that spawn kthreads to pass state. We should probably + * look at passing data to tests like this to make things more extensible, + * but will defer for now as this would disrupt KTF consumers. + */ +struct ktf_test_state { + struct ktf_test *self; + struct ktf_context *ctx; + int iter; + u32 value; +}; + +extern struct ktf_map test_cases; + +/* Current total number of test cases defined */ +size_t ktf_case_count(void); +const char *ktf_case_name(struct ktf_case *); +/* Manage test case refcount. */ +void ktf_case_get(struct ktf_case *); +void ktf_case_put(struct ktf_case *); + +int ktf_version_check(u64 version); + +void ktf_run_hook(struct sk_buff *skb, struct ktf_context *ctx, + struct ktf_test *t, u32 value, + void *oob_data, size_t oob_data_sz); +void flush_assert_cnt(struct ktf_test *self); + +/* Representation of a test case (a group of tests) */ +struct ktf_case; + +struct ktf_case *ktf_case_find(const char *name); + +/* Each module client of the test framework is required to + * declare at least one ktf_handle via the macro + * DECLARE_KTF_HANDLE (below) + * If the module require extra data of some sorts, that + * can be embedded within the handle + */ +struct ktf_handle; + +/* Find the handle associated with handle id hid */ +struct ktf_handle *ktf_handle_find(int hid); + +/* Called upon ktf unload to clean up test cases */ +int ktf_cleanup(void); + +/* The list of handles that have contexts associated with them */ +extern struct list_head context_handles; + +struct __test_desc +{ + const char* tclass; /* Test class name */ + const char* name; /* Test name */ + const char* file; /* File that implements test */ + ktf_test_fun fun; +}; + +/* Manage refcount for tests. */ +void ktf_test_get(struct ktf_test *t); +void ktf_test_put(struct ktf_test *t); + +/* Add a test function to a test case for a given handle (macro version) */ +#define ktf_add_test_to(td, __test_handle) \ + _ktf_add_test(td##_setup, &__test_handle, 0, 0, 0, 1) + +/* Add a test function to a test case (macro version) */ +#define ktf_add_test(td) \ + _ktf_add_test(td##_setup, &__test_handle, 0, 0, 0, 1) + +/* Add a looping test function to a test case (macro version) + + The test will be called in a for(i = s; i < e; i++) loop with each + iteration being executed in a new context. The loop variable 'i' is + available in the test. + */ +#define ktf_add_loop_test(td,s,e) \ + _ktf_add_test(td##_setup, &__test_handle, 0,0,(s),(e)) + +/* Add a test function to a test case + (function version -- use this when the macro won't work +*/ +void _ktf_add_test(struct __test_desc td, struct ktf_handle *th, + int _signal, int allowed_exit_value, int start, int end); + +/* Internal function to mark the start of a test function */ +void ktf_fn_start (const char *fname, const char *file, int line); + +/* Add a test previously created with TEST() or TEST_F() */ +#define ADD_TEST(__testname)\ + ktf_add_test(__testname) + +#define ADD_TEST_TO(__handle, __testname) \ + ktf_add_test_to(__testname, __handle) + +#define ADD_LOOP_TEST(__testname, from, to) \ + ktf_add_loop_test(__testname, from, to) + +/* Remove a test previously added with ADD_TEST */ +#define DEL_TEST(__testname)\ + ktf_del_test(__testname) + +/* Iterate over all test cases. Implicitly bumps refcount for pos and + * decreases it after we iterate past it. + */ +#define ktf_for_each_testcase(pos) \ + ktf_map_for_each_entry(pos, &test_cases, kmap) + +/* Iterate over all tests for testcases. Implicitly bumps refcount for pos + * and decreases it again after we iterate past it. + */ +#define ktf_testcase_for_each_test(pos, tc) \ + ktf_map_for_each_entry(pos, &tc->tests, kmap) + +#define KTF_GEN_TYPEID_MAX 3 + +/* A test_handle identifies the calling module: + * Declare one in the module global scope using + * KTF_INIT() or KTF_HANDLE_INIT() + * and call KTF_CLEANUP() or KTF_HANDLE_CLEANUP() upon unload + */ + +struct ktf_handle { + struct list_head handle_list; /* Linkage for the global list of all handles with context */ + struct ktf_map ctx_type_map; /* a map from type_id to ktf_context_type (see ktf_context.c) */ + struct ktf_map ctx_map; /* a (possibly empty) map from name to context for this handle */ + unsigned int id; /* A unique nonzero ID for this handle, set iff contexts */ + bool require_context; /* If set, tests are only valid if a context is provided */ + u64 version; /* version assoc. with handle */ + struct ktf_test *current_test;/* Current test running */ +}; + +void ktf_test_cleanup(struct ktf_handle *th); +void ktf_handle_cleanup_check(struct ktf_handle *handle); +void ktf_cleanup_check(void); + +#define KTF_HANDLE_INIT_VERSION(__test_handle, __version, __need_ctx) \ + struct ktf_handle __test_handle = { \ + .handle_list = LIST_HEAD_INIT(__test_handle.handle_list), \ + .ctx_type_map = __KTF_MAP_INITIALIZER(__test_handle, NULL, NULL), \ + .ctx_map = __KTF_MAP_INITIALIZER(__test_handle, NULL, NULL), \ + .id = 0, \ + .require_context = __need_ctx, \ + .version = __version, \ + }; + +#define KTF_HANDLE_INIT(__test_handle) \ + KTF_HANDLE_INIT_VERSION(__test_handle, KTF_VERSION_LATEST, false) + +#define KTF_INIT() KTF_HANDLE_INIT(__test_handle) + +#define KTF_HANDLE_CTX_INIT(__test_handle) \ + KTF_HANDLE_INIT_VERSION(__test_handle, KTF_VERSION_LATEST, true) + +#define KTF_CTX_INIT() KTF_HANDLE_CTX_INIT(__test_handle) + +#define KTF_HANDLE_CLEANUP(__test_handle) \ + do { \ + ktf_context_remove_all(&__test_handle); \ + ktf_test_cleanup(&__test_handle); \ + } while (0) + +#define KTF_CLEANUP() KTF_HANDLE_CLEANUP(__test_handle) + +/* Start a unit test with TEST(suite_name,unit_name) +*/ +#define TEST(__testsuite, __testname)\ + static void __testname(struct ktf_test *self, struct ktf_context *ctx, \ + int _i, u32 _value); \ + struct __test_desc __testname##_setup = \ + { .tclass = "" # __testsuite "", .name = "" # __testname "",\ + .fun = __testname, .file = __FILE__ }; \ + \ + static void __testname(struct ktf_test *self, struct ktf_context* ctx, \ + int _i, u32 _value) + +/* Start a unit test using a fixture + * NB! Note the intentionally missing start parenthesis on DECLARE_F! + * + * Prep: + * DECLARE_F(fixture_name) + * <attributes> + * }; + * INIT_F(fixture_name,setup,teardown); + * + * Usage: + * SETUP_F(fixture_name,setup) + * { + * <setup code, set fixture_name->ok to true to have the test executed> + * } + * TEARDOWN_F(fixture_name,teardown) + * { + * <teardown code> + * } + * TEST_F(fixture_name,suite_name,test_name) + * { + * <test code> + * } + * + * setup must set ctx->ok to true to have the test itself executed + */ +#define DECLARE_F(__fixture) \ + struct __fixture { \ + void (*setup) (struct ktf_test *, struct ktf_context *, struct __fixture *); \ + void (*teardown) (struct ktf_test *, struct __fixture *); \ + bool ok; + +#define INIT_F(__fixture,__setup,__teardown) \ + void __setup(struct ktf_test *, struct ktf_context *, struct __fixture *); \ + void __teardown(struct ktf_test *, struct __fixture *); \ + static struct __fixture __fixture##_template = {\ + .setup = __setup, \ + .teardown = __teardown, \ + .ok = false,\ + } + +#define SETUP_F(__fixture, __setup) \ + void __setup(struct ktf_test *self, struct ktf_context *ctx, \ + struct __fixture *__fixture) + +#define TEARDOWN_F(__fixture, __teardown) \ + void __teardown(struct ktf_test *self, struct __fixture *__fixture) + +#define TEST_F(__fixture, __testsuite, __testname) \ + static void __testname##_body(struct ktf_test *, struct __fixture *, \ + int, u32); \ + static void __testname(struct ktf_test *, struct ktf_context *, int, \ + u32); \ + struct __test_desc __testname##_setup = \ + { .tclass = "" # __testsuite "", .name = "" # __testname "", \ + .fun = __testname }; \ + \ + static void __testname(struct ktf_test *self, struct ktf_context* ctx, \ + int _i, u32 _value) \ + { \ + struct __fixture f_ctx = __fixture##_template; \ + f_ctx.ok = false; \ + f_ctx.setup(self, ctx, &f_ctx); \ + if (!f_ctx.ok) return; \ + __testname##_body(self, &f_ctx, _i, _value); \ + f_ctx.teardown(self,&f_ctx); \ + } \ + static void __testname##_body(struct ktf_test *self, struct __fixture *ctx, \ + int _i, u32 _value) + +/* Fail the test case unless expr is true */ +/* The space before the comma sign before ## is essential to be compatible + with gcc 2.95.3 and earlier. +*/ +#define ktf_assert_msg(expr, format, ...) \ + _ktf_assert(self, expr, __FILE__, __LINE__, \ + format , ## __VA_ARGS__, NULL) + +#define ktf_assert(expr, ...)\ + _ktf_assert(self, expr, __FILE__, __LINE__, \ + "Failure '"#expr"' occurred " , ## __VA_ARGS__, NULL) + +/* Always fail */ +#define ktf_fail(...) _ktf_assert(self, 0, __FILE__, __LINE__, "Failed" , ## __VA_ARGS__, NULL) + +/* Non-macro version of ktf_assert, with more complicated interface + * returns nonzero if ok, 0 otherwise + */ +long _ktf_assert (struct ktf_test *self, int result, const char *file, + int line, const char *expr, ...); + +/* Integer comparsion macros with improved output compared to ktf_assert(). */ +/* O may be any comparion operator. */ +#define ktf_assert_int_goto(X, O, Y, _lbl) \ + do { int x = (X); int y = (Y);\ + if (!ktf_assert_msg(x O y, \ + "Assertion '"#X#O#Y"' failed: "#X"==0x%x, "#Y"==0x%x", x, y)) \ + goto _lbl;\ + } while (0) + +#define ktf_assert_int(X, O, Y) \ + do { int x = (X); int y = (Y);\ + ktf_assert_msg(x O y,\ + "Assertion '"#X#O#Y"' failed: "#X"==0x%x, "#Y"==0x%x", x, y);\ + } while (0) + +#define ktf_assert_int_ret(X, O, Y)\ + do { int x = (X); int y = (Y);\ + if (!ktf_assert_msg(x O y, \ + "Assertion '"#X#O#Y"' failed: "#X"==0x%lx, "#Y"==0x%lx", x, y)) \ + return;\ + } while (0) + +#define ktf_assert_long_goto(X, O, Y, _lbl) \ + do { long x = (X); long y = (Y);\ + if (!ktf_assert_msg(x O y, \ + "Assertion '"#X#O#Y"' failed: "#X"==0x%lx, "#Y"==0x%lx", x, y)) \ + goto _lbl;\ + } while (0) + +#define ktf_assert_long_ret(X, O, Y)\ + do { long x = (X); long y = (Y);\ + if (!ktf_assert_msg(x O y, \ + "Assertion '"#X#O#Y"' failed: "#X"==0x%lx, "#Y"==0x%lx", x, y)) \ + return;\ + } while (0) + +/* O may be any comparion operator. */ +#define ktf_assert_long(X, O, Y) \ + do { long x = (X); long y = (Y);\ + ktf_assert_msg(x O y,\ + "Assertion '"#X#O#Y"' failed: "#X"==0x%lx, "#Y"==0x%lx", x, y);\ + } while (0) + +/* String comparsion macros with improved output compared to ktf_assert() */ +#define ktf_assert_str_eq(X, Y) \ + do { const char* x = (X); const char* y = (Y);\ + ktf_assert_msg(strcmp(x,y) == 0,\ + "Assertion '"#X"=="#Y"' failed: "#X"=="%s", "#Y"=="%s"",\ + x, y);\ + } while (0) + +#define ktf_assert_str_ne(X, Y) \ + do { const char* x = (X); const char* y = (Y);\ + ktf_assert_msg(strcmp(x,y) != 0,\ + "Assertion '"#X"!="#Y"' failed: "#X"=="%s", "#Y"=="%s"",\ + x, y);\ + } while (0) + +#endif /* KTF_TEST_H */
On Tue, Aug 13, 2019 at 08:09:17AM +0200, Knut Omang wrote:
Sorry, it's taken me way too long to get down to a proper code review on this. I was hoping to send you something a couple weeks ago in preparation for Tuesday, but I have been crazy busy.
The ktf module itself and basic data structures for management of test cases and tests and contexts for tests. Also contains the top level include file for kernel clients in ktf.h.
More elaborate documentation follows towards the end of the patch set.
This patch set contains both user level and kernel code, we'll provide the full implementation of ktf on the kernel side in this and forthcoming patches, then the user space code to execute tests within the kernel and report results, then documentation before introducing a small self test suite of tests to test ktf itself, and some very simple additional example tests.
ktf.h: Defines the KTF user API for kernel clients ktf_test.c: Kernel side code for tracking and reporting ktf test results
Signed-off-by: Knut Omang knut.omang@oracle.com
tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++++++++- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++++++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++++++++- 5 files changed, 1806 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/Makefile create mode 100644 tools/testing/selftests/ktf/kernel/ktf.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_context.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.h
[...]
diff --git a/tools/testing/selftests/ktf/kernel/ktf.h b/tools/testing/selftests/ktf/kernel/ktf.h new file mode 100644 index 0000000..ea270e7 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf.h @@ -0,0 +1,604 @@ +/*
- Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
- Author: Knut Omang knut.omang@oracle.com
- SPDX-License-Identifier: GPL-2.0
- ktf.h: Defines the KTF user API for kernel clients
- */
+#ifndef _KTF_H +#define _KTF_H
+#include <linux/completion.h> +#include <linux/kprobes.h> +#include <linux/kthread.h> +#include <linux/ptrace.h> +#include "ktf_test.h" +#include "ktf_override.h" +#include "ktf_map.h"
Where do you add this file? I don't see any definitions of `struct ktf_map` in either this or the preceding patches, so I don't think that this will compile.
+#include "ktf_unlproto.h"
Same here. This looks important for understanding what you presented here.
+#define KTF_MAX_LOG 2048
+/* Type for an optional configuration callback for contexts.
- Implementations should copy and store data into their private
- extensions of the context structure. The data pointer is
- only valid inside the callback:
- */
+typedef int (*ktf_config_cb)(struct ktf_context *ctx, const void* data, size_t data_sz); +typedef void (*ktf_context_cb)(struct ktf_context *ctx);
+struct ktf_context_type;
+struct ktf_context {
- struct ktf_map_elem elem; /* Linkage for ctx_map in handle */
- char name[KTF_MAX_KEY]; /* Context name used in map */
- struct ktf_handle *handle; /* Owner of this context */
- ktf_config_cb config_cb; /* Optional configuration callback */
- ktf_context_cb cleanup; /* Optional callback upon context release */
- int config_errno; /* If config_cb set: state of configuration */
- struct ktf_context_type *type; /* Associated type, must be set */
+};
+typedef struct ktf_context* (*ktf_context_alloc)(struct ktf_context_type *ct);
+struct ktf_context_type {
- struct ktf_map_elem elem; /* Linkage for map in handle */
- char name[KTF_MAX_KEY]; /* Context type name */
- struct ktf_handle *handle; /* Owner of this context type */
- ktf_context_alloc alloc; /* Allocate a new context of this type */
- ktf_config_cb config_cb; /* Configuration callback */
- ktf_context_cb cleanup; /* Optional callback upon context release */
+};
+#include "ktf_netctx.h"
+/* type for a test function */ +struct ktf_test;
Okay, so a `struct ktf_test` (I know you define it later in the patch); it seems like it correlates to a `struct kunit_case` in KUnit, correct? It represents the test to be run, kind of analogous to a `struct device_driver`, correct?
+/* state of running test, used to pass to threads spawned by test. */ +struct ktf_test_state;
+struct ktf_thread {
- int (*func)(void *);
- const char *name;
- struct task_struct *task;
- struct ktf_test_state state;
- struct completion started;
- struct completion completed;
+};
So that makes this analogous to `struct kunit` or `struct device` to keep the above analogy going, right?
+typedef void (*ktf_test_adder)(void);
+/* Generic setup function for client modules */ +void ktf_add_tests(ktf_test_adder f); +int ktf_context_add(struct ktf_handle *handle, struct ktf_context* ctx,
const char* name, ktf_config_cb cfg_cb, const char *type);
+struct ktf_context *ktf_context_add_from(struct ktf_handle *handle, const char *name,
struct ktf_context_type *ct);
+const char *ktf_context_name(struct ktf_context *ctx); +struct ktf_context* ktf_find_context(struct ktf_handle *handle, const char* name); +struct ktf_context *ktf_find_first_context(struct ktf_handle *handle); +struct ktf_context *ktf_find_next_context(struct ktf_context* ctx); +void ktf_context_remove(struct ktf_context *ctx); +size_t ktf_has_contexts(struct ktf_handle *handle); +void ktf_context_remove_all(struct ktf_handle *handle);
+/* Called by framework when a configuration is supplied,
- returns the return value of the configuration callback.
- */
+int ktf_context_set_config(struct ktf_context *ctx, const void* data, size_t data_sz);
+struct ktf_context *ktf_find_create_context(struct ktf_handle *handle, const char *name,
const char *type_name);
+int ktf_handle_add_ctx_type(struct ktf_handle *handle, struct ktf_context_type *ct); +struct ktf_context_type *ktf_handle_get_ctx_type(struct ktf_handle *handle,
const char *type_name);
+/* Declare the implicit __test_handle as extern for .c files that use it
- when adding tests with ADD_TEST but where definition is in another .c file:
- */
+extern struct ktf_handle __test_handle;
+/* Add/remove/find a context to/from the default handle */
nit: I think adding some vertile space to some of these sections might make them a little easier to read.
+#define KTF_CONTEXT_ADD(__context, name) \
- ktf_context_add(&__test_handle, __context, name, NULL, "default")
+#define KTF_CONTEXT_ADD_CFG(__context, name, __cb, __type_name) \
- ktf_context_add(&__test_handle, __context, name, __cb, __type_name)
+#define KTF_CONTEXT_FIND(name) ktf_find_context(&__test_handle, name) +#define KTF_CONTEXT_GET(name, type) \
- container_of(KTF_CONTEXT_FIND(name), type, k)
[...]
+/* For unsupported platforms. */ +#ifndef KTF_PROBE_SUPPORT +#define KTF_SET_RETURN_VALUE(value) do { } while (0) +#endif /* KTF_PROBE_SUPPORT */
+#define KTF_UNREGISTER_RETURN_PROBE(func, probehandler) \
- KTF_UNREGISTER_PROBE(handler, func, probehandler)
+#define KTF_OVERRIDE(func, probehandler) \
- static int probehandler(struct kprobe *, struct pt_regs *);\
- static struct kprobe __ktf_override_##probehandler = { \
.symbol_name = #func, \
.pre_handler = probehandler, \
.post_handler = ktf_post_handler, \
.fault_handler = NULL, \
.flags = 0, \
}; \
static int probehandler(struct kprobe *kp, struct pt_regs *regs)
Oh cool! Does this allow you to mock out/spy on arbitrary functions?
Rob Herring suggested I implement my arbitrary function mocking in terms of kprobe, if I remember correctly. I wonder how easily we could make this work with KUnit.
I don't want to totally derail this discussion by changing the subject, but I am thinking about doing parameter matching and/or C-style class mocking as one of the next major collection of things to upstream. I don't think you have anything comparable to parameter matching, would it help you if I tried to start upstreaming both of those things before/while you work on this?
I guess this is all part of a bigger meta question in what features you want to break out into separate patchsets? This patchset it pretty huge as it is, and I think you are going to want to break it up to make it easier to review. I think you should probably break this initial patch into multiple patches.
+#ifdef KTF_PROBE_SUPPORT +#define KTF_REGISTER_OVERRIDE(func, probehandler) \
- ktf_register_override(&__ktf_override_##probehandler)
+#else +#define KTF_REGISTER_OVERRIDE(func, probehandler) \
- ktf_no_probe_support()
+#endif
+#define KTF_UNREGISTER_OVERRIDE(func, probehandler) \
- do { \
unregister_kprobe(&__ktf_override_##probehandler); \
memset(&__ktf_override_##probehandler, 0, \
sizeof(struct kprobe)); \
__ktf_override_##probehandler.symbol_name = #func; \
__ktf_override_##probehandler.pre_handler = probehandler; \
__ktf_override_##probehandler.post_handler = ktf_post_handler; \
- } while (0)
+#define KTF_OVERRIDE_RETURN \
- do { \
ktf_override_function_with_return(regs); \
return 1; \
- } while (0)
+#ifdef KTF_PROBE_SUPPORT +#define KTF_SET_INSTRUCTION_POINTER(regs, value) \
- instruction_pointer_set(regs, (value))
+#else +#define KTF_SET_INSTRUCTION_POINTER(regs, value) do { } while (0) +#endif
+/* Interfaces for creating kthreads in tests. */ +#define KTF_THREAD_INIT(threadname, t) \
- do { \
(t)->func = threadname; \
(t)->name = #threadname; \
(t)->state.self = self; \
(t)->state.ctx = ctx; \
(t)->state.iter = _i; \
(t)->state.value = _value; \
init_completion(&((t)->started)); \
init_completion(&((t)->completed)); \
- } while (0)
+#define KTF_THREAD_RUN(t) \
- ((t)->task = kthread_run((t)->func, t, (t)->name))
+#define KTF_THREAD_STOP(t) \
- do { \
if ((t)->task) \
kthread_stop((t)->task); \
- } while (0)
+/* Wraps thread execution to supply same variables as test case - this allows
- us to define assertions etc in thread context.
- */
+#define KTF_THREAD(name) \
- static void __##name(struct ktf_thread *thread, struct ktf_test *self, \
struct ktf_context *ctx, int _i, u32 _value); \
- static int name(void *data) \
- { \
struct ktf_thread *t = data; \
complete(&t->started); \
__##name(t, t->state.self, t->state.ctx, t->state.iter, \
t->state.value); \
complete(&t->completed); \
return 0; \
- } \
- static void __##name(struct ktf_thread *_thread, struct ktf_test *self,\
struct ktf_context *ctx, int _i, u32 _value)
+#define KTF_THREAD_WAIT_STARTED(t) (wait_for_completion(&((t)->started))) +#define KTF_THREAD_WAIT_COMPLETED(t) (wait_for_completion(&((t)->completed)))
+u32 ktf_get_assertion_count(void);
+/**
- ASSERT_TRUE() - fail and return if @C evaluates to false
- @C: Boolean expression to evaluate
- */
+#define ASSERT_TRUE(C) do { \
if (!ktf_assert((C))) return; \
- } while (0)
+/**
- ASSERT_FALSE() - fail and return if @C evaluates to true
- @C: Boolean expression to evaluate
- */
+#define ASSERT_FALSE(C) do { \
if (!ktf_assert(!(C))) return; \
- } while (0)
+/**
- ASSERT_TRUE_GOTO() - fail and jump to @_lbl if @C evaluates to false
- @C: Boolean expression to evaluate
- @_lbl: Label to jump to in case of failure
- */
+#define ASSERT_TRUE_GOTO(C,_lbl) { \
- if (!ktf_assert((C))) goto _lbl;\
+}
+/**
- ASSERT_FALSE_GOTO() - fail and jump to @_lbl if @C evaluates to true
- @C: Boolean expression to evaluate
- @_lbl: Label to jump to in case of failure
- */
+#define ASSERT_FALSE_GOTO(C,_lbl) { \
- if (!ktf_assert(!(C))) goto _lbl;\
+}
+/**
- ASSERT_TRUE_RETVAL() - fail and return @V if @C evaluates to false
- @C: Boolean expression to evaluate
- @V: Value to return on failure
- */
+#define ASSERT_TRUE_RETVAL(C, V) do { \
- if (!ktf_assert((C))) return V; \
+} while (0)
+/**
- ASSERT_FALSE() - fail and return @V if @C evaluates to true
- @C: Boolean expression to evaluate
- @V: Value to return on failure
- */
+#define ASSERT_FALSE_RETVAL(C, V) do { \
- if (!ktf_assert(!(C))) return V; \
+} while (0)
+/**
- ASSERT_TRUE_CONT() - fail and continue if @C evaluates to false
- @C: Boolean expression to evaluate
- */
+#define ASSERT_TRUE_CONT(C) { \
- if (!ktf_assert((C))) continue; \
+}
+/**
- ASSERT_FALSE_CONT() - fail and continue if @C evaluates to true
- @C: Boolean expression to evaluate
- */
+#define ASSERT_FALSE_CONT(C) { \
- if (!ktf_assert(!(C))) continue; \
+}
+/**
- ASSERT_TRUE_BREAK() - fail and break if @C evaluates to false
- @C: Boolean expression to evaluate
- */
+#define ASSERT_TRUE_BREAK(C) { \
- if (!ktf_assert((C))) break; \
+}
+/**
- ASSERT_FALSE_BREAK() - fail and break if @C evaluates to true
- @C: Boolean expression to evaluate
- */
+#define ASSERT_FALSE_BREAK(C) { \
- if (!ktf_assert(!(C))) break; \
+}
+/**
- ASSERT_LONG_EQ() - compare two longs, fail and return if @X != @Y
- @X: Expected value
- @Y: Actual value
- */
+#define ASSERT_LONG_EQ(X, Y) \
- ktf_assert_long_ret(X, ==, Y);
+#define ASSERT_LONG_NE(X, Y) \
- ktf_assert_long_ret(X, !=, Y);
+#define ASSERT_ADDR_EQ(X, Y) \
- ktf_assert_long_ret((u64)(X), ==, (u64)(Y));
+#define ASSERT_ADDR_NE(X, Y) \
- ktf_assert_long_ret((u64)(X), !=, (u64)(Y));
+#define ASSERT_INT_EQ(X, Y) \
- ktf_assert_int_ret(X, ==, Y);
+#define ASSERT_INT_GT(X, Y) \
- ktf_assert_int_ret(X, >, Y);
+/**
- ASSERT_LONG_EQ() - compare two longs, jump to @_lbl if @X != @Y
- @X: Expected value
- @Y: Actual value
- @_lbl: Label to jump to in case of failure
- */
+#define ASSERT_LONG_EQ_GOTO(X, Y, _lbl) \
- ktf_assert_long_goto(X, ==, Y, _lbl)
+#define ASSERT_LONG_NE_GOTO(X, Y, _lbl) \
- ktf_assert_long_goto(X, !=, Y, _lbl)
+#define ASSERT_ADDR_EQ_GOTO(X, Y, _lbl) \
- ktf_assert_long_goto((u64)(X), ==, (u64)(Y), _lbl)
+#define ASSERT_ADDR_NE_GOTO(X, Y, _lbl) \
- ktf_assert_long_goto((u64)(X), !=, (u64)(Y), _lbl)
+#define ASSERT_INT_EQ_GOTO(X, Y, _lbl) \
- ktf_assert_int_goto(X, ==, Y, _lbl)
+#define ASSERT_INT_GE_GOTO(X, Y, _lbl) \
- ktf_assert_int_goto(X, >=, Y, _lbl)
+#define ASSERT_INT_GT_GOTO(X, Y, _lbl) \
- ktf_assert_int_goto(X, >, Y, _lbl)
+#define ASSERT_INT_LT_GOTO(X, Y, _lbl) \
- ktf_assert_int_goto(X, <, Y, _lbl)
+#define ASSERT_INT_NE(X,Y) \
- ktf_assert_int_ret(X, !=, Y);
+#define ASSERT_INT_NE_GOTO(X,Y,_lbl) \
- ktf_assert_int_goto(X, !=, Y, _lbl);
+/**
- EXPECT_TRUE() - fail if @C evaluates to false but allow test to continue
- @C: Boolean expression to evaluate
- */
+#define EXPECT_TRUE(C) ktf_assert(C) +#define EXPECT_FALSE(C) ktf_assert(!(C))
+#define OK_ADDR(X) (X && !IS_ERR(X))
+/* Valid kernel address check */ +#define EXPECT_OK_ADDR(X) \
- ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X))
+#define ASSERT_OK_ADDR(X) do { \
if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - value 0x%Lx", (X))) \
return; \
- } while (0)
+#define ASSERT_OK_ADDR_GOTO(X,_lbl) do { \
if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X))) \
goto _lbl; \
- } while (0)
+#define ASSERT_OK_ADDR_BREAK(X) do { \
- if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X))) \
break; \
- } while (0)
+#define EXPECT_INT_EQ(X,Y) ktf_assert_int(X, ==, Y) +#define EXPECT_INT_GT(X,Y) ktf_assert_int(X, >, Y) +#define EXPECT_INT_GE(X,Y) ktf_assert_int(X, >=, Y) +#define EXPECT_INT_LE(X,Y) ktf_assert_int(X, <=, Y) +#define EXPECT_INT_LT(X,Y) ktf_assert_int(X, <, Y) +#define EXPECT_INT_NE(X,Y) ktf_assert_int(X, !=, Y)
+#define EXPECT_LONG_EQ(X, Y) ktf_assert_long(X, ==, Y) +#define EXPECT_LONG_NE(X, Y) ktf_assert_long(X, !=, Y) +#define EXPECT_ADDR_EQ(X, Y) ktf_assert_long((u64)(X), ==, (u64)(Y)) +#define EXPECT_ADDR_NE(X, Y) ktf_assert_long((u64)(X), !=, (u64)(Y)) +#define EXPECT_LONG_GT(X, Y) ktf_assert_long(X, >, Y) +#define EXPECT_LONG_GE(X, Y) ktf_assert_long(X, >=, Y) +#define EXPECT_LONG_LE(X, Y) ktf_assert_long(X, <=, Y) +#define EXPECT_LONG_LT(X, Y) ktf_assert_long(X, <, Y)
+#define EXPECT_STREQ(X, Y) ktf_assert_str_eq(X, Y) +#define EXPECT_STRNE(X, Y) ktf_assert_str_ne(X, Y)
+extern ulong ktf_debug_mask;
+/* Defined debug bits - higher values should represent more
- verbose categories:
- */
+#define T_INFO 0x1 +#define T_LIST 0x2 +#define T_INTR 0x200 +#define T_INFO_V 0x800 +#define T_DEBUG 0x1000 +#define T_MCAST 0x2000 +#define T_TRACE 0x100000 +#define T_DEBUG_V 0x200000
+#define tlog(class, format, arg...) \
- do { \
if (unlikely((ktf_debug_mask) & (class))) \
printk(KERN_INFO \
"ktf pid [%d] " "%s: " format "\n", \
current->pid, __func__, \
## arg); \
- } while (0)
+#define twarn(format, arg...) \
- do { \
printk(KERN_WARNING \
"ktf pid [%d] " "%s: " format "\n", \
current->pid, __func__, \
## arg); \
- } while (0)
+#define terr(format, arg...) \
- do { \
printk(KERN_ERR \
"ktf pid [%d] " "%s: " format "\n", \
current->pid, __func__, \
## arg); \
- } while (0)
+#define tlogs(class, stmt_list) \
- do { \
if (unlikely((ktf_debug_mask) & (class))) { \
stmt_list;\
} \
- } while (0)
+/* Look up the current address of a potentially local symbol - to allow testing
- against it. NB! This is a hack for unit testing internal unexposed interfaces and
- violates the module boundaries and has no fw/bw comp gauarantees, but are
- still very useful for detailed unit testing complex logic:
- */
+void* ktf_find_symbol(const char *mod, const char *sym);
+unsigned long ktf_symbol_size(unsigned long addr);
+#define ktf_resolve_symbol(mname, sname) \
- do { \
sname = ktf_find_symbol(#mname, #sname); \
if (!sname) \
return -ENOENT; \
- } while (0)
+#endif
[...]
diff --git a/tools/testing/selftests/ktf/kernel/ktf_test.c b/tools/testing/selftests/ktf/kernel/ktf_test.c new file mode 100644 index 0000000..1e287d0 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_test.c
[...]
+long _ktf_assert(struct ktf_test *self, int result, const char *file,
int line, const char *fmt, ...)
This looks a lot like the KUnit expectation/assertion API.
+{
- int len;
- va_list ap;
- char *buf;
- char bufprefix[256];
- unsigned long flags;
- if (result) {
atomic_inc(&assert_cnt);
- } else {
flush_assert_cnt(self);
buf = kmalloc(MAX_PRINTF, GFP_KERNEL);
if (!buf) {
terr("file %s line %d: Unable to allocate memory for the error report!",
file, line);
goto out;
}
va_start(ap, fmt);
len = vsnprintf(buf, MAX_PRINTF - 1, fmt, ap);
buf[len] = 0;
va_end(ap);
if (self->skb) {
nla_put_u32(self->skb, KTF_A_STAT, result);
nla_put_string(self->skb, KTF_A_FILE, file);
nla_put_u32(self->skb, KTF_A_NUM, line);
nla_put_string(self->skb, KTF_A_STR, buf);
}
(void)snprintf(bufprefix, sizeof(bufprefix) - 1,
"file %s line %d: result %d: ", file, line,
result);
terr("%s%s", bufprefix, buf);
/* Multiple threads may try to update log */
spin_lock_irqsave(&assert_lock, flags);
(void)strncat(self->log, bufprefix, KTF_MAX_LOG);
(void)strncat(self->log, buf, KTF_MAX_LOG);
spin_unlock_irqrestore(&assert_lock, flags);
kfree(buf);
- }
+out:
- return result;
+} +EXPORT_SYMBOL(_ktf_assert);
[...]
diff --git a/tools/testing/selftests/ktf/kernel/ktf_test.h b/tools/testing/selftests/ktf/kernel/ktf_test.h new file mode 100644 index 0000000..9769664 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_test.h @@ -0,0 +1,381 @@ +/*
- Copyright (C) 2001, 2002, Arien Malec
- Copyright (C) 2011, 2017, Oracle and/or its affiliates.
- SPDX-License-Identifier: GPL-2.1
- This file originates from check.h from the Check C unit test
- framework, adapted by Knut Omang to build with the linux kernel.
- */
+#ifndef KTF_TEST_H +#define KTF_TEST_H
+#include <net/netlink.h> +#include <linux/version.h> +#include "ktf_map.h" +#include "ktf_unlproto.h"
+/* A test context is an extendable object that a test client module
- can supply, and that all tests will be invoked with as an implicit
- 'ctx' argument:
- */
+struct ktf_context;
+struct ktf_test;
+typedef void (*ktf_test_fun) (struct ktf_test *, struct ktf_context* tdev, int, u32);
+struct ktf_debugfs {
struct dentry *debugfs_results_testset;
struct dentry *debugfs_results_test;
struct dentry *debugfs_run_testset;
struct dentry *debugfs_run_test;
+};
+struct ktf_test {
- struct ktf_map_elem kmap; /* linkage for test case list */
- const char* tclass; /* test class name */
- const char* name; /* Name of the test */
- ktf_test_fun fun;
- int start; /* Start and end value to argument to fun */
- int end; /* Defines number of iterations */
- struct sk_buff *skb; /* sk_buff for recording assertion results */
- char *log; /* per-test log */
- void *data; /* Test specific out-of-band data */
- size_t data_sz; /* Size of the data element, if set */
- struct timespec lastrun; /* last time test was run */
- struct ktf_debugfs debugfs; /* debugfs info for test */
- struct ktf_handle *handle; /* Handler for owning module */
+};
+struct ktf_case {
- struct ktf_map_elem kmap; /* Linkage for ktf_map */
- struct ktf_map tests; /* List of tests to run */
- struct ktf_debugfs debugfs; /* debugfs handles for testset */
+};
So this seems comparable to a `struct kunit_suite`, does that seem right?
+/* Used for tests that spawn kthreads to pass state. We should probably
- look at passing data to tests like this to make things more extensible,
- but will defer for now as this would disrupt KTF consumers.
- */
+struct ktf_test_state {
- struct ktf_test *self;
- struct ktf_context *ctx;
- int iter;
- u32 value;
+};
[...]
+#endif /* KTF_TEST_H */
git-series 0.9.1
My initial thoughts: at a high level it seems that you have some data structures and some functions which are pretty similiar to what exists in KUnit. I also think this patch is *way* too complicated and is going to need to be broken down into several smaller patches.
I think some book-keeping is done differently, but I think, at the core, how we represent tests inside the kernel is actually very similar. I see a bunch of stuff that you added to support the KTF netlink protocol and the kprobe stuff, but I don't see any reason why you wouldn't be able to add that to the KUnit data structures.
Could you try to see if you can implement your features on top of what is already there in KUnit? I think a good place to start would be getting hybrid tests working. Shuah and I were talking offlist and we think that there is value to providing a mechanism to trigger tests from userspace; it seems you have something that does that, and I think it represents a small portion of what you have here. So maybe that is a good place for you to start.
As for breaking this patch down, several things immediately jump out to me that should be in different patchs: - kprobe stuff - expectations/assertions - hybrid tests - core in-kernel test stuff You might even be able to break it down further, but I think that would be a good start.
In any case, looks like good stuff. I am very keen on the arbitrary function mocking/spying and the hybrid testing stuff in particular.
Cheers!
On Sun, 2019-09-08 at 18:23 -0700, Brendan Higgins wrote:
On Tue, Aug 13, 2019 at 08:09:17AM +0200, Knut Omang wrote:
Sorry, it's taken me way too long to get down to a proper code review on this. I was hoping to send you something a couple weeks ago in preparation for Tuesday, but I have been crazy busy.
The ktf module itself and basic data structures for management of test cases and tests and contexts for tests. Also contains the top level include file for kernel clients in ktf.h.
More elaborate documentation follows towards the end of the patch set.
This patch set contains both user level and kernel code, we'll provide the full implementation of ktf on the kernel side in this and forthcoming patches, then the user space code to execute tests within the kernel and report results, then documentation before introducing a small self test suite of tests to test ktf itself, and some very simple additional example tests.
ktf.h: Defines the KTF user API for kernel clients ktf_test.c: Kernel side code for tracking and reporting ktf test results
Signed-off-by: Knut Omang knut.omang@oracle.com
tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++++++++- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++++++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++++++++- 5 files changed, 1806 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/Makefile create mode 100644 tools/testing/selftests/ktf/kernel/ktf.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_context.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.h
[...]
diff --git a/tools/testing/selftests/ktf/kernel/ktf.h b/tools/testing/selftests/ktf/kernel/ktf.h new file mode 100644 index 0000000..ea270e7 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf.h @@ -0,0 +1,604 @@ +/*
- Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
- Author: Knut Omang knut.omang@oracle.com
- SPDX-License-Identifier: GPL-2.0
- ktf.h: Defines the KTF user API for kernel clients
- */
+#ifndef _KTF_H +#define _KTF_H
+#include <linux/completion.h> +#include <linux/kprobes.h> +#include <linux/kthread.h> +#include <linux/ptrace.h> +#include "ktf_test.h" +#include "ktf_override.h" +#include "ktf_map.h"
Where do you add this file? I don't see any definitions of `struct ktf_map` in either this or the preceding patches, so I don't think that this will compile.
Compiling is not enabled until patch 17, so that should not be a problem. I wanted to convey the core KTF API early in the set to not let it "drown" in the utilities, but I get your point. One way to do it would be to move the header files up front and keep the implementations in the later patches, even though that would separate the API definition from the implementation.
+#include "ktf_unlproto.h"
Same here. This looks important for understanding what you presented here.
yes
Thanks, Knut
+#define KTF_MAX_LOG 2048
+/* Type for an optional configuration callback for contexts.
- Implementations should copy and store data into their private
- extensions of the context structure. The data pointer is
- only valid inside the callback:
- */
+typedef int (*ktf_config_cb)(struct ktf_context *ctx, const void* data, size_t data_sz); +typedef void (*ktf_context_cb)(struct ktf_context *ctx);
+struct ktf_context_type;
+struct ktf_context {
- struct ktf_map_elem elem; /* Linkage for ctx_map in handle */
- char name[KTF_MAX_KEY]; /* Context name used in map */
- struct ktf_handle *handle; /* Owner of this context */
- ktf_config_cb config_cb; /* Optional configuration callback */
- ktf_context_cb cleanup; /* Optional callback upon context release
*/
- int config_errno; /* If config_cb set: state of configuration */
- struct ktf_context_type *type; /* Associated type, must be set */
+};
+typedef struct ktf_context* (*ktf_context_alloc)(struct ktf_context_type *ct);
+struct ktf_context_type {
- struct ktf_map_elem elem; /* Linkage for map in handle */
- char name[KTF_MAX_KEY]; /* Context type name */
- struct ktf_handle *handle; /* Owner of this context type */
- ktf_context_alloc alloc; /* Allocate a new context of this type */
- ktf_config_cb config_cb; /* Configuration callback */
- ktf_context_cb cleanup; /* Optional callback upon context release
*/ +};
+#include "ktf_netctx.h"
+/* type for a test function */ +struct ktf_test;
Okay, so a `struct ktf_test` (I know you define it later in the patch); it seems like it correlates to a `struct kunit_case` in KUnit, correct? It represents the test to be run, kind of analogous to a `struct device_driver`, correct?
+/* state of running test, used to pass to threads spawned by test. */ +struct ktf_test_state;
+struct ktf_thread {
- int (*func)(void *);
- const char *name;
- struct task_struct *task;
- struct ktf_test_state state;
- struct completion started;
- struct completion completed;
+};
So that makes this analogous to `struct kunit` or `struct device` to keep the above analogy going, right?
+typedef void (*ktf_test_adder)(void);
+/* Generic setup function for client modules */ +void ktf_add_tests(ktf_test_adder f); +int ktf_context_add(struct ktf_handle *handle, struct ktf_context* ctx,
const char* name, ktf_config_cb cfg_cb, const char *type);
+struct ktf_context *ktf_context_add_from(struct ktf_handle *handle, const char *name,
struct ktf_context_type *ct);
+const char *ktf_context_name(struct ktf_context *ctx); +struct ktf_context* ktf_find_context(struct ktf_handle *handle, const char* name); +struct ktf_context *ktf_find_first_context(struct ktf_handle *handle); +struct ktf_context *ktf_find_next_context(struct ktf_context* ctx); +void ktf_context_remove(struct ktf_context *ctx); +size_t ktf_has_contexts(struct ktf_handle *handle); +void ktf_context_remove_all(struct ktf_handle *handle);
+/* Called by framework when a configuration is supplied,
- returns the return value of the configuration callback.
- */
+int ktf_context_set_config(struct ktf_context *ctx, const void* data, size_t data_sz);
+struct ktf_context *ktf_find_create_context(struct ktf_handle *handle, const char *name,
const char *type_name);
+int ktf_handle_add_ctx_type(struct ktf_handle *handle, struct ktf_context_type *ct); +struct ktf_context_type *ktf_handle_get_ctx_type(struct ktf_handle *handle,
const char *type_name);
+/* Declare the implicit __test_handle as extern for .c files that use it
- when adding tests with ADD_TEST but where definition is in another .c
file:
- */
+extern struct ktf_handle __test_handle;
+/* Add/remove/find a context to/from the default handle */
nit: I think adding some vertile space to some of these sections might make them a little easier to read.
+#define KTF_CONTEXT_ADD(__context, name) \
- ktf_context_add(&__test_handle, __context, name, NULL, "default")
+#define KTF_CONTEXT_ADD_CFG(__context, name, __cb, __type_name) \
- ktf_context_add(&__test_handle, __context, name, __cb, __type_name)
+#define KTF_CONTEXT_FIND(name) ktf_find_context(&__test_handle, name) +#define KTF_CONTEXT_GET(name, type) \
- container_of(KTF_CONTEXT_FIND(name), type, k)
[...]
+/* For unsupported platforms. */ +#ifndef KTF_PROBE_SUPPORT +#define KTF_SET_RETURN_VALUE(value) do { } while (0) +#endif /* KTF_PROBE_SUPPORT */
+#define KTF_UNREGISTER_RETURN_PROBE(func, probehandler) \
- KTF_UNREGISTER_PROBE(handler, func, probehandler)
+#define KTF_OVERRIDE(func, probehandler) \
- static int probehandler(struct kprobe *, struct pt_regs *);\
- static struct kprobe __ktf_override_##probehandler = { \
.symbol_name = #func, \
.pre_handler = probehandler, \
.post_handler = ktf_post_handler, \
.fault_handler = NULL, \
.flags = 0, \
}; \
static int probehandler(struct kprobe *kp, struct pt_regs *regs)
Oh cool! Does this allow you to mock out/spy on arbitrary functions?
Rob Herring suggested I implement my arbitrary function mocking in terms of kprobe, if I remember correctly. I wonder how easily we could make this work with KUnit.
I don't want to totally derail this discussion by changing the subject, but I am thinking about doing parameter matching and/or C-style class mocking as one of the next major collection of things to upstream. I don't think you have anything comparable to parameter matching, would it help you if I tried to start upstreaming both of those things before/while you work on this?
I guess this is all part of a bigger meta question in what features you want to break out into separate patchsets? This patchset it pretty huge as it is, and I think you are going to want to break it up to make it easier to review. I think you should probably break this initial patch into multiple patches.
+#ifdef KTF_PROBE_SUPPORT +#define KTF_REGISTER_OVERRIDE(func, probehandler) \
- ktf_register_override(&__ktf_override_##probehandler)
+#else +#define KTF_REGISTER_OVERRIDE(func, probehandler) \
- ktf_no_probe_support()
+#endif
+#define KTF_UNREGISTER_OVERRIDE(func, probehandler) \
- do { \
unregister_kprobe(&__ktf_override_##probehandler); \
memset(&__ktf_override_##probehandler, 0, \
sizeof(struct kprobe)); \
__ktf_override_##probehandler.symbol_name = #func; \
__ktf_override_##probehandler.pre_handler = probehandler; \
__ktf_override_##probehandler.post_handler = ktf_post_handler; \
- } while (0)
+#define KTF_OVERRIDE_RETURN \
- do { \
ktf_override_function_with_return(regs); \
return 1; \
- } while (0)
+#ifdef KTF_PROBE_SUPPORT +#define KTF_SET_INSTRUCTION_POINTER(regs, value) \
- instruction_pointer_set(regs, (value))
+#else +#define KTF_SET_INSTRUCTION_POINTER(regs, value) do { } while (0) +#endif
+/* Interfaces for creating kthreads in tests. */ +#define KTF_THREAD_INIT(threadname, t) \
- do { \
(t)->func = threadname; \
(t)->name = #threadname; \
(t)->state.self = self; \
(t)->state.ctx = ctx; \
(t)->state.iter = _i; \
(t)->state.value = _value; \
init_completion(&((t)->started)); \
init_completion(&((t)->completed)); \
- } while (0)
+#define KTF_THREAD_RUN(t) \
- ((t)->task = kthread_run((t)->func, t, (t)->name))
+#define KTF_THREAD_STOP(t) \
- do { \
if ((t)->task) \
kthread_stop((t)->task); \
- } while (0)
+/* Wraps thread execution to supply same variables as test case - this allows
- us to define assertions etc in thread context.
- */
+#define KTF_THREAD(name) \
- static void __##name(struct ktf_thread *thread, struct ktf_test *self, \
struct ktf_context *ctx, int _i, u32 _value); \
- static int name(void *data) \
- { \
struct ktf_thread *t = data; \
complete(&t->started); \
__##name(t, t->state.self, t->state.ctx, t->state.iter, \
t->state.value); \
complete(&t->completed); \
return 0; \
- } \
- static void __##name(struct ktf_thread *_thread, struct ktf_test *self,\
struct ktf_context *ctx, int _i, u32 _value)
+#define KTF_THREAD_WAIT_STARTED(t) (wait_for_completion(&((t)-
started)))
+#define KTF_THREAD_WAIT_COMPLETED(t) (wait_for_completion(&((t)-
completed)))
+u32 ktf_get_assertion_count(void);
+/**
- ASSERT_TRUE() - fail and return if @C evaluates to false
- @C: Boolean expression to evaluate
- */
+#define ASSERT_TRUE(C) do { \
if (!ktf_assert((C))) return; \
- } while (0)
+/**
- ASSERT_FALSE() - fail and return if @C evaluates to true
- @C: Boolean expression to evaluate
- */
+#define ASSERT_FALSE(C) do { \
if (!ktf_assert(!(C))) return; \
- } while (0)
+/**
- ASSERT_TRUE_GOTO() - fail and jump to @_lbl if @C evaluates to false
- @C: Boolean expression to evaluate
- @_lbl: Label to jump to in case of failure
- */
+#define ASSERT_TRUE_GOTO(C,_lbl) { \
- if (!ktf_assert((C))) goto _lbl;\
+}
+/**
- ASSERT_FALSE_GOTO() - fail and jump to @_lbl if @C evaluates to true
- @C: Boolean expression to evaluate
- @_lbl: Label to jump to in case of failure
- */
+#define ASSERT_FALSE_GOTO(C,_lbl) { \
- if (!ktf_assert(!(C))) goto _lbl;\
+}
+/**
- ASSERT_TRUE_RETVAL() - fail and return @V if @C evaluates to false
- @C: Boolean expression to evaluate
- @V: Value to return on failure
- */
+#define ASSERT_TRUE_RETVAL(C, V) do { \
- if (!ktf_assert((C))) return V; \
+} while (0)
+/**
- ASSERT_FALSE() - fail and return @V if @C evaluates to true
- @C: Boolean expression to evaluate
- @V: Value to return on failure
- */
+#define ASSERT_FALSE_RETVAL(C, V) do { \
- if (!ktf_assert(!(C))) return V; \
+} while (0)
+/**
- ASSERT_TRUE_CONT() - fail and continue if @C evaluates to false
- @C: Boolean expression to evaluate
- */
+#define ASSERT_TRUE_CONT(C) { \
- if (!ktf_assert((C))) continue; \
+}
+/**
- ASSERT_FALSE_CONT() - fail and continue if @C evaluates to true
- @C: Boolean expression to evaluate
- */
+#define ASSERT_FALSE_CONT(C) { \
- if (!ktf_assert(!(C))) continue; \
+}
+/**
- ASSERT_TRUE_BREAK() - fail and break if @C evaluates to false
- @C: Boolean expression to evaluate
- */
+#define ASSERT_TRUE_BREAK(C) { \
- if (!ktf_assert((C))) break; \
+}
+/**
- ASSERT_FALSE_BREAK() - fail and break if @C evaluates to true
- @C: Boolean expression to evaluate
- */
+#define ASSERT_FALSE_BREAK(C) { \
- if (!ktf_assert(!(C))) break; \
+}
+/**
- ASSERT_LONG_EQ() - compare two longs, fail and return if @X != @Y
- @X: Expected value
- @Y: Actual value
- */
+#define ASSERT_LONG_EQ(X, Y) \
- ktf_assert_long_ret(X, ==, Y);
+#define ASSERT_LONG_NE(X, Y) \
- ktf_assert_long_ret(X, !=, Y);
+#define ASSERT_ADDR_EQ(X, Y) \
- ktf_assert_long_ret((u64)(X), ==, (u64)(Y));
+#define ASSERT_ADDR_NE(X, Y) \
- ktf_assert_long_ret((u64)(X), !=, (u64)(Y));
+#define ASSERT_INT_EQ(X, Y) \
- ktf_assert_int_ret(X, ==, Y);
+#define ASSERT_INT_GT(X, Y) \
- ktf_assert_int_ret(X, >, Y);
+/**
- ASSERT_LONG_EQ() - compare two longs, jump to @_lbl if @X != @Y
- @X: Expected value
- @Y: Actual value
- @_lbl: Label to jump to in case of failure
- */
+#define ASSERT_LONG_EQ_GOTO(X, Y, _lbl) \
- ktf_assert_long_goto(X, ==, Y, _lbl)
+#define ASSERT_LONG_NE_GOTO(X, Y, _lbl) \
- ktf_assert_long_goto(X, !=, Y, _lbl)
+#define ASSERT_ADDR_EQ_GOTO(X, Y, _lbl) \
- ktf_assert_long_goto((u64)(X), ==, (u64)(Y), _lbl)
+#define ASSERT_ADDR_NE_GOTO(X, Y, _lbl) \
- ktf_assert_long_goto((u64)(X), !=, (u64)(Y), _lbl)
+#define ASSERT_INT_EQ_GOTO(X, Y, _lbl) \
- ktf_assert_int_goto(X, ==, Y, _lbl)
+#define ASSERT_INT_GE_GOTO(X, Y, _lbl) \
- ktf_assert_int_goto(X, >=, Y, _lbl)
+#define ASSERT_INT_GT_GOTO(X, Y, _lbl) \
- ktf_assert_int_goto(X, >, Y, _lbl)
+#define ASSERT_INT_LT_GOTO(X, Y, _lbl) \
- ktf_assert_int_goto(X, <, Y, _lbl)
+#define ASSERT_INT_NE(X,Y) \
- ktf_assert_int_ret(X, !=, Y);
+#define ASSERT_INT_NE_GOTO(X,Y,_lbl) \
- ktf_assert_int_goto(X, !=, Y, _lbl);
+/**
- EXPECT_TRUE() - fail if @C evaluates to false but allow test to continue
- @C: Boolean expression to evaluate
- */
+#define EXPECT_TRUE(C) ktf_assert(C) +#define EXPECT_FALSE(C) ktf_assert(!(C))
+#define OK_ADDR(X) (X && !IS_ERR(X))
+/* Valid kernel address check */ +#define EXPECT_OK_ADDR(X) \
- ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx", (X))
+#define ASSERT_OK_ADDR(X) do { \
if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - value
0x%Lx", (X))) \
return; \
- } while (0)
+#define ASSERT_OK_ADDR_GOTO(X,_lbl) do { \
if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was
0x%Lx", (X))) \
goto _lbl; \
- } while (0)
+#define ASSERT_OK_ADDR_BREAK(X) do { \
- if (!ktf_assert_msg(OK_ADDR(X), "Invalid pointer '"#X"' - was 0x%Lx",
(X))) \
break; \
- } while (0)
+#define EXPECT_INT_EQ(X,Y) ktf_assert_int(X, ==, Y) +#define EXPECT_INT_GT(X,Y) ktf_assert_int(X, >, Y) +#define EXPECT_INT_GE(X,Y) ktf_assert_int(X, >=, Y) +#define EXPECT_INT_LE(X,Y) ktf_assert_int(X, <=, Y) +#define EXPECT_INT_LT(X,Y) ktf_assert_int(X, <, Y) +#define EXPECT_INT_NE(X,Y) ktf_assert_int(X, !=, Y)
+#define EXPECT_LONG_EQ(X, Y) ktf_assert_long(X, ==, Y) +#define EXPECT_LONG_NE(X, Y) ktf_assert_long(X, !=, Y) +#define EXPECT_ADDR_EQ(X, Y) ktf_assert_long((u64)(X), ==, (u64)(Y)) +#define EXPECT_ADDR_NE(X, Y) ktf_assert_long((u64)(X), !=, (u64)(Y)) +#define EXPECT_LONG_GT(X, Y) ktf_assert_long(X, >, Y) +#define EXPECT_LONG_GE(X, Y) ktf_assert_long(X, >=, Y) +#define EXPECT_LONG_LE(X, Y) ktf_assert_long(X, <=, Y) +#define EXPECT_LONG_LT(X, Y) ktf_assert_long(X, <, Y)
+#define EXPECT_STREQ(X, Y) ktf_assert_str_eq(X, Y) +#define EXPECT_STRNE(X, Y) ktf_assert_str_ne(X, Y)
+extern ulong ktf_debug_mask;
+/* Defined debug bits - higher values should represent more
- verbose categories:
- */
+#define T_INFO 0x1 +#define T_LIST 0x2 +#define T_INTR 0x200 +#define T_INFO_V 0x800 +#define T_DEBUG 0x1000 +#define T_MCAST 0x2000 +#define T_TRACE 0x100000 +#define T_DEBUG_V 0x200000
+#define tlog(class, format, arg...) \
- do { \
if (unlikely((ktf_debug_mask) & (class))) \
printk(KERN_INFO \
"ktf pid [%d] " "%s: " format "\n", \
current->pid, __func__, \
## arg); \
- } while (0)
+#define twarn(format, arg...) \
- do { \
printk(KERN_WARNING \
"ktf pid [%d] " "%s: " format "\n", \
current->pid, __func__, \
## arg); \
- } while (0)
+#define terr(format, arg...) \
- do { \
printk(KERN_ERR \
"ktf pid [%d] " "%s: " format "\n", \
current->pid, __func__, \
## arg); \
- } while (0)
+#define tlogs(class, stmt_list) \
- do { \
if (unlikely((ktf_debug_mask) & (class))) { \
stmt_list;\
} \
- } while (0)
+/* Look up the current address of a potentially local symbol - to allow testing
- against it. NB! This is a hack for unit testing internal unexposed
interfaces and
- violates the module boundaries and has no fw/bw comp gauarantees, but
are
- still very useful for detailed unit testing complex logic:
- */
+void* ktf_find_symbol(const char *mod, const char *sym);
+unsigned long ktf_symbol_size(unsigned long addr);
+#define ktf_resolve_symbol(mname, sname) \
- do { \
sname = ktf_find_symbol(#mname, #sname); \
if (!sname) \
return -ENOENT; \
- } while (0)
+#endif
[...]
diff --git a/tools/testing/selftests/ktf/kernel/ktf_test.c b/tools/testing/selftests/ktf/kernel/ktf_test.c new file mode 100644 index 0000000..1e287d0 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_test.c
[...]
+long _ktf_assert(struct ktf_test *self, int result, const char *file,
int line, const char *fmt, ...)
This looks a lot like the KUnit expectation/assertion API.
+{
- int len;
- va_list ap;
- char *buf;
- char bufprefix[256];
- unsigned long flags;
- if (result) {
atomic_inc(&assert_cnt);
- } else {
flush_assert_cnt(self);
buf = kmalloc(MAX_PRINTF, GFP_KERNEL);
if (!buf) {
terr("file %s line %d: Unable to allocate memory for the
error report!",
file, line);
goto out;
}
va_start(ap, fmt);
len = vsnprintf(buf, MAX_PRINTF - 1, fmt, ap);
buf[len] = 0;
va_end(ap);
if (self->skb) {
nla_put_u32(self->skb, KTF_A_STAT, result);
nla_put_string(self->skb, KTF_A_FILE, file);
nla_put_u32(self->skb, KTF_A_NUM, line);
nla_put_string(self->skb, KTF_A_STR, buf);
}
(void)snprintf(bufprefix, sizeof(bufprefix) - 1,
"file %s line %d: result %d: ", file, line,
result);
terr("%s%s", bufprefix, buf);
/* Multiple threads may try to update log */
spin_lock_irqsave(&assert_lock, flags);
(void)strncat(self->log, bufprefix, KTF_MAX_LOG);
(void)strncat(self->log, buf, KTF_MAX_LOG);
spin_unlock_irqrestore(&assert_lock, flags);
kfree(buf);
- }
+out:
- return result;
+} +EXPORT_SYMBOL(_ktf_assert);
[...]
diff --git a/tools/testing/selftests/ktf/kernel/ktf_test.h b/tools/testing/selftests/ktf/kernel/ktf_test.h new file mode 100644 index 0000000..9769664 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_test.h @@ -0,0 +1,381 @@ +/*
- Copyright (C) 2001, 2002, Arien Malec
- Copyright (C) 2011, 2017, Oracle and/or its affiliates.
- SPDX-License-Identifier: GPL-2.1
- This file originates from check.h from the Check C unit test
- framework, adapted by Knut Omang to build with the linux kernel.
- */
+#ifndef KTF_TEST_H +#define KTF_TEST_H
+#include <net/netlink.h> +#include <linux/version.h> +#include "ktf_map.h" +#include "ktf_unlproto.h"
+/* A test context is an extendable object that a test client module
- can supply, and that all tests will be invoked with as an implicit
- 'ctx' argument:
- */
+struct ktf_context;
+struct ktf_test;
+typedef void (*ktf_test_fun) (struct ktf_test *, struct ktf_context* tdev, int, u32);
+struct ktf_debugfs {
struct dentry *debugfs_results_testset;
struct dentry *debugfs_results_test;
struct dentry *debugfs_run_testset;
struct dentry *debugfs_run_test;
+};
+struct ktf_test {
- struct ktf_map_elem kmap; /* linkage for test case list */
- const char* tclass; /* test class name */
- const char* name; /* Name of the test */
- ktf_test_fun fun;
- int start; /* Start and end value to argument to fun */
- int end; /* Defines number of iterations */
- struct sk_buff *skb; /* sk_buff for recording assertion results */
- char *log; /* per-test log */
- void *data; /* Test specific out-of-band data */
- size_t data_sz; /* Size of the data element, if set */
- struct timespec lastrun; /* last time test was run */
- struct ktf_debugfs debugfs; /* debugfs info for test */
- struct ktf_handle *handle; /* Handler for owning module */
+};
+struct ktf_case {
- struct ktf_map_elem kmap; /* Linkage for ktf_map */
- struct ktf_map tests; /* List of tests to run */
- struct ktf_debugfs debugfs; /* debugfs handles for testset */
+};
So this seems comparable to a `struct kunit_suite`, does that seem right?
+/* Used for tests that spawn kthreads to pass state. We should probably
- look at passing data to tests like this to make things more extensible,
- but will defer for now as this would disrupt KTF consumers.
- */
+struct ktf_test_state {
- struct ktf_test *self;
- struct ktf_context *ctx;
- int iter;
- u32 value;
+};
[...]
+#endif /* KTF_TEST_H */
git-series 0.9.1
My initial thoughts: at a high level it seems that you have some data structures and some functions which are pretty similiar to what exists in KUnit. I also think this patch is *way* too complicated and is going to need to be broken down into several smaller patches.
I think some book-keeping is done differently, but I think, at the core, how we represent tests inside the kernel is actually very similar. I see a bunch of stuff that you added to support the KTF netlink protocol and the kprobe stuff, but I don't see any reason why you wouldn't be able to add that to the KUnit data structures.
Could you try to see if you can implement your features on top of what is already there in KUnit? I think a good place to start would be getting hybrid tests working. Shuah and I were talking offlist and we think that there is value to providing a mechanism to trigger tests from userspace; it seems you have something that does that, and I think it represents a small portion of what you have here. So maybe that is a good place for you to start.
As for breaking this patch down, several things immediately jump out to me that should be in different patchs:
- kprobe stuff
- expectations/assertions
- hybrid tests
- core in-kernel test stuff
You might even be able to break it down further, but I think that would be a good start.
In any case, looks like good stuff. I am very keen on the arbitrary function mocking/spying and the hybrid testing stuff in particular.
Cheers!
The generic netlink protocol used to communicate between kernel and user space about tests and test results, as well as some means for configuring tests within the kernel.
Unlike other kernel side test code in the kernel, ktf does not print anything from inside the kernel (except for optional debugging features to help "internal" debugging of ktf or ktf tests). Instead all test results are communicated back to the user space frontend, which decides how to do the reporting.
ktf_nl.c: ktf netlink protocol implementation ktf_unlproto.h: implements interfaces for user-kernel netlink interactions
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/kernel/ktf_nl.c | 516 +++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_nl.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_unlproto.h | 105 +++- 3 files changed, 636 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_nl.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_nl.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_unlproto.h
diff --git a/tools/testing/selftests/ktf/kernel/ktf_nl.c b/tools/testing/selftests/ktf/kernel/ktf_nl.c new file mode 100644 index 0000000..56d5f3b --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_nl.c @@ -0,0 +1,516 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_nl.c: ktf netlink protocol implementation + */ +#include <linux/version.h> +#include <net/netlink.h> +#include <net/genetlink.h> +#define NL_INTERNAL 1 +#include "ktf_unlproto.h" +#include "ktf_test.h" +#include "ktf_nl.h" +#include "ktf.h" +#include "ktf_cov.h" + +/* Generic netlink support to communicate with user level + * test framework. + */ + +/* Callback functions defined below */ +static int ktf_run(struct sk_buff *skb, struct genl_info *info); +static int ktf_query(struct sk_buff *skb, struct genl_info *info); +static int ktf_req(struct sk_buff *skb, struct genl_info *info); +static int ktf_resp(struct sk_buff *skb, struct genl_info *info); +static int ktf_cov_cmd(enum ktf_cmd_type type, struct sk_buff *skb, + struct genl_info *info); +static int ktf_ctx_cfg(struct sk_buff *skb, struct genl_info *info); +static int send_version_only(struct sk_buff *skb, struct genl_info *info); + +/* operation definition */ +static struct genl_ops ktf_ops[] = { + { + .cmd = KTF_C_REQ, + .flags = 0, + .doit = ktf_req, + .dumpit = NULL, + }, + { + .cmd = KTF_C_RESP, + .flags = 0, + .doit = ktf_resp, + .dumpit = NULL, + } +}; + +/* family definition */ +static struct genl_family ktf_gnl_family = { + .module = THIS_MODULE, + .hdrsize = 0, + .name = "ktf", + .version = 1, + .maxattr = KTF_A_MAX + 4, + .policy = ktf_gnl_policy, + .ops = ktf_ops, + .n_ops = ARRAY_SIZE(ktf_ops), +}; + +/* handler, returns 0 on success, negative + * values on failure. It doesn't make much difference + * what error values are used, as they are anyway discarded + * at the netlink level, but do result in a nonzero return + * from nl_wait_for_ack() in user space. + */ +static int ktf_req(struct sk_buff *skb, struct genl_info *info) +{ + enum ktf_cmd_type type; + u64 version; + + /* Dispatch on type of request */ + + if (!info->attrs[KTF_A_TYPE] || !info->attrs[KTF_A_VERSION]) { + terr("received netlink msg with no type/version!"); + return -EINVAL; + } + + version = nla_get_u64(info->attrs[KTF_A_VERSION]); + if (ktf_version_check(version)) { + /* a query is the first call for any reasonable application: + * Respond to it with a version only: + */ + if (nla_get_u32(info->attrs[KTF_A_TYPE]) == KTF_CT_QUERY) + return send_version_only(skb, info); + return -EINVAL; + } + + type = nla_get_u32(info->attrs[KTF_A_TYPE]); + switch (type) { + case KTF_CT_QUERY: + return ktf_query(skb, info); + case KTF_CT_RUN: + return ktf_run(skb, info); + case KTF_CT_COV_ENABLE: + case KTF_CT_COV_DISABLE: + return ktf_cov_cmd(type, skb, info); + case KTF_CT_CTX_CFG: + return ktf_ctx_cfg(skb, info); + default: + terr("received netlink msg with invalid type (%d)", type); + } + return -EINVAL; +} + +/* Reply with just version information to let user space report the issue: */ +static int send_version_only(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *resp_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + void *data; + int retval = 0; + + if (!resp_skb) + return -ENOMEM; + data = genlmsg_put_reply(resp_skb, info, &ktf_gnl_family, + 0, KTF_C_RESP); + if (!data) { + retval = -ENOMEM; + goto resp_failure; + } + nla_put_u32(resp_skb, KTF_A_TYPE, KTF_CT_QUERY); + nla_put_u64_64bit(resp_skb, KTF_A_VERSION, KTF_VERSION_LATEST, 0); + + /* Recompute message header */ + genlmsg_end(resp_skb, data); + + retval = genlmsg_reply(resp_skb, info); +resp_failure: + /* Free buffer if failure */ + if (retval) + nlmsg_free(resp_skb); + return retval; +} + +/* Send data about one testcase */ +static int send_test_data(struct sk_buff *resp_skb, struct ktf_case *tc) +{ + struct nlattr *nest_attr; + struct ktf_test *t; + int stat; + int cnt = 0; + + stat = nla_put_string(resp_skb, KTF_A_STR, ktf_case_name(tc)); + if (stat) + return stat; + + nest_attr = nla_nest_start(resp_skb, KTF_A_TEST); + ktf_testcase_for_each_test(t, tc) { + cnt++; + /* A test is not valid if the handle requires a context and none is present */ + if (t->handle->id) { + stat = nla_put_u32(resp_skb, KTF_A_HID, t->handle->id); + if (stat) + goto fail; + } else if (t->handle->require_context) { + continue; + } + stat = nla_put_string(resp_skb, KTF_A_STR, t->name); + if (stat) + goto fail; + } + nla_nest_end(resp_skb, nest_attr); + tlog(T_DEBUG, "Sent data about %d tests", cnt); + return 0; +fail: + twarn("Failed with status %d after sending data about %d tests", stat, cnt); + /* we hold reference to t here - drop it! */ + ktf_test_put(t); + return stat; +} + +static int send_handle_data(struct sk_buff *resp_skb, struct ktf_handle *handle) +{ + struct ktf_context_type *ct; + struct nlattr *nest_attr; + struct ktf_context *ctx; + int stat; + + tlog(T_DEBUG, "Sending context handle %d: ", handle->id); + + /* Send HID */ + stat = nla_put_u32(resp_skb, KTF_A_HID, handle->id); + if (stat) + return stat; + + /* Send contexts */ + nest_attr = nla_nest_start(resp_skb, KTF_A_LIST); + if (!nest_attr) + return -ENOMEM; + + tlog(T_DEBUG, "Sending context type list"); + /* Send any context types that user space are allowed to create contexts for */ + ktf_map_for_each_entry(ct, &handle->ctx_type_map, elem) { + if (ct->alloc) { + stat = nla_put_string(resp_skb, KTF_A_FILE, ct->name); + if (stat) + return -ENOMEM; + } + } + + /* Then send all the contexts themselves */ + ctx = ktf_find_first_context(handle); + while (ctx) { + nla_put_string(resp_skb, KTF_A_STR, ktf_context_name(ctx)); + if (ctx->config_cb) { + stat = nla_put_string(resp_skb, KTF_A_MOD, ctx->type->name); + if (stat) + return stat; + stat = nla_put_u32(resp_skb, KTF_A_STAT, ctx->config_errno); + if (stat) + return stat; + } + ctx = ktf_find_next_context(ctx); + } + nla_nest_end(resp_skb, nest_attr); + return 0; +} + +static int ktf_query(struct sk_buff *skb, struct genl_info *info) +{ + struct sk_buff *resp_skb; + void *data; + int retval = 0; + struct nlattr *nest_attr; + struct ktf_handle *handle; + struct ktf_case *tc; + + /* No options yet, just send a response */ + resp_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!resp_skb) + return -ENOMEM; + + data = genlmsg_put_reply(resp_skb, info, &ktf_gnl_family, + 0, KTF_C_RESP); + if (!data) { + retval = -ENOMEM; + goto resp_failure; + } + + nla_put_u64_64bit(resp_skb, KTF_A_VERSION, KTF_VERSION_LATEST, 0); + + /* Add all test sets to the report + * We send test info as follows: + * KTF_CT_QUERY hid1 [context1 [context2 ...]] hid2 [context1 [context2 ...]] + * testset_num [testset1 [name1 name2 ..] testset2 [name1 name2 ..]] + * Handle IDs without contexts are not present + */ + if (!nla_put_u32(resp_skb, KTF_A_TYPE, KTF_CT_QUERY)) { + if (!list_empty(&context_handles)) { + /* Traverse list of handles with contexts */ + nest_attr = nla_nest_start(resp_skb, KTF_A_HLIST); + list_for_each_entry(handle, &context_handles, handle_list) { + retval = send_handle_data(resp_skb, handle); + if (retval) + goto resp_failure; + } + nla_nest_end(resp_skb, nest_attr); + } + + /* Send total number of tests */ + tlog(T_DEBUG, "Total #of test cases: %ld", ktf_case_count()); + nla_put_u32(resp_skb, KTF_A_NUM, ktf_case_count()); + nest_attr = nla_nest_start(resp_skb, KTF_A_LIST); + if (!nest_attr) { + retval = -ENOMEM; + goto resp_failure; + } + ktf_for_each_testcase(tc) { + retval = send_test_data(resp_skb, tc); + if (retval) { + retval = -ENOMEM; + goto resp_failure; + } + } + nla_nest_end(resp_skb, nest_attr); + } + + /* Recompute message header */ + genlmsg_end(resp_skb, data); + + retval = genlmsg_reply(resp_skb, info); +resp_failure: + if (retval) + twarn("Message failure (status %d)", retval); + /* Free buffer if failure */ + if (retval) + nlmsg_free(resp_skb); + return retval; +} + +static int ktf_run_func(struct sk_buff *skb, const char *ctxname, + const char *setname, const char *testname, + u32 value, void *oob_data, size_t oob_data_sz) +{ + struct ktf_case *testset = ktf_case_find(setname); + struct ktf_test *t; + int tn = 0; + + if (!testset) { + tlog(T_INFO, "No such testset "%s"\n", setname); + return -EFAULT; + } + + /* Execute test functions */ + ktf_testcase_for_each_test(t, testset) { + if (t->fun && strcmp(t->name, testname) == 0) { + struct ktf_context *ctx = ktf_find_context(t->handle, ctxname); + + ktf_run_hook(skb, ctx, t, value, oob_data, oob_data_sz); + } else if (!t->fun) { + tlog(T_DEBUG, "** no function for test %s.%s **", t->tclass, t->name); + } + tn++; + } + tlog(T_DEBUG, "Set %s contained %d tests", ktf_case_name(testset), tn); + ktf_case_put(testset); + return 0; +} + +static int ktf_run(struct sk_buff *skb, struct genl_info *info) +{ + u32 value = 0; + struct sk_buff *resp_skb; + void *data; + int retval = 0; + struct nlattr *nest_attr, *data_attr; + char ctxname_store[KTF_MAX_NAME + 1]; + char *ctxname = ctxname_store; + char setname[KTF_MAX_NAME + 1]; + char testname[KTF_MAX_NAME + 1]; + void *oob_data = NULL; + size_t oob_data_sz = 0; + + if (info->attrs[KTF_A_STR]) + nla_strlcpy(ctxname, info->attrs[KTF_A_STR], KTF_MAX_NAME); + else + ctxname = NULL; + + if (!info->attrs[KTF_A_SNAM]) { + terr("received KTF_CT_RUN msg without testset name!"); + return -EINVAL; + } + nla_strlcpy(setname, info->attrs[KTF_A_SNAM], KTF_MAX_NAME); + + if (!info->attrs[KTF_A_TNAM]) { /* Test name wo/context */ + terr("received KTF_CT_RUN msg without test name!"); + return -EINVAL; + } + nla_strlcpy(testname, info->attrs[KTF_A_TNAM], KTF_MAX_NAME); + + if (info->attrs[KTF_A_NUM]) { + /* Using NUM field as optional u32 input parameter to test */ + value = nla_get_u32(info->attrs[KTF_A_NUM]); + } + + data_attr = info->attrs[KTF_A_DATA]; + if (data_attr) { + /* User space sends out-of-band data: */ + oob_data = nla_memdup(data_attr, GFP_KERNEL); + oob_data_sz = nla_len(data_attr); + } + + tlog(T_DEBUG, "Request for testset %s, test %s\n", setname, testname); + + /* Start building a response */ + resp_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!resp_skb) + return -ENOMEM; + + data = genlmsg_put_reply(resp_skb, info, &ktf_gnl_family, + 0, KTF_C_RESP); + if (!data) { + retval = -ENOMEM; + goto put_fail; + } + + nla_put_u32(resp_skb, KTF_A_TYPE, KTF_CT_RUN); + nest_attr = nla_nest_start(resp_skb, KTF_A_LIST); + retval = ktf_run_func(resp_skb, ctxname, setname, testname, value, oob_data, oob_data_sz); + nla_nest_end(resp_skb, nest_attr); + nla_put_u32(resp_skb, KTF_A_STAT, retval); + + /* Recompute message header */ + genlmsg_end(resp_skb, data); + + retval = genlmsg_reply(resp_skb, info); + if (!retval) + tlog(T_DEBUG, "Sent reply for test %s.%s\n", setname, testname); + else + twarn("Failed to send reply for test %s.%s - value %d", + setname, testname, retval); + + kfree(oob_data); +put_fail: + /* Free buffer if failure */ + if (retval) + nlmsg_free(resp_skb); + return retval; +} + +static int ktf_resp(struct sk_buff *skb, struct genl_info *info) +{ + /* not to expect this message here */ + terr("unexpected netlink RESP msg received"); + return 0; +} + +static int ktf_cov_cmd(enum ktf_cmd_type type, struct sk_buff *skb, + struct genl_info *info) +{ + char *cmd = type == KTF_CT_COV_ENABLE ? "COV_ENABLE" : "COV_DISABLE"; + char module[KTF_MAX_NAME + 1]; + struct sk_buff *resp_skb; + int retval = 0; + void *data; + u32 opts = 0; + + if (!info->attrs[KTF_A_MOD]) { + terr("received KTF_CT_%s msg without module name!", cmd); + return -EINVAL; + } + nla_strlcpy(module, info->attrs[KTF_A_MOD], KTF_MAX_NAME); + if (info->attrs[KTF_A_COVOPT]) + opts = nla_get_u32(info->attrs[KTF_A_COVOPT]); + + /* Start building a response */ + resp_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!resp_skb) + return -ENOMEM; + + tlog(T_DEBUG, "%s coverage for %s\n", cmd, module); + if (type == KTF_CT_COV_ENABLE) + retval = ktf_cov_enable(module, opts); + else + ktf_cov_disable(module); + + data = genlmsg_put_reply(resp_skb, info, &ktf_gnl_family, + 0, KTF_C_RESP); + if (!data) { + retval = -ENOMEM; + goto put_fail; + } + nla_put_u32(resp_skb, KTF_A_TYPE, type); + nla_put_u32(resp_skb, KTF_A_STAT, retval); + /* Recompute message header */ + genlmsg_end(resp_skb, data); + + retval = genlmsg_reply(resp_skb, info); + if (!retval) + tlog(T_DEBUG, "Sent reply for %s module %s\n", + cmd, module); + else + twarn("Failed to send reply for %s module %s - value %d", + cmd, module, retval); +put_fail: + /* Free buffer if failure */ + if (retval) + nlmsg_free(resp_skb); + return retval; +} + +/* Process request to configure a configurable context: + * Expected format: KTF_CT_CTX_CFG hid type_name context_name data + * placed in A_HID, A_FILE, A_STR and A_DATA respectively. + */ +static int ktf_ctx_cfg(struct sk_buff *skb, struct genl_info *info) +{ + char ctxname[KTF_MAX_NAME + 1]; + char type_name[KTF_MAX_NAME + 1]; + struct nlattr *data_attr; + void *ctx_data = NULL; + size_t ctx_data_sz = 0; + int hid; + struct ktf_handle *handle; + struct ktf_context *ctx; + int ret; + + if (!info->attrs[KTF_A_STR] || !info->attrs[KTF_A_HID]) + return -EINVAL; + data_attr = info->attrs[KTF_A_DATA]; + if (!data_attr) + return -EINVAL; + hid = nla_get_u32(info->attrs[KTF_A_HID]); + handle = ktf_handle_find(hid); + if (!handle) + return -EINVAL; + if (info->attrs[KTF_A_FILE]) + nla_strlcpy(type_name, info->attrs[KTF_A_FILE], KTF_MAX_NAME); + else + strcpy(type_name, "default"); + nla_strlcpy(ctxname, info->attrs[KTF_A_STR], KTF_MAX_NAME); + tlog(T_DEBUG, "Trying to find/create context %s with type %s\n", ctxname, type_name); + ctx = ktf_find_create_context(handle, ctxname, type_name); + if (!ctx) + return -ENODEV; + + tlog(T_DEBUG, "Received context configuration for context %s, handle %d\n", + ctxname, hid); + + ctx_data = nla_memdup(data_attr, GFP_KERNEL); + ctx_data_sz = nla_len(data_attr); + ret = ktf_context_set_config(ctx, ctx_data, ctx_data_sz); + kfree(ctx_data); + return ret; +} + +int ktf_nl_register(void) +{ + int stat = genl_register_family(&ktf_gnl_family); + return stat; +} + +void ktf_nl_unregister(void) +{ + genl_unregister_family(&ktf_gnl_family); +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_nl.h b/tools/testing/selftests/ktf/kernel/ktf_nl.h new file mode 100644 index 0000000..87d8012 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_nl.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * nl.h: ktf netlink protocol interface + */ +#ifndef KTF_NL_H +#define KTF_NL_H + +int ktf_nl_register(void); +void ktf_nl_unregister(void); + +#endif diff --git a/tools/testing/selftests/ktf/kernel/ktf_unlproto.h b/tools/testing/selftests/ktf/kernel/ktf_unlproto.h new file mode 100644 index 0000000..e6d4525 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_unlproto.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_unlproto.h: implements interfaces for user-kernel netlink interactions + * for querying/running tests. + */ +#ifndef _KTF_UNLPROTO_H +#define _KTF_UNLPROTO_H +#ifdef __cplusplus +extern "C" { +#endif + +enum ktf_cmd_type { + KTF_CT_UNSPEC, + KTF_CT_QUERY, + KTF_CT_RUN, + KTF_CT_COV_ENABLE, + KTF_CT_COV_DISABLE, + KTF_CT_CTX_CFG, + KTF_CT_MAX, +}; + +/* Netlink protocol definition shared between user and kernel space + * Include once per user app as it defines struct values! + */ + +/* supported attributes */ +enum ktf_attr { + KTF_A_UNSPEC, + KTF_A_TYPE, + KTF_A_VERSION, + KTF_A_SNAM, /* Test suite name */ + KTF_A_TNAM, /* Test name */ + KTF_A_NUM, + KTF_A_STR, + KTF_A_FILE, + KTF_A_STAT, + KTF_A_LIST, + KTF_A_TEST, + KTF_A_HID, /* Test handle ID */ + KTF_A_HLIST, /* List of handles repr. as a LIST of contexts for a given HID */ + KTF_A_MOD, /* module for coverage analysis, also used for context type */ + KTF_A_COVOPT, /* options for coverage analysis */ + KTF_A_DATA, /* Binary data used by a.o. hybrid tests */ + KTF_A_MAX +}; + +/* attribute policy */ +#ifdef NL_INTERNAL +static struct nla_policy ktf_gnl_policy[KTF_A_MAX] = { + [KTF_A_TYPE] = { .type = NLA_U32 }, + [KTF_A_VERSION] = { .type = NLA_U64 }, + [KTF_A_SNAM] = { .type = NLA_STRING }, + [KTF_A_TNAM] = { .type = NLA_STRING }, + [KTF_A_NUM] = { .type = NLA_U32 }, + [KTF_A_STAT] = { .type = NLA_U32 }, + [KTF_A_HID] = { .type = NLA_U32 }, + [KTF_A_LIST] = { .type = NLA_NESTED }, + [KTF_A_TEST] = { .type = NLA_NESTED }, + [KTF_A_HLIST] = { .type = NLA_NESTED }, + [KTF_A_STR] = { .type = NLA_STRING }, + [KTF_A_FILE] = { .type = NLA_STRING }, + [KTF_A_MOD] = { .type = NLA_STRING }, + [KTF_A_COVOPT] = { .type = NLA_U32 }, + [KTF_A_DATA] = { .type = NLA_BINARY }, +}; +#endif + +/* supported commands */ +enum ktf_cmd { + KTF_C_UNSPEC, + KTF_C_REQ, + KTF_C_RESP, + KTF_C_MAX +}; + +enum ktf_vshift { + KTF_VSHIFT_BUILD = 0, + KTF_VSHIFT_MICRO = 16, + KTF_VSHIFT_MINOR = 32, + KTF_VSHIFT_MAJOR = 48 +}; + +#define KTF_VERSION(__field, __v) \ + ((__v & (0xffffULL << KTF_VSHIFT_##__field)) \ + >> KTF_VSHIFT_##__field) + +#define KTF_VERSION_SET(__field, __v) \ + ((__v & 0xffffULL) << KTF_VSHIFT_##__field) + +#define KTF_VERSION_LATEST \ + (KTF_VERSION_SET(MAJOR, 0ULL) | KTF_VERSION_SET(MINOR, 2ULL) | KTF_VERSION_SET(MICRO, 1ULL)) + +/* Coverage options */ +#define KTF_COV_OPT_MEM 0x1 + +struct nla_policy *ktf_get_gnl_policy(void); + +#ifdef __cplusplus +} +#endif +#endif
On Tue, Aug 13, 2019 at 08:09:18AM +0200, Knut Omang wrote:
The generic netlink protocol used to communicate between kernel and user space about tests and test results, as well as some means for configuring tests within the kernel.
Unlike other kernel side test code in the kernel, ktf does not print anything from inside the kernel (except for optional debugging features to help "internal" debugging of ktf or ktf tests). Instead all test results are communicated back to the user space frontend, which decides how to do the reporting.
So why netlink? Why not just a file interface?
[...]
Cheers
On Sun, 2019-09-08 at 18:28 -0700, Brendan Higgins wrote:
On Tue, Aug 13, 2019 at 08:09:18AM +0200, Knut Omang wrote:
The generic netlink protocol used to communicate between kernel and user space about tests and test results, as well as some means for configuring tests within the kernel.
Unlike other kernel side test code in the kernel, ktf does not print anything from inside the kernel (except for optional debugging features to help "internal" debugging of ktf or ktf tests). Instead all test results are communicated back to the user space frontend, which decides how to do the reporting.
So why netlink? Why not just a file interface?
Netlink allows more flexibility in that it is bidirectional and asynchronous. User space may query the kernel for available tests and then decide which tests to invoke. User land test frameworks like Googletest allows use of wildcards and exceptions to select particular tests to run. This is in my opinion very important functionality as we want the tests to be valuable as developer tools, not just to check the code as part of a later QA cycle. Being able to run a single test or a small subset of the tests is very useful.
Wrt test reporting, the kernel side just dispatches off messages about test results as they are gathered. Compare this to the complexities, side effects and limitations of printk.
Besides, for hybrid tests, bidirectional communication allows a test to contain a mix (or a function) of results gathered in the kernel and in user space.
We also use it for network tests, where user space needs to tell the kernel what peer(s) to communicate with, and for certain minimal configuration, such as which device instance to use for device testing. Test nodes may vary in what they offer of hardware. Although we'd like to minimize the need for configuration, as results should be easily reproducable, sometimes there is no good way around.
Thanks, Knut
[...]
Cheers
This container is mapping from an index datatype to a "value" datatatype, using rbtree for the implementation. This datatype is used for various purposes by ktf to store and find data related to the actual test cases.
ktf_map.c: Implementation of a kernel version independent std::map like API ktf_map.h: simple objects with key lookup to be inlined into a
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/kernel/ktf_map.c | 261 ++++++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_map.h | 154 ++++++++++++- 2 files changed, 415 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_map.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_map.h
diff --git a/tools/testing/selftests/ktf/kernel/ktf_map.c b/tools/testing/selftests/ktf/kernel/ktf_map.c new file mode 100644 index 0000000..78ae5fb --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_map.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_map.c: Implementation of a kernel version independent std::map like API + * (made abstract to allow impl to change) + */ + +#include "ktf_map.h" +#include "ktf.h" + +void ktf_map_init(struct ktf_map *map, ktf_map_elem_comparefn elem_comparefn, + ktf_map_elem_freefn elem_freefn) +{ + map->root = RB_ROOT; + map->size = 0; + map->elem_comparefn = elem_comparefn; + map->elem_freefn = elem_freefn; + spin_lock_init(&map->lock); +} + +int ktf_map_elem_init(struct ktf_map_elem *elem, const char *key) +{ + memcpy(elem->key, key, KTF_MAX_KEY); + /* For strings that are too long, ensure truncation at + * KTF_MAX_NAME == KTF_MAX_KEY - 1 length: + */ + elem->key[KTF_MAX_NAME] = '\0'; + elem->map = NULL; + kref_init(&elem->refcount); + return 0; +} + +/* A convenience unsigned int compare function as an alternative + * to the string compare: + */ +int ktf_uint_compare(const char *ac, const char *bc) +{ + unsigned int a = *((unsigned int *)ac); + unsigned int b = *((unsigned int *)bc); + + return a > b ? 1 : (a < b ? -1 : 0); +} +EXPORT_SYMBOL(ktf_uint_compare); + +/* Copy "elem"s key representation into "name". For cases where no + * compare function is defined - i.e. string keys - just copy string, + * otherwise name is hexascii of first 8 bytes of key. + */ +char * +ktf_map_elem_name(struct ktf_map_elem *elem, char *name) +{ + if (!name) + return NULL; + + if (!elem || !elem->map) + (void)strlcpy(name, "<none>", KTF_MAX_NAME); + else if (!elem->map->elem_comparefn) + (void)strlcpy(name, elem->key, KTF_MAX_NAME); + else + (void)snprintf(name, KTF_MAX_NAME, "'%*ph'", 8, elem->key); + + return name; +} + +/* Called when refcount of elem is 0. */ +static void ktf_map_elem_release(struct kref *kref) +{ + struct ktf_map_elem *elem = container_of(kref, struct ktf_map_elem, + refcount); + struct ktf_map *map = elem->map; + char name[KTF_MAX_KEY]; + + tlog(T_DEBUG_V, "Releasing %s, %s free function", + ktf_map_elem_name(elem, name), + map && map->elem_freefn ? "calling" : "no"); + if (map && map->elem_freefn) + map->elem_freefn(elem); +} + +void ktf_map_elem_put(struct ktf_map_elem *elem) +{ + char name[KTF_MAX_KEY]; + + tlog(T_DEBUG_V, "Decreasing refcount for %s to %d", + ktf_map_elem_name(elem, name), + refcount_read(&elem->refcount.refcount) - 1); + kref_put(&elem->refcount, ktf_map_elem_release); +} + +void ktf_map_elem_get(struct ktf_map_elem *elem) +{ + char name[KTF_MAX_KEY]; + + tlog(T_DEBUG_V, "Increasing refcount for %s to %d", + ktf_map_elem_name(elem, name), + refcount_read(&elem->refcount.refcount) + 1); + kref_get(&elem->refcount); +} + +struct ktf_map_elem *ktf_map_find(struct ktf_map *map, const char *key) +{ + struct rb_node *node; + unsigned long flags; + + /* may be called in interrupt context */ + spin_lock_irqsave(&map->lock, flags); + node = map->root.rb_node; + + while (node) { + struct ktf_map_elem *elem = container_of(node, struct ktf_map_elem, node); + int result; + + if (map->elem_comparefn) + result = map->elem_comparefn(key, elem->key); + else + result = strncmp(key, elem->key, KTF_MAX_KEY); + + if (result < 0) { + node = node->rb_left; + } else if (result > 0) { + node = node->rb_right; + } else { + ktf_map_elem_get(elem); + spin_unlock_irqrestore(&map->lock, flags); + return elem; + } + } + spin_unlock_irqrestore(&map->lock, flags); + return NULL; +} + +/* Find the first map elem in 'map' */ +struct ktf_map_elem *ktf_map_find_first(struct ktf_map *map) +{ + struct ktf_map_elem *elem = NULL; + struct rb_node *node; + unsigned long flags; + + spin_lock_irqsave(&map->lock, flags); + node = rb_first(&map->root); + if (node) { + elem = container_of(node, struct ktf_map_elem, node); + ktf_map_elem_get(elem); + } + spin_unlock_irqrestore(&map->lock, flags); + return elem; +} + +/* Find the next element in the map after 'elem' if any */ +struct ktf_map_elem *ktf_map_find_next(struct ktf_map_elem *elem) +{ + struct ktf_map_elem *next = NULL; + struct ktf_map *map = elem->map; + struct rb_node *node; + unsigned long flags; + + if (!elem->map) + return NULL; + spin_lock_irqsave(&map->lock, flags); + node = rb_next(&elem->node); + + /* Assumption here - we don't need ref to elem any more. + * Common usage pattern is + * + * for (elem = ktf_map_elem_first(map); elem != NULL; + * elem = ktf_map_find_next(elem)) + * + * but if other use cases occur we may need to revisit. + * This assumption allows us to define our _for_each macros + * and still manage refcounts. + */ + ktf_map_elem_put(elem); + + if (node) { + next = container_of(node, struct ktf_map_elem, node); + ktf_map_elem_get(next); + } + spin_unlock_irqrestore(&map->lock, flags); + return next; +} + +int ktf_map_insert(struct ktf_map *map, struct ktf_map_elem *elem) +{ + struct rb_node **newobj, *parent = NULL; + unsigned long flags; + + spin_lock_irqsave(&map->lock, flags); + newobj = &map->root.rb_node; + while (*newobj) { + struct ktf_map_elem *this = container_of(*newobj, struct ktf_map_elem, node); + int result; + + if (map->elem_comparefn) + result = map->elem_comparefn(elem->key, this->key); + else + result = strncmp(elem->key, this->key, KTF_MAX_KEY); + + parent = *newobj; + if (result < 0) { + newobj = &((*newobj)->rb_left); + } else if (result > 0) { + newobj = &((*newobj)->rb_right); + } else { + spin_unlock_irqrestore(&map->lock, flags); + return -EEXIST; + } + } + + /* Add newobj node and rebalance tree. */ + rb_link_node(&elem->node, parent, newobj); + rb_insert_color(&elem->node, &map->root); + elem->map = map; + map->size++; + /* Bump reference count for map reference */ + ktf_map_elem_get(elem); + spin_unlock_irqrestore(&map->lock, flags); + return 0; +} + +void ktf_map_remove_elem(struct ktf_map *map, struct ktf_map_elem *elem) +{ + if (elem) { + rb_erase(&elem->node, &map->root); + map->size--; + ktf_map_elem_put(elem); + } +} + +struct ktf_map_elem *ktf_map_remove(struct ktf_map *map, const char *key) +{ + struct ktf_map_elem *elem; + unsigned long flags; + + elem = ktf_map_find(map, key); + spin_lock_irqsave(&map->lock, flags); + ktf_map_remove_elem(map, elem); + spin_unlock_irqrestore(&map->lock, flags); + return elem; +} + +void ktf_map_delete_all(struct ktf_map *map) +{ + struct ktf_map_elem *elem; + struct rb_node *node; + unsigned long flags; + + spin_lock_irqsave(&map->lock, flags); + do { + node = rb_first(&(map)->root); + if (node) { + rb_erase(node, &(map)->root); + map->size--; + elem = container_of(node, struct ktf_map_elem, node); + ktf_map_elem_put(elem); + } + } while (node); + spin_unlock_irqrestore(&map->lock, flags); +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_map.h b/tools/testing/selftests/ktf/kernel/ktf_map.h new file mode 100644 index 0000000..1c8ae9b --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_map.h @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_map.h: simple objects with key lookup to be inlined into a + * larger object + */ + +#ifndef _KTF_MAP_H +#define _KTF_MAP_H +#include <linux/kref.h> +#include <linux/version.h> +#include <linux/rbtree.h> + +#define KTF_MAX_KEY 64 +#define KTF_MAX_NAME (KTF_MAX_KEY - 1) + +struct ktf_map_elem; + +/* Compare function called to compare element keys - optional and if + * not specified we revert to string comparison. Should return < 0 + * if first key < second, > 0 if first key > second, and 0 if they are + * identical. + */ +typedef int (*ktf_map_elem_comparefn)(const char *, const char *); + +/* A convenience unsigned int compare function as an alternative + * to the string compare: + */ +int ktf_uint_compare(const char *a, const char *b); + +/* Free function called when elem refcnt is 0 - optional and of course for + * dynamically-allocated elems only. + */ +typedef void (*ktf_map_elem_freefn)(struct ktf_map_elem *); + +struct ktf_map { + struct rb_root root; /* The rb tree holding the map */ + size_t size; /* Current size (number of elements) of the map */ + spinlock_t lock; /* held for map lookup etc */ + ktf_map_elem_comparefn elem_comparefn; /* Key comparison function */ + ktf_map_elem_freefn elem_freefn; /* Free function */ +}; + +struct ktf_map_elem { + struct rb_node node; /* Linkage for the map */ + char key[KTF_MAX_KEY+1] __aligned(8); + /* Key of the element - must be unique within the same map */ + struct ktf_map *map; /* owning map */ + struct kref refcount; /* reference count for element */ +}; + +#define __KTF_MAP_INITIALIZER(_mapname, _elem_comparefn, _elem_freefn) \ + { \ + .root = RB_ROOT, \ + .size = 0, \ + .lock = __SPIN_LOCK_UNLOCKED(_mapname), \ + .elem_comparefn = _elem_comparefn, \ + .elem_freefn = _elem_freefn, \ + } + +#define DEFINE_KTF_MAP(_mapname, _elem_comparefn, _elem_freefn) \ + struct ktf_map _mapname = __KTF_MAP_INITIALIZER(_mapname, _elem_comparefn, _elem_freefn) + +void ktf_map_init(struct ktf_map *map, ktf_map_elem_comparefn elem_comparefn, + ktf_map_elem_freefn elem_freefn); + +/* returns 0 upon success or -errno upon error */ +int ktf_map_elem_init(struct ktf_map_elem *elem, const char *key); + +/* increase/reduce reference count to element. If count reaches 0, the + * free function associated with map (if any) is called. + */ +void ktf_map_elem_get(struct ktf_map_elem *elem); +void ktf_map_elem_put(struct ktf_map_elem *elem); + +char *ktf_map_elem_name(struct ktf_map_elem *elem, char *name); + +/* Insert a new element in map - return 0 iff 'elem' was inserted or -1 if + * the key already existed - duplicates are not insterted. + */ +int ktf_map_insert(struct ktf_map *map, struct ktf_map_elem *elem); + +/* Find and return the element with 'key' */ +struct ktf_map_elem *ktf_map_find(struct ktf_map *map, const char *key); + +/* Find the first map elem in 'map' with reference count increased. */ +struct ktf_map_elem *ktf_map_find_first(struct ktf_map *map); + +/* Find the next element in the map after 'elem' if any. Decreases refcount + * for "elem" and increases it for returned map element - this helps manage + * reference counts when iterating over map elements. + */ +struct ktf_map_elem *ktf_map_find_next(struct ktf_map_elem *elem); + +/* Remove the element 'key' from the map and return a pointer to it with + * refcount increased. + */ +struct ktf_map_elem *ktf_map_remove(struct ktf_map *map, const char *key); + +/* Remove specific element elem from the map. Refcount is not increased + * as caller must already have had a reference. + */ +void ktf_map_remove_elem(struct ktf_map *map, struct ktf_map_elem *elem); + +void ktf_map_delete_all(struct ktf_map *map); + +static inline size_t ktf_map_size(struct ktf_map *map) { + return map->size; +} + +static inline bool ktf_map_empty(struct ktf_map *map) { + return map->size == 0; +} + +/* Gets first entry with refcount of entry increased for caller. */ +#define ktf_map_first_entry(_map, _type, _member) \ + ktf_map_empty(_map) ? NULL : \ + container_of(ktf_map_find_first(_map), _type, _member) + +/* Gets next elem after "pos", decreasing refcount for pos and increasing + * it for returned entry. + */ +#define ktf_map_next_entry(_pos, _member) ({ \ + struct ktf_map_elem *_e = ktf_map_find_next(&(_pos)->_member); \ + _e ? container_of(_e, typeof(*_pos), _member) : NULL; \ +}) + +/* Iterates over map elements, incrementing refcount for current element and + * decreasing it when we iterate to the next element. Important - if you + * break out of the loop via break/return, ensure ktf_map_elem_put(pos) + * is called for current element since we have a reference to it for the + * current loop body iteration. + */ +#define ktf_map_for_each(pos, map) \ + for (pos = ktf_map_find_first(map); pos != NULL; pos = ktf_map_find_next(pos)) + +/* Iterate over map elements in similar manner as above but using + * container_of() wrappers to work with the type embedding a + * "struct ktf_map_elem". + */ +#define ktf_map_for_each_entry(_pos, _map, _member) \ + for (_pos = ktf_map_first_entry(_map, typeof(*_pos), _member); \ + _pos != NULL; \ + _pos = ktf_map_next_entry(_pos, _member)) + +#define ktf_map_find_entry(_map, _key, _type, _member) ({ \ + struct ktf_map_elem *_entry = ktf_map_find(_map, _key); \ + _entry ? container_of(_entry, _type, _member) : NULL; \ +}) + +#endif
From: Alan Maguire alan.maguire@oracle.com
This is a very powerful and yet simple way to verify or modify behaviour of kernel calls. It uses the same technique as the error injection framework in kernel/fail_function.c to to override function entry and return. In addition to error injection, this is very useful to for instance verify that a particular API actually ends up being called, and in the right way, as an effect of a test.
ktf_override.c: support for overriding function entry. ktf_override.h: Function override support interface for KTF.
Signed-off-by: Alan Maguire alan.maguire@oracle.com Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/kernel/ktf_override.c | 45 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_override.h | 15 +++++- 2 files changed, 60 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_override.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_override.h
diff --git a/tools/testing/selftests/ktf/kernel/ktf_override.c b/tools/testing/selftests/ktf/kernel/ktf_override.c new file mode 100644 index 0000000..7f046c8 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_override.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire alan.maguire@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_override.c: support for overriding function entry. + */ +#include <linux/kprobes.h> +#include <linux/ptrace.h> +#include "ktf.h" +#include "ktf_override.h" + +asmlinkage void ktf_just_return_func(void); + +asm( + ".type ktf_just_return_func, @function\n" + ".globl ktf_just_return_func\n" + "ktf_just_return_func:\n" + " ret\n" + ".size ktf_just_return_func, .-ktf_just_return_func\n" +); + +void ktf_post_handler(struct kprobe *kp, struct pt_regs *regs, + unsigned long flags) +{ + /* + * A dummy post handler is required to prohibit optimizing, because + * jump optimization does not support execution path overriding. + */ +} +EXPORT_SYMBOL(ktf_post_handler); + +void ktf_override_function_with_return(struct pt_regs *regs) +{ + KTF_SET_INSTRUCTION_POINTER(regs, (unsigned long)&ktf_just_return_func); +} +EXPORT_SYMBOL(ktf_override_function_with_return); +NOKPROBE_SYMBOL(ktf_override_function_with_return); + +int ktf_register_override(struct kprobe *kp) +{ + return register_kprobe(kp); +} +EXPORT_SYMBOL(ktf_register_override); diff --git a/tools/testing/selftests/ktf/kernel/ktf_override.h b/tools/testing/selftests/ktf/kernel/ktf_override.h new file mode 100644 index 0000000..8a9cf39 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_override.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire alan.maguire@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_override.h: Function override support interface for KTF. + */ +#include <linux/kprobes.h> +#include "ktf.h" + +void ktf_post_handler(struct kprobe *kp, struct pt_regs *regs, + unsigned long flags); +void ktf_override_function_with_return(struct pt_regs *regs); +int ktf_register_override(struct kprobe *kp);
From: Alan Maguire alan.maguire@oracle.com
While test results is available via netlink from user space, sometimes it may be useful to be able to access the results from the kernel as well, for instance due to a crash. Make that possible via debugfs.
ktf_debugfs.h: Support for creating a debugfs representation of test
Signed-off-by: Alan Maguire alan.maguire@oracle.com Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 ++- 2 files changed, 390 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.h
diff --git a/tools/testing/selftests/ktf/kernel/ktf_debugfs.c b/tools/testing/selftests/ktf/kernel/ktf_debugfs.c new file mode 100644 index 0000000..a20fbd2 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_debugfs.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire alan.maguire@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + */ +#include <asm/unistd.h> +#include <linux/module.h> +#include <linux/time.h> +#include "ktf_debugfs.h" +#include "ktf.h" +#include "ktf_test.h" +#include "ktf_cov.h" + +/* Create a debugfs representation of test sets/tests. Hierarchy looks like + * this: + * + * Path Semantics + * /sys/kernel/debug/ktf/run/<testset> Run all tests in testset + * /sys/kernel/debug/ktf/run/<testset>/<test> Run specific test in testset + * /sys/kernel/debug/ktf/results/<testset> Show results of last run for + * testset + * + * /sys/kernel/debug/ktf/results/<testset>/<test> + * Show results of last run for + * test + * + */ + +static struct dentry *ktf_debugfs_rootdir; +static struct dentry *ktf_debugfs_rundir; +static struct dentry *ktf_debugfs_resultsdir; +static struct dentry *ktf_debugfs_cov_file; + +static void ktf_debugfs_print_result(struct seq_file *seq, struct ktf_test *t) +{ + struct timespec now; + + if (t && strlen(t->log) > 0) { + getnstimeofday(&now); + seq_printf(seq, "[%s/%s, %ld seconds ago] %s\n", + t->tclass, t->name, + now.tv_sec - t->lastrun.tv_sec, t->log); + } +} + +/* /sys/kernel/debug/ktf/results/<testset>-tests/<test> shows specific result */ +static int ktf_debugfs_result(struct seq_file *seq, void *v) +{ + struct ktf_test *t = (struct ktf_test *)seq->private; + + ktf_debugfs_print_result(seq, t); + + return 0; +} + +/* /sys/kernel/debug/ktf/results/<testset> shows all results for testset. */ +static int ktf_debugfs_results_all(struct seq_file *seq, void *v) +{ + struct ktf_case *testset = (struct ktf_case *)seq->private; + struct ktf_test *t; + + if (!testset) + return 0; + + seq_printf(seq, "%s results:\n", ktf_case_name(testset)); + ktf_testcase_for_each_test(t, testset) + ktf_debugfs_print_result(seq, t); + + return 0; +} + +/* /sys/kernel/debug/ktf/run/<testset>-tests/<test> runs specific test. */ +static int ktf_debugfs_run(struct seq_file *seq, void *v) +{ + struct ktf_test *t = (struct ktf_test *)seq->private; + + if (!t) + return 0; + + ktf_run_hook(NULL, NULL, t, 0, NULL, 0); + ktf_debugfs_print_result(seq, t); + + return 0; +} + +/* /sys/kernel/debug/ktf/run/<testset> runs all tests in testset. */ +static int ktf_debugfs_run_all(struct seq_file *seq, void *v) +{ + struct ktf_case *testset = (struct ktf_case *)seq->private; + struct ktf_test *t; + + if (!testset) + return 0; + + seq_printf(seq, "Running %s\n", ktf_case_name(testset)); + ktf_testcase_for_each_test(t, testset) { + ktf_run_hook(NULL, NULL, t, 0, NULL, 0); + ktf_debugfs_print_result(seq, t); + } + + return 0; +} + +static int ktf_run_test_open(struct inode *inode, struct file *file) +{ + struct ktf_test *t; + + if (!try_module_get(THIS_MODULE)) + return -EIO; + + t = (struct ktf_test *)inode->i_private; + + return single_open(file, ktf_debugfs_run, t); +} + +static int ktf_debugfs_release(struct inode *inode, struct file *file) +{ + module_put(THIS_MODULE); + return single_release(inode, file); +} + +static const struct file_operations ktf_run_test_fops = { + .open = ktf_run_test_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +static int ktf_results_test_open(struct inode *inode, struct file *file) +{ + struct ktf_test *t; + + if (!try_module_get(THIS_MODULE)) + return -EIO; + + t = (struct ktf_test *)inode->i_private; + + return single_open(file, ktf_debugfs_result, t); +} + +static const struct file_operations ktf_results_test_fops = { + .open = ktf_results_test_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +static void _ktf_debugfs_destroy_test(struct ktf_test *t) +{ + if (!t) + return; + + tlog(T_DEBUG, "Destroying debugfs test %s", t->name); + debugfs_remove(t->debugfs.debugfs_results_test); + debugfs_remove(t->debugfs.debugfs_run_test); + memset(&t->debugfs, 0, sizeof(t->debugfs)); +} + +void ktf_debugfs_create_test(struct ktf_test *t) +{ + struct ktf_case *testset = ktf_case_find(t->tclass); + + if (!testset) + return; + + memset(&t->debugfs, 0, sizeof(t->debugfs)); + + t->debugfs.debugfs_results_test = + debugfs_create_file(t->name, S_IFREG | 0444, + testset->debugfs.debugfs_results_test, + t, &ktf_results_test_fops); + + if (t->debugfs.debugfs_results_test) { + t->debugfs.debugfs_run_test = + debugfs_create_file(t->name, S_IFREG | 0444, + testset->debugfs.debugfs_run_test, + t, &ktf_run_test_fops); + if (!t->debugfs.debugfs_run_test) { + _ktf_debugfs_destroy_test(t); + } else { + /* Take reference for test for debugfs */ + ktf_test_get(t); + } + } + /* Drop reference to testset from ktf_case_find(). */ + ktf_case_put(testset); +} + +void ktf_debugfs_destroy_test(struct ktf_test *t) +{ + _ktf_debugfs_destroy_test(t); + /* Release reference now debugfs files are gone. */ + ktf_test_put(t); +} + +static int ktf_results_testset_open(struct inode *inode, struct file *file) +{ + struct ktf_case *testset; + + if (!try_module_get(THIS_MODULE)) + return -EIO; + + testset = (struct ktf_case *)inode->i_private; + + return single_open(file, ktf_debugfs_results_all, testset); +} + +static const struct file_operations ktf_results_testset_fops = { + .open = ktf_results_testset_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +static int ktf_run_testset_open(struct inode *inode, struct file *file) +{ + struct ktf_case *testset; + + if (!try_module_get(THIS_MODULE)) + return -EIO; + + testset = (struct ktf_case *)inode->i_private; + + return single_open(file, ktf_debugfs_run_all, testset); +} + +static const struct file_operations ktf_run_testset_fops = { + .open = ktf_run_testset_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +static void _ktf_debugfs_destroy_testset(struct ktf_case *testset) +{ + debugfs_remove(testset->debugfs.debugfs_run_testset); + debugfs_remove(testset->debugfs.debugfs_run_test); + debugfs_remove(testset->debugfs.debugfs_results_testset); + debugfs_remove(testset->debugfs.debugfs_results_test); +} + +void ktf_debugfs_create_testset(struct ktf_case *testset) +{ + char tests_subdir[KTF_DEBUGFS_NAMESZ]; + const char *name = ktf_case_name(testset); + + memset(&testset->debugfs, 0, sizeof(testset->debugfs)); + + /* First add /sys/kernel/debug/ktf/[results|run]/<testset> */ + testset->debugfs.debugfs_results_testset = + debugfs_create_file(name, S_IFREG | 0444, + ktf_debugfs_resultsdir, + testset, &ktf_results_testset_fops); + if (!testset->debugfs.debugfs_results_testset) + goto err; + + testset->debugfs.debugfs_run_testset = + debugfs_create_file(name, S_IFREG | 0444, + ktf_debugfs_rundir, + testset, &ktf_run_testset_fops); + if (!testset->debugfs.debugfs_run_testset) + goto err; + + /* Now add parent directories for individual test result/run tests + * which live in + * /sys/kernel/debug/ktf/[results|run]/<testset>-tests/<testname> + */ + (void)snprintf(tests_subdir, sizeof(tests_subdir), "%s%s", + name, KTF_DEBUGFS_TESTS_SUFFIX); + + testset->debugfs.debugfs_results_test = + debugfs_create_dir(tests_subdir, ktf_debugfs_resultsdir); + if (!testset->debugfs.debugfs_results_test) + goto err; + + testset->debugfs.debugfs_run_test = + debugfs_create_dir(tests_subdir, ktf_debugfs_rundir); + if (!testset->debugfs.debugfs_run_test) + goto err; + + /* Take reference count for testset. One will do as we will always + * free testset debugfs resources together. + */ + ktf_case_get(testset); + return; +err: + _ktf_debugfs_destroy_testset(testset); +} + +void ktf_debugfs_destroy_testset(struct ktf_case *testset) +{ + tlog(T_DEBUG, "Destroying debugfs testset %s", ktf_case_name(testset)); + _ktf_debugfs_destroy_testset(testset); + /* Remove our debugfs reference cout to testset */ + ktf_case_put(testset); +} + +/* /sys/kernel/debug/ktf/coverage shows coverage statistics. */ +static int ktf_debugfs_cov(struct seq_file *seq, void *v) +{ + ktf_cov_seq_print(seq); + + return 0; +} + +static int ktf_cov_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -EIO; + + return single_open(file, ktf_debugfs_cov, NULL); +} + +static const struct file_operations ktf_cov_fops = { + .open = ktf_cov_open, + .read = seq_read, + .llseek = seq_lseek, + .release = ktf_debugfs_release, +}; + +void ktf_debugfs_cleanup(void) +{ + tlog(T_DEBUG, "Removing ktf debugfs dirs..."); + debugfs_remove(ktf_debugfs_cov_file); + debugfs_remove(ktf_debugfs_rundir); + debugfs_remove(ktf_debugfs_resultsdir); + debugfs_remove(ktf_debugfs_rootdir); +} + +void ktf_debugfs_init(void) +{ + ktf_debugfs_rootdir = debugfs_create_dir(KTF_DEBUGFS_ROOT, NULL); + if (!ktf_debugfs_rootdir) + goto err; + ktf_debugfs_rundir = debugfs_create_dir(KTF_DEBUGFS_RUN, + ktf_debugfs_rootdir); + if (!ktf_debugfs_rundir) + goto err; + ktf_debugfs_resultsdir = debugfs_create_dir(KTF_DEBUGFS_RESULTS, + ktf_debugfs_rootdir); + if (!ktf_debugfs_resultsdir) + goto err; + + ktf_debugfs_cov_file = debugfs_create_file(KTF_DEBUGFS_COV, + S_IFREG | 0444, + ktf_debugfs_rootdir, + NULL, + &ktf_cov_fops); + if (ktf_debugfs_cov_file) + return; +err: + terr("Could not init %s\n", KTF_DEBUGFS_ROOT); + ktf_debugfs_cleanup(); +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_debugfs.h b/tools/testing/selftests/ktf/kernel/ktf_debugfs.h new file mode 100644 index 0000000..4dab1ea --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_debugfs.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire alan.maguire@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_debugfs.h: Support for creating a debugfs representation of test + * sets/tests. + */ + +#ifndef KTF_DEBUGFS_H +#define KTF_DEBUGFS_H +#include <linux/debugfs.h> + +#define KTF_DEBUGFS_ROOT "ktf" +#define KTF_DEBUGFS_RUN "run" +#define KTF_DEBUGFS_RESULTS "results" +#define KTF_DEBUGFS_COV "coverage" +#define KTF_DEBUGFS_TESTS_SUFFIX "-tests" + +#define KTF_DEBUGFS_NAMESZ 256 + +struct ktf_test; +struct ktf_case; + +void ktf_debugfs_create_test(struct ktf_test *); +void ktf_debugfs_destroy_test(struct ktf_test *); +void ktf_debugfs_create_testset(struct ktf_case *); +void ktf_debugfs_destroy_testset(struct ktf_case *); +void ktf_debugfs_init(void); +void ktf_debugfs_cleanup(void); + + +#endif
On Tue, Aug 13, 2019 at 08:09:21AM +0200, Knut Omang wrote:
From: Alan Maguire alan.maguire@oracle.com
While test results is available via netlink from user space, sometimes it may be useful to be able to access the results from the kernel as well, for instance due to a crash. Make that possible via debugfs.
ktf_debugfs.h: Support for creating a debugfs representation of test
Signed-off-by: Alan Maguire alan.maguire@oracle.com Signed-off-by: Knut Omang knut.omang@oracle.com
tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 ++- 2 files changed, 390 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.h
diff --git a/tools/testing/selftests/ktf/kernel/ktf_debugfs.c b/tools/testing/selftests/ktf/kernel/ktf_debugfs.c new file mode 100644 index 0000000..a20fbd2 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_debugfs.c @@ -0,0 +1,356 @@ +/*
- Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
- Author: Alan Maguire alan.maguire@oracle.com
- SPDX-License-Identifier: GPL-2.0
Has to be the first line of the file, did you run this through checkpatch?
+static int ktf_run_test_open(struct inode *inode, struct file *file) +{
- struct ktf_test *t;
- if (!try_module_get(THIS_MODULE))
return -EIO;
This is an anti-pattern, and one guaranteed to not work properly. NEVER do this.
- t = (struct ktf_test *)inode->i_private;
- return single_open(file, ktf_debugfs_run, t);
+}
+static int ktf_debugfs_release(struct inode *inode, struct file *file) +{
- module_put(THIS_MODULE);
Same here, not ok.
- return single_release(inode, file);
+}
+static const struct file_operations ktf_run_test_fops = {
- .open = ktf_run_test_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = ktf_debugfs_release,
+};
+static int ktf_results_test_open(struct inode *inode, struct file *file) +{
- struct ktf_test *t;
- if (!try_module_get(THIS_MODULE))
return -EIO;
Nope!
And why -EIO? That is not an io issue.
+void ktf_debugfs_create_test(struct ktf_test *t) +{
- struct ktf_case *testset = ktf_case_find(t->tclass);
- if (!testset)
return;
- memset(&t->debugfs, 0, sizeof(t->debugfs));
- t->debugfs.debugfs_results_test =
debugfs_create_file(t->name, S_IFREG | 0444,
testset->debugfs.debugfs_results_test,
t, &ktf_results_test_fops);
- if (t->debugfs.debugfs_results_test) {
How can that variable ever be NULL (hint, it can not.)
t->debugfs.debugfs_run_test =
debugfs_create_file(t->name, S_IFREG | 0444,
testset->debugfs.debugfs_run_test,
t, &ktf_run_test_fops);
if (!t->debugfs.debugfs_run_test) {
_ktf_debugfs_destroy_test(t);
} else {
/* Take reference for test for debugfs */
ktf_test_get(t);
}
- }
Never test the result of any debugfs call, you do not need to. Just call it and move on, your code flow should NEVER be different with, or without, a successful debugfs call.
+static int ktf_run_testset_open(struct inode *inode, struct file *file) +{
- struct ktf_case *testset;
- if (!try_module_get(THIS_MODULE))
return -EIO;
Again no. I hate to know what code you copied this all from, as that code is very wrong. Do you have a pointer to that code anywhere so we can fix that up?
- testset = (struct ktf_case *)inode->i_private;
- return single_open(file, ktf_debugfs_run_all, testset);
+}
+static const struct file_operations ktf_run_testset_fops = {
- .open = ktf_run_testset_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = ktf_debugfs_release,
If you really care about module references you should be setting the owner of the module here.
+};
+static void _ktf_debugfs_destroy_testset(struct ktf_case *testset) +{
- debugfs_remove(testset->debugfs.debugfs_run_testset);
- debugfs_remove(testset->debugfs.debugfs_run_test);
- debugfs_remove(testset->debugfs.debugfs_results_testset);
- debugfs_remove(testset->debugfs.debugfs_results_test);
Why not just recursivly remove the directory? That way you do not have to keep track of any individual files.
+}
+void ktf_debugfs_create_testset(struct ktf_case *testset) +{
- char tests_subdir[KTF_DEBUGFS_NAMESZ];
- const char *name = ktf_case_name(testset);
- memset(&testset->debugfs, 0, sizeof(testset->debugfs));
- /* First add /sys/kernel/debug/ktf/[results|run]/<testset> */
- testset->debugfs.debugfs_results_testset =
debugfs_create_file(name, S_IFREG | 0444,
ktf_debugfs_resultsdir,
testset, &ktf_results_testset_fops);
- if (!testset->debugfs.debugfs_results_testset)
goto err;
Again, can never happen, and again, do not do different things depending on the result of a debugfs call.
- testset->debugfs.debugfs_run_testset =
debugfs_create_file(name, S_IFREG | 0444,
ktf_debugfs_rundir,
testset, &ktf_run_testset_fops);
- if (!testset->debugfs.debugfs_run_testset)
goto err;
Again, nope.
- /* Now add parent directories for individual test result/run tests
* which live in
* /sys/kernel/debug/ktf/[results|run]/<testset>-tests/<testname>
*/
- (void)snprintf(tests_subdir, sizeof(tests_subdir), "%s%s",
name, KTF_DEBUGFS_TESTS_SUFFIX);
why (void)?
- testset->debugfs.debugfs_results_test =
debugfs_create_dir(tests_subdir, ktf_debugfs_resultsdir);
- if (!testset->debugfs.debugfs_results_test)
goto err;
nope :)
- testset->debugfs.debugfs_run_test =
debugfs_create_dir(tests_subdir, ktf_debugfs_rundir);
- if (!testset->debugfs.debugfs_run_test)
goto err;
Nope :)
- /* Take reference count for testset. One will do as we will always
* free testset debugfs resources together.
*/
- ktf_case_get(testset);
- return;
+err:
- _ktf_debugfs_destroy_testset(testset);
+}
+void ktf_debugfs_destroy_testset(struct ktf_case *testset) +{
- tlog(T_DEBUG, "Destroying debugfs testset %s", ktf_case_name(testset));
- _ktf_debugfs_destroy_testset(testset);
- /* Remove our debugfs reference cout to testset */
- ktf_case_put(testset);
+}
+/* /sys/kernel/debug/ktf/coverage shows coverage statistics. */ +static int ktf_debugfs_cov(struct seq_file *seq, void *v) +{
- ktf_cov_seq_print(seq);
- return 0;
+}
+static int ktf_cov_open(struct inode *inode, struct file *file) +{
- if (!try_module_get(THIS_MODULE))
return -EIO;
{sigh} I'll stop reviewing now :)
thanks,
greg k-h
On Tue, 2019-08-13 at 10:21 +0200, Greg Kroah-Hartman wrote:
On Tue, Aug 13, 2019 at 08:09:21AM +0200, Knut Omang wrote:
From: Alan Maguire alan.maguire@oracle.com
While test results is available via netlink from user space, sometimes it may be useful to be able to access the results from the kernel as well, for instance due to a crash. Make that possible via debugfs.
ktf_debugfs.h: Support for creating a debugfs representation of test
Signed-off-by: Alan Maguire alan.maguire@oracle.com Signed-off-by: Knut Omang knut.omang@oracle.com
tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 ++- 2 files changed, 390 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.h
diff --git a/tools/testing/selftests/ktf/kernel/ktf_debugfs.c b/tools/testing/selftests/ktf/kernel/ktf_debugfs.c new file mode 100644 index 0000000..a20fbd2 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_debugfs.c @@ -0,0 +1,356 @@ +/*
- Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved.
- Author: Alan Maguire alan.maguire@oracle.com
- SPDX-License-Identifier: GPL-2.0
Has to be the first line of the file, did you run this through checkpatch?
Yes, the code has been subject to continuous integration which uses a version of my runchecks tool (https://lkml.org/lkml/2018/1/19/157) to ensure that it is not possible to "forget" to run checkpatch (or sparse, smatch doc.check for that sake)
Ironically though I fell victim to my own tooling here, as I postponed fixing the SPDX_LICENSE_TAG class of issues once that test appeared, while working on something else, and just forgot to re-enable it again..
+static int ktf_run_test_open(struct inode *inode, struct file *file) +{
- struct ktf_test *t;
- if (!try_module_get(THIS_MODULE))
return -EIO;
This is an anti-pattern, and one guaranteed to not work properly. NEVER do this.
Sorry, I didn't know this, and the origin is probably my responsibility.
I know the feeling of never being able to get rid of bad examples because they keep getting copied..
The pattern seemed to be widely used the first time I saw it, and although somewhat awkward, it seemed to be the standard way then, but as you know, my Infiniband driver ( https://github.com/oracle/linux-uek/blob/uek4/qu7/drivers/infiniband/hw/sif/...) unfortunately never made it to the scrutiny of LKML, since the hardware project got cancelled. The -EIO return value was also copied from merged kernel code back then.
I notice the discussion and your response here: http://linux-kernel.2935.n7.nabble.com/debugfs-and-module-unloading-td865175... I assume that means that protection against module unload while a debugfs file is open is now safe.
On older kernels, having this code in place is far better than an unprotected debugfs entry/exit - I have tested it extensively in the past :-)
Back when I first used it, I had this cool set of polymorphic debugfs file code to list the set of active MRs, CQs, QPs, AHs etc that the whole infiniband driver, database and hardware teams loved so much that multiple users ended up using it in multiple windows from within watch for live observations of state changes, and often also running driver load/unloads for testing purposes.
I perfectly agree with you that reducing the hole for a race condition is generally a bad idea, but from the above mail thread it seems that's the only available choice for older kernels?
(I am asking because I still want to be able to support rather old kernels with the github version of KTF)
Anyway, great to know that a better solution now exists!
We'll fix the rest of the issues below as well for the next version..
Thanks! Knut
- t = (struct ktf_test *)inode->i_private;
- return single_open(file, ktf_debugfs_run, t);
+}
+static int ktf_debugfs_release(struct inode *inode, struct file *file) +{
- module_put(THIS_MODULE);
Same here, not ok.
- return single_release(inode, file);
+}
+static const struct file_operations ktf_run_test_fops = {
- .open = ktf_run_test_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = ktf_debugfs_release,
+};
+static int ktf_results_test_open(struct inode *inode, struct file *file) +{
- struct ktf_test *t;
- if (!try_module_get(THIS_MODULE))
return -EIO;
Nope!
And why -EIO? That is not an io issue.
Agreed
+void ktf_debugfs_create_test(struct ktf_test *t) +{
- struct ktf_case *testset = ktf_case_find(t->tclass);
- if (!testset)
return;
- memset(&t->debugfs, 0, sizeof(t->debugfs));
- t->debugfs.debugfs_results_test =
debugfs_create_file(t->name, S_IFREG | 0444,
testset->debugfs.debugfs_results_test,
t, &ktf_results_test_fops);
- if (t->debugfs.debugfs_results_test) {
How can that variable ever be NULL (hint, it can not.)
t->debugfs.debugfs_run_test =
debugfs_create_file(t->name, S_IFREG | 0444,
testset->debugfs.debugfs_run_test,
t, &ktf_run_test_fops);
if (!t->debugfs.debugfs_run_test) {
_ktf_debugfs_destroy_test(t);
} else {
/* Take reference for test for debugfs */
ktf_test_get(t);
}
- }
Never test the result of any debugfs call, you do not need to. Just call it and move on, your code flow should NEVER be different with, or without, a successful debugfs call.
+static int ktf_run_testset_open(struct inode *inode, struct file *file) +{
- struct ktf_case *testset;
- if (!try_module_get(THIS_MODULE))
return -EIO;
Again no. I hate to know what code you copied this all from, as that code is very wrong. Do you have a pointer to that code anywhere so we can fix that up?
- testset = (struct ktf_case *)inode->i_private;
- return single_open(file, ktf_debugfs_run_all, testset);
+}
+static const struct file_operations ktf_run_testset_fops = {
- .open = ktf_run_testset_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = ktf_debugfs_release,
If you really care about module references you should be setting the owner of the module here.
+};
+static void _ktf_debugfs_destroy_testset(struct ktf_case *testset) +{
- debugfs_remove(testset->debugfs.debugfs_run_testset);
- debugfs_remove(testset->debugfs.debugfs_run_test);
- debugfs_remove(testset->debugfs.debugfs_results_testset);
- debugfs_remove(testset->debugfs.debugfs_results_test);
Why not just recursivly remove the directory? That way you do not have to keep track of any individual files.
+}
+void ktf_debugfs_create_testset(struct ktf_case *testset) +{
- char tests_subdir[KTF_DEBUGFS_NAMESZ];
- const char *name = ktf_case_name(testset);
- memset(&testset->debugfs, 0, sizeof(testset->debugfs));
- /* First add /sys/kernel/debug/ktf/[results|run]/<testset> */
- testset->debugfs.debugfs_results_testset =
debugfs_create_file(name, S_IFREG | 0444,
ktf_debugfs_resultsdir,
testset, &ktf_results_testset_fops);
- if (!testset->debugfs.debugfs_results_testset)
goto err;
Again, can never happen, and again, do not do different things depending on the result of a debugfs call.
- testset->debugfs.debugfs_run_testset =
debugfs_create_file(name, S_IFREG | 0444,
ktf_debugfs_rundir,
testset, &ktf_run_testset_fops);
- if (!testset->debugfs.debugfs_run_testset)
goto err;
Again, nope.
- /* Now add parent directories for individual test result/run tests
* which live in
* /sys/kernel/debug/ktf/[results|run]/<testset>-tests/<testname>
*/
- (void)snprintf(tests_subdir, sizeof(tests_subdir), "%s%s",
name, KTF_DEBUGFS_TESTS_SUFFIX);
why (void)?
- testset->debugfs.debugfs_results_test =
debugfs_create_dir(tests_subdir, ktf_debugfs_resultsdir);
- if (!testset->debugfs.debugfs_results_test)
goto err;
nope :)
- testset->debugfs.debugfs_run_test =
debugfs_create_dir(tests_subdir, ktf_debugfs_rundir);
- if (!testset->debugfs.debugfs_run_test)
goto err;
Nope :)
- /* Take reference count for testset. One will do as we will always
* free testset debugfs resources together.
*/
- ktf_case_get(testset);
- return;
+err:
- _ktf_debugfs_destroy_testset(testset);
+}
+void ktf_debugfs_destroy_testset(struct ktf_case *testset) +{
- tlog(T_DEBUG, "Destroying debugfs testset %s", ktf_case_name(testset));
- _ktf_debugfs_destroy_testset(testset);
- /* Remove our debugfs reference cout to testset */
- ktf_case_put(testset);
+}
+/* /sys/kernel/debug/ktf/coverage shows coverage statistics. */ +static int ktf_debugfs_cov(struct seq_file *seq, void *v) +{
- ktf_cov_seq_print(seq);
- return 0;
+}
+static int ktf_cov_open(struct inode *inode, struct file *file) +{
- if (!try_module_get(THIS_MODULE))
return -EIO;
{sigh} I'll stop reviewing now :)
thanks,
greg k-h
On Wed, Aug 14, 2019 at 07:17:07PM +0200, Knut Omang wrote:
I notice the discussion and your response here: http://linux-kernel.2935.n7.nabble.com/debugfs-and-module-unloading-td865175... I assume that means that protection against module unload while a debugfs file is open is now safe.
It should be, if you set the *owner field of your file_operations properly. Try it and see!
On older kernels, having this code in place is far better than an unprotected debugfs entry/exit - I have tested it extensively in the past :-)
Yes, it seems to work, but again, it really is racy and will fail. Please don't use it.
I perfectly agree with you that reducing the hole for a race condition is generally a bad idea, but from the above mail thread it seems that's the only available choice for older kernels?
I have no idea, but please, do not use that pattern of code as it is racy in all kernels, from all of time.
thanks,
greg k-h
On Thu, 2019-08-15 at 10:49 +0200, Greg Kroah-Hartman wrote:
On Wed, Aug 14, 2019 at 07:17:07PM +0200, Knut Omang wrote:
I notice the discussion and your response here: http://linux-kernel.2935.n7.nabble.com/debugfs-and-module-unloading-td865175... I assume that means that protection against module unload while a debugfs file is open is now safe.
It should be, if you set the *owner field of your file_operations properly. Try it and see!
Might be a case for a KTF selftest to play with the timing to increase the chance :) Wasn't able to make it crash with these simple, short files.
I notice I had set the .owner field correctly myself in that driver code I referred to, so that's a "copy regression".
On older kernels, having this code in place is far better than an unprotected debugfs entry/exit - I have tested it extensively in the past :-)
Yes, it seems to work, but again, it really is racy and will fail. Please don't use it.
I perfectly agree with you that reducing the hole for a race condition is generally a bad idea, but from the above mail thread it seems that's the only available choice for older kernels?
I have no idea, but please, do not use that pattern of code as it is racy in all kernels, from all of time.
Ok, will remove it :-)
I tried in vain to find the commit from Al Viro that made the code safe, to identify which kernels that are safe from this issue, but he has a **lot** of commits, do you have a clue for what/where to look?
It will be good to have a mention/comment on this for future reference, like the earliest kernel version where this is safe.
Maybe we can even get rid of some more of the remaining of these too.. (I notice there's 65 cases of 'if (!try_module_get(THIS_MODULE))' right now)
Thanks! Knut
thanks,
greg k-h
On Thu, Aug 15, 2019 at 12:35:26PM +0200, Knut Omang wrote:
On Thu, 2019-08-15 at 10:49 +0200, Greg Kroah-Hartman wrote:
I perfectly agree with you that reducing the hole for a race condition is generally a bad idea, but from the above mail thread it seems that's the only available choice for older kernels?
I have no idea, but please, do not use that pattern of code as it is racy in all kernels, from all of time.
Ok, will remove it :-)
I tried in vain to find the commit from Al Viro that made the code safe, to identify which kernels that are safe from this issue, but he has a **lot** of commits, do you have a clue for what/where to look?
It will be good to have a mention/comment on this for future reference, like the earliest kernel version where this is safe.
Always use a "newer" kernel to be "safe" and you will be fine :)
Maybe we can even get rid of some more of the remaining of these too.. (I notice there's 65 cases of 'if (!try_module_get(THIS_MODULE))' right now)
Something to put on a TODO list somewhere...
thanks,
greg k-h
From: Alan Maguire alan.maguire@oracle.com
Allows reports of code coverage and memory allocation.
ktf_cov.c: Code coverage support implementation for KTF. ktf_cov.h: Code coverage support interface for KTF.
Signed-off-by: Alan Maguire alan.maguire@oracle.com Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/kernel/ktf_cov.c | 690 ++++++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_cov.h | 94 +++- 2 files changed, 784 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_cov.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_cov.h
diff --git a/tools/testing/selftests/ktf/kernel/ktf_cov.c b/tools/testing/selftests/ktf/kernel/ktf_cov.c new file mode 100644 index 0000000..f917994 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_cov.c @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire alan.maguire@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_cov.c: Code coverage support implementation for KTF. + */ +#include <linux/kallsyms.h> +#include <linux/debugfs.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/stacktrace.h> +#ifdef CONFIG_SLUB +#include <linux/slub_def.h> +#endif /* CONFIG_SLUB */ +#ifdef CONFIG_SLAB +#include <linux/slab_def.h> +#endif +#include <linux/string.h> +#include <linux/kprobes.h> +#include <linux/ptrace.h> +#include "ktf.h" +#include "ktf_map.h" +#include "ktf_cov.h" + +/* It may seem odd that we use a refcnt field in ktf_cov_entry structures + * in addition to using krefcount management via the ktf_map. The reasoning + * here is that if we enable and then disable coverage, we do not want to + * purge the entry data as we likely want to examine counts after disabling + * coverage. So the first enable will add entries to the cov_entry map and + * subsequent disable/enables will simply update the entry's refcnt. The + * free function below should only be called therefore from cleanup context + * when the cov entries are finally removed from the cov_entry map. + */ +static void ktf_cov_entry_free(struct ktf_map_elem *elem) +{ + struct ktf_cov_entry *entry = container_of(elem, struct ktf_cov_entry, + kmap); + if (entry->refcnt > 0) + unregister_kprobe(&entry->kprobe); + kfree(entry); +} + +/* Comparison function is subtle. We want to be able to compare key1 + * and key2 here, where key1 may either be an existing object, in which + * case it has an address and size; or it may be an object offset, in which + * case k1's address will be the address with offset of size 0. In both + * cases for the -1 case we can simply check if k1's address is less than + * k2's. For the 1 case, we need to ensure that the address is >= + * k2's address and it's size, since this ensures the address does not + * fall within the object bounds. Finally we are left with the case + * that k1.address >= k2.address _and_ it falls within the bounds of k2, + * which we consider a match. For a concrete example of how this matching + * is used, see how we walk the stack of functions within the kmalloc + * kretprobe below: we will have a function + offset on the stack, and we + * want to see if this offset falls within a function in our coverage entry + * map. If it does, we track the allocation. The implicit assumption is + * no overlap between different objects. + */ +static int ktf_cov_obj_compare(const char *key1, const char *key2) +{ + struct ktf_cov_obj_key *k1 = (struct ktf_cov_obj_key *)key1; + struct ktf_cov_obj_key *k2 = (struct ktf_cov_obj_key *)key2; + + if (k1->address < k2->address) + return -1; + if (k1->address >= (k2->address + k2->size)) + return 1; + return 0; +} + +void ktf_cov_entry_get(struct ktf_cov_entry *entry) +{ + ktf_map_elem_get(&entry->kmap); +} + +void ktf_cov_entry_put(struct ktf_cov_entry *entry) +{ + ktf_map_elem_put(&entry->kmap); +} + +/* Global map for address-> symbol/module mapping. Sort via symbol address + * and size combination, see ktf_cov_obj_compare() above for comparison + * logic. + */ +static DEFINE_KTF_MAP(cov_entry_map, ktf_cov_obj_compare, ktf_cov_entry_free); + +struct ktf_cov_entry *ktf_cov_entry_find(unsigned long addr, unsigned long size) +{ + struct ktf_cov_obj_key k; + + k.address = addr; + k.size = size; + + return ktf_map_find_entry(&cov_entry_map, (char *)&k, + struct ktf_cov_entry, kmap); +} + +static void ktf_cov_free(struct ktf_map_elem *elem) +{ + struct ktf_cov *cov = container_of(elem, struct ktf_cov, kmap); + + kfree(cov); +} + +void ktf_cov_put(struct ktf_cov *cov) +{ + ktf_map_elem_put(&cov->kmap); +} + +/* Coverage object map. Just modules supported for now, sort by name. */ +static DEFINE_KTF_MAP(cov_map, NULL, ktf_cov_free); + +struct ktf_cov *ktf_cov_find(const char *module) +{ + return ktf_map_find_entry(&cov_map, module, struct ktf_cov, kmap); +} + +/* cache for memory objects used to track allocations */ +static struct kmem_cache *cov_mem_cache; + +static void ktf_cov_mem_free(struct ktf_map_elem *elem) +{ + struct ktf_cov_mem *m = container_of(elem, struct ktf_cov_mem, + kmap); + + kmem_cache_free(cov_mem_cache, m); +} + +/* Global map for tracking memory allocations */ +DEFINE_KTF_MAP(cov_mem_map, ktf_cov_obj_compare, ktf_cov_mem_free); +EXPORT_SYMBOL(cov_mem_map); + +struct ktf_cov_mem *ktf_cov_mem_find(unsigned long addr, unsigned long size) +{ + struct ktf_cov_obj_key k; + + k.address = addr; + k.size = size; + + return ktf_map_find_entry(&cov_mem_map, (char *)&k, + struct ktf_cov_mem, kmap); +} + +void ktf_cov_mem_get(struct ktf_cov_mem *m) +{ + ktf_map_elem_get(&m->kmap); +} + +void ktf_cov_mem_put(struct ktf_cov_mem *m) +{ + ktf_map_elem_put(&m->kmap); +} + +static void ktf_cov_mem_remove(struct ktf_cov_mem *m) +{ + ktf_map_remove_elem(&cov_mem_map, &m->kmap); +} + +/* Do not use ktf_cov_entry_find() here as we can get entry directly + * from probe address (as probe is first field in struct ktf_cov_entry). + * No reference counting issues should apply as when entry refcnt drops + * to 0 we unregister the kprobe prior to freeing the entry. + */ +static int ktf_cov_handler(struct kprobe *p, struct pt_regs *regs) +{ + struct ktf_cov_entry *entry = (struct ktf_cov_entry *)p; + + /* Make sure probe is ours... */ + if (!entry || entry->magic != KTF_COV_ENTRY_MAGIC) + return 0; + entry->count++; + if (entry->count == 1 && entry->cov) + entry->cov->count++; + return 0; +} + +static int ktf_cov_init_symbol(void *data, const char *name, + struct module *mod, unsigned long addr) +{ + struct ktf_cov_entry *entry; + struct ktf_cov *cov = data; + char buf[256]; + + if (!mod || !cov) + return 0; + + if (!try_module_get(mod)) + return 0; + + /* module_mutex is grabbed by register_kprobe() */ + mutex_unlock(&module_mutex); + + /* We only care about symbols for cov-specified module. */ + if (strcmp(mod->name, cov->kmap.key)) + goto out; + + /* We don't probe ourselves and functions called within probe ctxt. */ + if (strncmp(name, "ktf_cov", strlen("ktf_cov")) == 0 || + strcmp(name, "ktf_map_find") == 0) + goto out; + + /* Check if we're already covered for this module/symbol. */ + entry = ktf_cov_entry_find(addr, 0); + if (entry) { + tlog(T_DEBUG, "%s already present in coverage: %s", + name, entry->name); + ktf_cov_entry_put(entry); + goto out; + } + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + (void)strlcpy(entry->name, name, sizeof(entry->name)); + entry->magic = KTF_COV_ENTRY_MAGIC; + entry->cov = cov; + entry->refcnt = 1; + + entry->kprobe.pre_handler = ktf_cov_handler; + entry->kprobe.symbol_name = entry->name; + + /* Ugh - we try to register a kprobe as a means of determining + * if the symbol is a function. + */ + if (register_kprobe(&entry->kprobe) < 0) { + /* not a probe-able function */ + kfree(entry); + goto out; + } + entry->key.address = addr; + entry->key.size = ktf_symbol_size(addr); + (void)sprint_symbol(buf, entry->key.address); + if (ktf_map_elem_init(&entry->kmap, (char *)&entry->key) < 0 || + ktf_map_insert(&cov_entry_map, &entry->kmap) < 0) { + unregister_kprobe(&entry->kprobe); + kfree(entry); + goto out; + } + tlog(T_DEBUG, "Added %s/%s (%p, size %lu) to coverage: %s", + mod->name, entry->name, (void *)entry->kprobe.addr, + entry->key.size, buf); + + cov->total++; + ktf_cov_entry_put(entry); + +out: + mutex_lock(&module_mutex); + module_put(mod); + return 0; +} + +static int ktf_cov_kmem_cache_alloc_handler(struct kretprobe_instance *, + struct pt_regs *); + +static unsigned long register_kretprobe_size; + +/* Handler tracking allocations. Determine if any functions we are + * tracking coverage for (coverage entries) are on the stack; if so + * we track the allocation. + */ +static int ktf_cov_kmem_alloc_entry(struct ktf_cov_mem *m, unsigned long bytes) +{ + struct ktf_cov_entry *entry = NULL; + int n; + + /* We don't care about 0-length allocations. */ + if (!bytes) + return 0; + + /* Find first cov entry on stack to allow us to attribute traced + * allocation to first coverage entry we come across. + */ + m->nr_entries = stack_trace_save(m->stack_entries, KTF_COV_MAX_STACK_DEPTH, 1); + for (n = 0; n < m->nr_entries; n++) { + /* avoid recursive enter when allocating cov mem */ + if (m->stack_entries[n] == + (unsigned long)ktf_cov_kmem_cache_alloc_handler) + break; + /* ignore allocs as a result of registering probes */ + if (m->stack_entries[n] > + (unsigned long)register_kretprobe && + m->stack_entries[n] < ((unsigned long)register_kretprobe + + register_kretprobe_size)) + break; + entry = ktf_cov_entry_find(m->stack_entries[n], 0); + if (entry) + break; + } + if (!entry) { + m->nr_entries = 0; + return 0; + } + ktf_cov_entry_put(entry); + + m->key.size = bytes; + /* Have to wait until alloc returns to get key.address */ + + return 0; +} + +/* Handler tracking kmalloc allocations. */ +static int ktf_cov_kmalloc_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct ktf_cov_mem *m = (struct ktf_cov_mem *)ri->data; + unsigned long bytes = (unsigned long)KTF_ENTRY_PROBE_ARG0; + + return ktf_cov_kmem_alloc_entry(m, bytes); +} + +static int ktf_cov_kmem_cache_alloc_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct kmem_cache *cache = + (struct kmem_cache *)KTF_ENTRY_PROBE_ARG0; + struct ktf_cov_mem *m = (struct ktf_cov_mem *)ri->data; + unsigned long bytes; + + if (!cache) + return 0; + + bytes = cache->object_size; + if (cache == cov_mem_cache) + return 0; + return ktf_cov_kmem_alloc_entry(m, bytes); +} + +static int ktf_cov_kmem_alloc_return(struct ktf_cov_mem *m, + unsigned long ret) +{ + struct ktf_cov_mem *mm; + + m->key.address = ret; + mm = kmem_cache_alloc(cov_mem_cache, GFP_NOWAIT); + if (!mm) + return 0; + memcpy(mm, m, sizeof(*mm)); + if (ktf_map_elem_init(&mm->kmap, (char *)&mm->key) < 0 || + ktf_map_insert(&cov_mem_map, &mm->kmap) < 0) { + /* This can happen as inexplicably the same probe + * can fire twice for _kmalloc; this results in + * us attempting to add the same address twice, with + * the result that we get -EEXIST from ktf_map_insert() + * the second time. Annoying but the end result is + * we track the allocation once, which is what we want. + */ + terr("Failed to insert cov_mem %p", (void *)ret); + kmem_cache_free(cov_mem_cache, mm); + } + tlog(T_DEBUG, "cov_mem: tracking allocation %p", (void *)m->key.address); + m->nr_entries = 0; + return 0; +} + +static int ktf_cov_kmalloc_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct ktf_cov_mem *m = (struct ktf_cov_mem *)ri->data; + unsigned long ret = regs_return_value(regs); + + if (m->nr_entries) + return ktf_cov_kmem_alloc_return(m, ret); + return 0; +} + +static int ktf_cov_kmem_cache_alloc_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct kmem_cache *cache = + (struct kmem_cache *)KTF_ENTRY_PROBE_ARG0; + struct ktf_cov_mem *m = (struct ktf_cov_mem *)ri->data; + unsigned long ret = regs_return_value(regs); + + if (cache == cov_mem_cache) + return 0; + + if (m->nr_entries) + return ktf_cov_kmem_alloc_return(m, ret); + return 0; +} + +static int ktf_cov_kmem_free_entry(unsigned long tofree) +{ + struct ktf_cov_mem *m; + + if (!tofree) + return 0; + + m = ktf_cov_mem_find(tofree, 0); + if (m) { + tlog(T_DEBUG, "cov_mem: freeing allocation %p", + (void *)m->key.address); + ktf_cov_mem_remove(m); + ktf_cov_mem_put(m); + } + return 0; +} + +static int ktf_cov_kfree_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + unsigned long tofree = (unsigned long)KTF_ENTRY_PROBE_ARG0; + + if (!tofree) + return 0; + + return ktf_cov_kmem_free_entry(tofree); +} + +static int ktf_cov_kmem_cache_free_entry_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + struct kmem_cache *cache = + (struct kmem_cache *)KTF_ENTRY_PROBE_ARG0; + unsigned long tofree = (unsigned long)KTF_ENTRY_PROBE_ARG1; + + if (!tofree || cache == cov_mem_cache) + return 0; + + return ktf_cov_kmem_free_entry(tofree); +} + +static struct kretprobe cov_mem_probes[] = { + { .kp = { .symbol_name = "__kmalloc" }, + .handler = ktf_cov_kmalloc_handler, + .entry_handler = ktf_cov_kmalloc_entry_handler, + .data_size = sizeof(struct ktf_cov_mem), + .maxactive = 0, /* assumes default value */ + }, + { .kp = { .symbol_name = "kmem_cache_alloc" }, + .handler = ktf_cov_kmem_cache_alloc_handler, + .entry_handler = ktf_cov_kmem_cache_alloc_entry_handler, + .data_size = sizeof(struct ktf_cov_mem), + .maxactive = 0, /* assumes default value */ + }, + { .kp = { .symbol_name = "kfree" }, + .handler = NULL, + .entry_handler = ktf_cov_kfree_entry_handler, + .data_size = 0, + .maxactive = 0, /* assumes default value */ + }, + { .kp = { .symbol_name = "kmem_cache_free" }, + .handler = NULL, + .entry_handler = ktf_cov_kmem_cache_free_entry_handler, + .data_size = 0, + .maxactive = 0, /* assumes default value */ + } +}; + +static int cov_opt_mem_cnt; + +static int ktf_cov_init_opts(struct ktf_cov *cov) +{ + int i, ret = 0; + + if (cov->opts & KTF_COV_OPT_MEM && ++cov_opt_mem_cnt == 1) { + if (!cov_mem_cache) { + cov_mem_cache = + kmem_cache_create("ktf_cov_mem_cache", + sizeof(struct ktf_cov_mem), 0, + SLAB_HWCACHE_ALIGN | SLAB_PANIC, + NULL); + + if (!cov_mem_cache) + return -ENOMEM; + } + + for (i = 0; i < ARRAY_SIZE(cov_mem_probes); i++) { + /* reset in case we're re-registering */ + cov_mem_probes[i].kp.addr = NULL; + cov_mem_probes[i].kp.flags = 0; + ret = register_kretprobe(&cov_mem_probes[i]); + if (ret) { + tlog(T_DEBUG, + "%d: failed to register retprobe for %s", + ret, cov_mem_probes[i].kp.symbol_name); + return ret; + } + } + } + + return ret; +} + +static void ktf_cov_cleanup_opts(struct ktf_cov *cov) +{ + int i; + + if (cov->opts & KTF_COV_OPT_MEM && --cov_opt_mem_cnt == 0) { + for (i = 0; i < ARRAY_SIZE(cov_mem_probes); i++) { + if (cov_mem_probes[i].nmissed > 0) { + tlog(T_INFO, "%s: retprobe missed %d.", + cov_mem_probes[i].kp.symbol_name, + cov_mem_probes[i].nmissed); + } + if (cov_mem_probes[i].kp.addr) + unregister_kretprobe(&cov_mem_probes[i]); + } + } +} + +/* If the module we are monitoring coverage for was unloaded/reloaded + * while coverage was disabled, we can end up re-enabling kprobes at + * different addresses for the same function. The problem is however + * we reference coverage entries by their address in the coverage + * entry map, so we need to clean it up to reflect the new locations + * of the probes. So we remove/re-add the entries with the updated + * addresses. It would obviously be easier to just remove the entries + * on coverage disable, but that limits our ability to examine coverage + * data - a common pattern is enable coverage, run test(s), disable + * coverage, check coverage data. + */ +static void ktf_cov_update_entries(const char *name, struct ktf_cov *cov) +{ + struct ktf_cov_entry *entry = ktf_map_first_entry(&cov_entry_map, + struct ktf_cov_entry, + kmap); + + while (entry) { + if (entry->cov != cov || + (unsigned long)entry->kprobe.addr == entry->key.address) { + entry = ktf_map_next_entry(entry, kmap); + continue; + } + + /* Address has changed; remove entry with old address as key + * and re-add with new address/size as key (size may have + * changed if module was re-compiled). + */ + ktf_map_remove_elem(&cov_entry_map, &entry->kmap); + entry->key.address = (unsigned long)entry->kprobe.addr; + entry->key.size = ktf_symbol_size(entry->key.address); + if (ktf_map_elem_init(&entry->kmap, (char *)&entry->key) < 0 || + ktf_map_insert(&cov_entry_map, &entry->kmap) < 0) { + tlog(T_DEBUG, "Failed to add %s/%s", name, entry->name); + unregister_kprobe(&entry->kprobe); + entry->refcnt--; + entry = ktf_map_next_entry(entry, kmap); + } else { + tlog(T_DEBUG, "Added %s/%s (%p, size %lu) to coverage", + name, entry->name, (void *)entry->key.address, + entry->key.size); + /* Map has changed, reset to root. */ + entry = ktf_map_first_entry(&cov_entry_map, + struct ktf_cov_entry, kmap); + } + } +} + +int ktf_cov_enable(const char *name, unsigned int opts) +{ + struct ktf_cov *cov = ktf_cov_find(name); + struct ktf_cov_entry *entry; + int ret = 0; + +#ifndef KTF_PROBE_SUPPORT + return -ENOTSUPP; +#endif + if (!cov) { + cov = kzalloc(sizeof(*cov), GFP_KERNEL); + if (!cov) + return -ENOMEM; + + cov->type = KTF_COV_TYPE_MODULE; + cov->opts = opts; + if (ktf_map_elem_init(&cov->kmap, name) < 0 || + ktf_map_insert(&cov_map, &cov->kmap) < 0) { + tlog(T_DEBUG, "cov %s already present", cov->kmap.key); + kfree(cov); + return -EEXIST; + } + register_kretprobe_size = + ktf_symbol_size((unsigned long)register_kretprobe); + mutex_lock(&module_mutex); + kallsyms_on_each_symbol(ktf_cov_init_symbol, cov); + mutex_unlock(&module_mutex); + } else { + ktf_map_for_each_entry(entry, &cov_entry_map, kmap) { + if (entry->cov != cov) + continue; + if (++entry->refcnt == 1) { + /* reset kprobe as we're re-registering */ + memset(&entry->kprobe, 0, + sizeof(entry->kprobe)); + entry->kprobe.pre_handler = ktf_cov_handler; + entry->kprobe.symbol_name = entry->name; + ret = register_kprobe(&entry->kprobe); + if (ret) { + tlog(T_DEBUG, "Failed to add %s/%s", + name, entry->name); + entry->refcnt--; + } + } + } + /* Probe addresses/function sizes for functions may have + * changed if module was unloaded/reloaded - entry map + * needs to be updated to use new address/size as key. + */ + ktf_cov_update_entries(name, cov); + } + + ret = ktf_cov_init_opts(cov); + + ktf_cov_put(cov); + + return ret; +} + +void ktf_cov_disable(const char *module) +{ + struct ktf_cov *cov = ktf_cov_find(module); + struct ktf_cov_entry *entry; + +#ifndef KTF_PROBE_SUPPORT + return; +#endif + + if (!cov) + return; + + ktf_map_for_each_entry(entry, &cov_entry_map, kmap) { + if (entry->cov == cov) { + if (--entry->refcnt == 0) { + unregister_kprobe(&entry->kprobe); + tlog(T_DEBUG, "Removed coverage %s/%s", + cov->kmap.key, entry->name); + } + } + } + ktf_cov_cleanup_opts(cov); + ktf_cov_put(cov); +} + +static void ktf_cov_mem_seq_print(struct seq_file *seq) +{ + struct ktf_cov_mem *m; + char buf[256]; + int n; + + seq_puts(seq, "\nMemory in use allocated by covered functions:\n\n"); + seq_printf(seq, "%44s %16s %10s\n", "ALLOCATION STACK", "ADDRESS", + "SIZE"); + ktf_for_each_cov_mem(m) { + for (n = 0; n < m->nr_entries; n++) { + sprint_symbol(buf, m->stack_entries[n]); + seq_printf(seq, "%44s", buf); + if (n == 0) + seq_printf(seq, " %16p %10lu", + (void *)m->key.address, + m->key.size); + seq_puts(seq, "\n"); + } + seq_puts(seq, "\n"); + } +} + +void ktf_cov_seq_print(struct seq_file *seq) +{ + struct ktf_cov_entry *entry; + struct ktf_cov *cov; + + seq_printf(seq, "%10s %44s %10s\n", "MODULE", "#FUNCTIONS", + "#CALLED"); + ktf_map_for_each_entry(cov, &cov_map, kmap) + seq_printf(seq, "%10s %44d %10d\n", + cov->kmap.key, cov->total, cov->count); + + seq_printf(seq, "\n%10s %44s %10s\n", "MODULE", "FUNCTION", "COUNT"); + ktf_map_for_each_entry(entry, &cov_entry_map, kmap) + seq_printf(seq, "%10s %44s %10d\n", + entry->cov ? entry->cov->kmap.key : "-", + entry->name, entry->count); + + ktf_cov_mem_seq_print(seq); +} + + +void ktf_cov_cleanup(void) +{ + struct ktf_cov *cov; + char name[KTF_MAX_KEY]; + + ktf_map_for_each_entry(cov, &cov_map, kmap) { + ktf_cov_disable(ktf_map_elem_name(&cov->kmap, name)); + } + ktf_map_delete_all(&cov_map); + ktf_map_delete_all(&cov_entry_map); + ktf_map_delete_all(&cov_mem_map); + kmem_cache_destroy(cov_mem_cache); +} diff --git a/tools/testing/selftests/ktf/kernel/ktf_cov.h b/tools/testing/selftests/ktf/kernel/ktf_cov.h new file mode 100644 index 0000000..56ae753 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_cov.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire alan.maguire@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_cov.h: Code coverage support interface for KTF. + */ + +#ifndef KTF_COV_H +#define KTF_COV_H + +#include <linux/debugfs.h> +#include <linux/module.h> +#include <linux/kprobes.h> +#include "ktf.h" +#include "ktf_map.h" + +enum ktf_cov_type { + KTF_COV_TYPE_MODULE, + KTF_COV_TYPE_MAX, +}; + +struct ktf_cov { + struct ktf_map_elem kmap; + enum ktf_cov_type type; /* only modules supported for now. */ + int count; /* number of unique functions called */ + int total; /* total number of functions */ + unsigned int opts; +}; + +/* Key for coverage entries (functions) consists in function address _and_ + * size - this allows us to find offsets in function on stack. Also used + * to track allocated memory - allocated address + size. + */ +struct ktf_cov_obj_key { + unsigned long address; + unsigned long size; +}; + +#define KTF_COV_ENTRY_MAGIC 0xc07e8a5e +struct ktf_cov_entry { + struct kprobe kprobe; + int magic; /* magic number identifying entry */ + struct ktf_map_elem kmap; + char name[KTF_MAX_KEY]; + struct ktf_cov_obj_key key; + struct ktf_cov *cov; + struct ktf_map cov_mem; + int refcnt; + int count; +}; + +#define KTF_COV_MAX_STACK_DEPTH 32 + +struct ktf_cov_mem { + struct ktf_map_elem kmap; + struct ktf_cov_obj_key key; + unsigned long flags; + unsigned int nr_entries; + unsigned long stack_entries[KTF_COV_MAX_STACK_DEPTH]; +}; + +#define KTF_COV_MEM_IGNORE 0x1 /* avoid recursive enter */ + +struct ktf_cov_mem_probe { + const char *name; + struct kretprobe kretprobe; +}; + +extern struct ktf_map cov_mem_map; + +#define ktf_for_each_cov_mem(pos) \ + ktf_map_for_each_entry(pos, &cov_mem_map, kmap) + +struct ktf_cov_entry *ktf_cov_entry_find(unsigned long, unsigned long); +void ktf_cov_entry_put(struct ktf_cov_entry *); +void ktf_cov_entry_get(struct ktf_cov_entry *); + +struct ktf_cov *ktf_cov_find(const char *); +void ktf_cov_put(struct ktf_cov *); +void ktf_cov_get(struct ktf_cov *); + +struct ktf_cov_mem *ktf_cov_mem_find(unsigned long, unsigned long); +void ktf_cov_mem_put(struct ktf_cov_mem *); +void ktf_cov_mem_get(struct ktf_cov_mem *); + +void ktf_cov_seq_print(struct seq_file *); +void ktf_cov_cleanup(void); + +int ktf_cov_enable(const char *, unsigned int); +void ktf_cov_disable(const char *); + +#endif
An implementation of configurable contexts for exchanging network information, to make it easier to run network tests potentially involving more than one kernel.
ktf_netctx.h: Configurable context setup for multinode network tests
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/kernel/ktf_netctx.c | 132 +++++++++++++++++- tools/testing/selftests/ktf/kernel/ktf_netctx.h | 64 ++++++++- 2 files changed, 196 insertions(+) create mode 100644 tools/testing/selftests/ktf/kernel/ktf_netctx.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_netctx.h
diff --git a/tools/testing/selftests/ktf/kernel/ktf_netctx.c b/tools/testing/selftests/ktf/kernel/ktf_netctx.c new file mode 100644 index 0000000..84ef6ac --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_netctx.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_netcfg.h: Configurable context setup for multinode network tests + * + */ + +#include "ktf.h" +#include "ktf_netctx.h" +#include <linux/in.h> + +/* Configuration callback to configure a network context */ + +int ktf_netctx_cb(struct ktf_context *ctx, const void *data, size_t data_sz) +{ + struct ktf_netctx *nc = container_of(ctx, struct ktf_netctx, k); + struct ktf_addrinfo *kai = (struct ktf_addrinfo *)data; + short n = kai->n; + size_t param_sz; + + if (n < 2) { + terr("Unsupported number of nodes (%d) - must be at least 2", n); + return -EINVAL; + } + + param_sz = sizeof(*kai) + sizeof(kai->a) * (n - 2); + + if (n > nc->max_nodes || n < nc->min_nodes) { + terr("Unsupported number of nodes (%d) - must be between %d and %d!", + n, nc->min_nodes, nc->max_nodes); + return -EINVAL; + } + + if (param_sz != data_sz) { + terr("Expected %lu bytes of parameter data, received %lu!", + param_sz, data_sz); + return -EINVAL; + } + + if (nc->a && nc->a_sz != data_sz) { + kfree(nc->a); + nc->a = NULL; + } + + if (!nc->a) { + nc->a = kzalloc(data_sz, GFP_KERNEL); + if (!nc->a) + return -ENOMEM; + } + + memcpy(nc->a, kai, data_sz); + return 0; +} +EXPORT_SYMBOL(ktf_netctx_cb); + +void ktf_netctx_cleanup(struct ktf_context *ctx) +{ + struct ktf_netctx *nc = container_of(ctx, struct ktf_netctx, k); + + kfree(nc->a); +} +EXPORT_SYMBOL(ktf_netctx_cleanup); + +/* Make network contexts dynamically allocatable from user mode + * Caller must supply desired values for callback functions in @nct. + */ +int ktf_netctx_enable(struct ktf_handle *handle, struct ktf_netctx_type *nct, + short min_nodes, short max_nodes) +{ + struct ktf_context *lo_ctx; + struct ktf_addrinfo ai = { + .n = 2, + .rank = 0 + }; + int ret; + int i; + + ret = ktf_handle_add_ctx_type(handle, &nct->t); + if (ret) + return ret; + + nct->min_nodes = min_nodes; + nct->max_nodes = max_nodes; + strcpy(nct->t.name, "netctx"); + + for (i = 0; i < 2; i++) { + struct sockaddr_in *ai_in = (struct sockaddr_in *)&ai.a[i].addr; + + ai.a[i].addr.ss_family = AF_INET; + strcpy(ai.a[i].ifname, "lo"); + ai_in->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + + /* create and configure the loopback network context */ + lo_ctx = ktf_context_add_from(handle, "lo", &nct->t); + if (!lo_ctx) + return -ENOMEM; + + ret = ktf_context_set_config(lo_ctx, &ai, sizeof(ai)); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(ktf_netctx_enable); + +struct sockaddr_storage *ktf_netctx_addr(struct ktf_netctx *ctx, short rank) +{ + return &ctx->a->a[rank].addr; +} +EXPORT_SYMBOL(ktf_netctx_addr); + +const char *ktf_netctx_ifname(struct ktf_netctx *ctx, short rank) +{ + return ctx->a->a[rank].ifname; +} +EXPORT_SYMBOL(ktf_netctx_ifname); + +short ktf_netctx_rank(struct ktf_netctx *ctx) +{ + return ctx->a->rank; +} +EXPORT_SYMBOL(ktf_netctx_rank); + +short ktf_netctx_n(struct ktf_netctx *ctx) +{ + return ctx->a->n; +} +EXPORT_SYMBOL(ktf_netctx_n); diff --git a/tools/testing/selftests/ktf/kernel/ktf_netctx.h b/tools/testing/selftests/ktf/kernel/ktf_netctx.h new file mode 100644 index 0000000..414c744 --- /dev/null +++ b/tools/testing/selftests/ktf/kernel/ktf_netctx.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_netctx.h: Configurable context setup for multinode network tests + * + * KTF implements handling on the kernel side for this but leaves + * user space implementation to construct the corresponding + * stuct ktf_addrinfo parameter block + */ + +#ifndef _KTF_NETCTX_H +#define _KTF_NETCTX_H + +/* KTF matches against this type id as a possible discriminator: */ +#define KTF_NETCTX_TYPE_ID 0x2222 + +#define IFNAMSZ 16 + +struct ktf_peer_address +{ + struct sockaddr_storage addr; /* Address to use for this peer */ + char ifname[IFNAMSZ]; /* Local name of the interface with this address at peer */ +}; + +struct ktf_addrinfo +{ + short n; /* Number of nodes involved, including the local */ + short rank; /* Index into ktf_peer_address that corresponds to local host */ + struct ktf_peer_address a[2]; /* KTF expects size n instead of 2 here */ +}; + +#ifdef __KERNEL__ + +struct ktf_netctx { + struct ktf_context k; + struct ktf_addrinfo *a; /* Addr.info dyn.allocated based on incoming data */ + size_t a_sz; /* Size of the allocation in a, if any */ + short min_nodes; /* Minimum number of nodes for this context */ + short max_nodes; /* Maximum number of nodes this context supports */ +}; + +struct ktf_netctx_type { + struct ktf_context_type t; + short min_nodes; /* Minimum number of nodes for the context type */ + short max_nodes; /* Maximum number of nodes for the context type */ +}; + +int ktf_netctx_enable(struct ktf_handle *handle, struct ktf_netctx_type *nct, + short min_nodes, short max_nodes); + +int ktf_netctx_cb(struct ktf_context *ctx, const void *data, size_t data_sz); +void ktf_netctx_cleanup(struct ktf_context *ctx); + +struct sockaddr_storage *ktf_netctx_addr(struct ktf_netctx *ctx, short rank); +const char *ktf_netctx_ifname(struct ktf_netctx *ctx, short rank); +short ktf_netctx_rank(struct ktf_netctx *ctx); +short ktf_netctx_n(struct ktf_netctx *ctx); + +#endif + +#endif
Takes an input file foo.txt with a list of symbols on the form:
#module foo #header foo.h private_foo_symbol_1 private_foo_symbol_2
and creates usable definitions to access these (requires CONFIG_KALLSYMS_ALL). This is useful to be able to easily and cleanly test internal interfaces of a module. Examples follows in the selftests later in the series.
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/scripts/resolve | 188 +++++++++++++++++++++- 1 file changed, 188 insertions(+) create mode 100755 tools/testing/selftests/ktf/scripts/resolve
diff --git a/tools/testing/selftests/ktf/scripts/resolve b/tools/testing/selftests/ktf/scripts/resolve new file mode 100755 index 0000000..74005f7 --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/resolve @@ -0,0 +1,188 @@ +#!/usr/bin/python + +# Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. +# Author: Knut Omang knut.omang@oracle.com +# +# SPDX-License-Identifier: GPL-2.0 +# +# A script to generate code and header definitions for +# module and kernel symbols not directly exposed to KTF. +# (See the documentation for KTF for details) +# + +import os, sys, re, shutil, string, ConfigParser + +def usage(): + print "Usage: resolve symbolfile outputfile" + exit(0) + +class FuncInfo: + def __init__(self, sym, re_sym, re_decl): + self.symbol = sym + self.re_sym = re_sym + self.re_decl = re_decl + + def __repr__(self): + return "FuncInfo: [%s: %s]" % (self.symbol, self.re_decl) + +class Module: + def __init__(self, name, header): + self.cur_header = header + self.prefix = "Z" + self.name = name + self.symbols = {} # Input: headerfile -> symbol list + self.func_def = [] # FuncInfo list + self.debug = False + all_modules.append(self) + + def log(self, str): + if self.debug: + print str + + def SetHeader(self, header): + self.cur_header = header + + def AddSymbol(self, sym): + try: + h = self.symbols[self.cur_header] + h.append(sym) + except: + self.symbols[self.cur_header] = [sym] + + # Open along include path: + def Open(self, filename): + for p in includepath: + try: + f = os.path.join(p, filename) + self.log(" -- trying " + f) + header = open(f,'r') + return header + except: + continue + sys.stderr.write(" ** unable to open "%s"\n" % filename) + return None + + # Parse the relevant header files for function defs: + def ParseDefs(self): + for hf in self.symbols.keys(): + self.log(" ** Parsing %s:" % hf) + header = self.Open(hf) + if header == None: + return + content = header.read() + symbols = self.symbols[hf] + types = r"(extern|u8|u16|u32|u64|int|long|size_t|off_t|loff_t|void|struct|union\s)(.*[*\s]" + funor = ")(" + "|".join(symbols) + r")(([^)]+);)$" + tt = types + funor + s = re.compile(tt, re.MULTILINE) + miter = s.finditer(content) + s_count = 0 + for m in miter: + sym = m.group(3) + re_name = "_".join([self.prefix, sym]) + re_decl = "%s%s(*%s)%s" % (m.group(1), m.group(2), re_name, m.group(4)) + self.func_def.append(FuncInfo(sym, re_name, re_decl)) + s_count = s_count + 1 + + if s_count != len(symbols): + print " ** Warning: File %s: Found %d definitions from %d symbols!" % \ + (hf, s_count, len(symbols)) + print " ** - please check/fix output manually!" + + # Output functions: + def write_funcs(self, file): + for fi in self.func_def: + file.write("\t%s\n"% fi.re_decl) + + def write_defines(self, file): + for fi in self.func_def: + file.write("#define %s ktf_syms.%s\n" % (fi.symbol, fi.re_sym)) + + def write_resolve_calls(self, file): + for fi in self.func_def: + file.write("\tktf_resolve_symbol(%s, %s);\n" % (self.name, fi.symbol)) + +usage_h = False +my_argv = [] +includepath = [""] + +for arg in sys.argv[1:]: + if arg == "-h": + usage_h = True + continue + incl = re.match(r"-I([^\s]+)", arg) + if incl != None: + includepath.append(incl.group(1)) + continue + genopt = re.match(r"-([^\s]+)", arg) + if genopt != None: + # Silently ignore other cc options as we accept cc-flags-y: + # + continue + my_argv.append(arg) + +# Main program: + +if len(my_argv) != 2 or usage_h: + usage() + +symfile = my_argv[0] +outputfile = my_argv[1] + +all_modules = [] +module = Module("kernel", None) # Default, at the top of the file is the main kernel symbols +header = None # A header directive is required before any symbols + +try: + file = open(symfile, 'r') +except: + print "Unable to open config file "%s"" % symfile + exit(1) +for line in file: + match = re.match(r"^#(\w+) ([\w.]+)\s*$", line) + if match != None: + cmd = match.group(1) + value = match.group(2) + if cmd == "module": + module = Module(value, header) + elif cmd == "header": + header = value + module.SetHeader(header) + else: + raise ResolveError("Unknown directive "%s"" % cmd) + #print "%s set to %s" % (cmd, value) + continue + match = re.match(r"\s*(\w+)\s*", line) + if match != None: + s = match.group(1) + module.AddSymbol(s) + +for m in all_modules: + m.ParseDefs() + +try: + output = open(outputfile, "w") +except: + print "Failed to open output header file "%s"" % outputfile +output.write('#ifndef _KTF_RESOLVE_H\n#define _KTF_RESOLVE_H\n') +output.write('#include "ktf.h"\n\nstruct ktf_syms {\n') + +for m in all_modules: + m.write_funcs(output) + +output.write('};\n\n') + +for m in all_modules: + m.write_defines(output) + +output.write('\n\nextern struct ktf_syms ktf_syms;\n') +output.write('\nint ktf_resolve_symbols(void);\n#ifndef KTF_CLIENT\n') +output.write('struct ktf_syms ktf_syms;\n\n') + +output.write('\nint ktf_resolve_symbols(void)\n{\n') + +for m in all_modules: + m.write_resolve_calls(output) + +output.write('\treturn 0;\n}\n\n#endif /* !KTF_CLIENT */\n#endif /* _KTF_RESOLVE_H */ \n') +output.close()
Signed-off-by: Knut Omang knut.omang@oracle.com --- Documentation/dev-tools/ktf/concepts.rst | 242 ++++++++++++++- Documentation/dev-tools/ktf/debugging.rst | 248 +++++++++++++++- Documentation/dev-tools/ktf/examples.rst | 26 ++- Documentation/dev-tools/ktf/features.rst | 307 ++++++++++++++++++- Documentation/dev-tools/ktf/implementation.rst | 70 ++++- Documentation/dev-tools/ktf/index.rst | 14 +- Documentation/dev-tools/ktf/installation.rst | 73 ++++- Documentation/dev-tools/ktf/introduction.rst | 134 ++++++++- Documentation/dev-tools/ktf/progref.rst | 144 ++++++++- 9 files changed, 1258 insertions(+) create mode 100644 Documentation/dev-tools/ktf/concepts.rst create mode 100644 Documentation/dev-tools/ktf/debugging.rst create mode 100644 Documentation/dev-tools/ktf/examples.rst create mode 100644 Documentation/dev-tools/ktf/features.rst create mode 100644 Documentation/dev-tools/ktf/implementation.rst create mode 100644 Documentation/dev-tools/ktf/index.rst create mode 100644 Documentation/dev-tools/ktf/installation.rst create mode 100644 Documentation/dev-tools/ktf/introduction.rst create mode 100644 Documentation/dev-tools/ktf/progref.rst
diff --git a/Documentation/dev-tools/ktf/concepts.rst b/Documentation/dev-tools/ktf/concepts.rst new file mode 100644 index 0000000..9b9ef1a --- /dev/null +++ b/Documentation/dev-tools/ktf/concepts.rst @@ -0,0 +1,242 @@ + +5. KTF Basic Concepts +--------------------- + +Tests and test suites +********************* + +The simplest form of test is to just specify:: + + TEST(suite_name, test_name) + { + <test code and assertions go here> + } + +A KTF test is declared with TEST() or TEST_F(), which both +takes both a test suite name and a test name, which are two different +name spaces. Consequently, each test belongs to one test suite, and +the test suites are created based on what tests that exists. +A test suite is just a container of tests which in user space +contributes to the extended name of a test. Test names must be +unique within a suite, and test names must also be unique within a +source file, since the test name is the only parameter needed +when adding a test. + +All tests must be added using ADD_TEST or ADD_LOOP_TEST to be visible +to KTF's runtime framework. This allows tests to be declared while +under development, but not added (or the ADD_TEST could be commented +out) if the test or the kernel module under test is not ready +yet for some reason. + +Test fixtures +************* + +As in other unit test frameworks, a test fixture is a mechanism to +allow several tests to run under the same conditions, in that setup +and teardown is done before and after each test. In KTF a test fixture +must first be declared with DECLARE_F() which takes a fixture name +followed by a list of attributes and an end brace, and initialized +with INIT_F() which takes the fixture name and a setup and teardown +function to be defined subsequently. Note that there are +no start brace, which is intentional:: + + DECLARE_F(a_fixture) + int value; + struct my_details; + ... + }; + INIT_F(a_fixture, a_setup, a_teardown); + +Then to the implementation of the fixture, in the form of actual setup and +a teardown functions that may operate on the attributes of the fixture:: + + SETUP_F(a_fixture, a_setup) + { + a_fixture->value = 42; /* or whatever.. */ + <other actions needed to set up> + /* If everything went well during setup: */ + a_fixture->ok = true; + } + + TEARDOWN_F(a_fixture, a_teardown) + { + <necessary cleanup code> + } + +Now individual tests that uses this fixture can be declared with:: + + TEST_F(a_fixture, suite_name, test_name) + { + <test code> + } + +Contexts +******** + +A context provides a way to instantiate a test in multiple ways. +A typical use case is if you have multiple similar devices +you want to run a set of tests on. Another use case could be that +you want to run a set of tests under different configurations. + +You are free to let the number and names of these contexts +vary as to where you run your test. For the devices use case, you can +have the init function loop through all available devices, to identify +the ones the tests applies to, then instantiate a context for each +device, possibly using the device name for trackability. The context +names will be prepended to the test name and the number of available +tests will be multipled by the number of contexts. + +Note that the state of a context persists through the whole "life" of +the module (until it gets unloaded) so it can be used to store more +long term bookeeping in addition to any configuration information. +The test writer must make sure that subsequent runs of the test suite +(or parallel runs!) does not interfere with +each other. Similar to fixtures, there's a generic part that KTF uses, +and it can be extended the normal way. Make sure to declare the +test specific context struct type with an element named:: + + struct ktf_context k; + +typically as the first element of the struct, then you can continue +with whatever other datastructure desired. A test module can declare +and use as many contexts as desired. Note that contexts are associated +with and unique within a ``handle`` (see below). So if you need +to use a different set of contexts for different tests, you need to +put these contexts and tests into different handles. + +A context can be added using something like:: + + KTF_CONTEXT_ADD(&my_struct.k, "mycfg") + +where the first argument is a reference to the ktf_context structure +within the test specific structure, and the second argument is a text +name to use to refer to the context. Once one or more contexts exists +for a handle, tests for that handle will show up with names postfixed +by the context name, and there will be a distinct version of the test +for each context, e.g if a handle has contexts named ``c1`` and +``c2``, and tests declared with ``TEST(x, t1)`` and ``TEST(x, t2)``, +then this will manifest as 4 tests:: + + x.t1_c1 + x.t1_c2 + x.t2_c1 + x.t2_c2 + +Tests that depends on having a context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +It is likely that once you have a set of tests that uses contexts, +that they also depends on a context being available, e.g. that the +``ctx`` variable inside a test points to a non-nil context. One example +use case for contexts would be a certain class of device. Such a +device might or might not be available in the system. If one or more +devices are available, you might want to have tests named +``c.t1_dev1`` and ``c.t1_dev2`` where ``dev1`` and ``dev2`` are the +device names for these devices in the system, but never have any +``c.t1`` test without a device. You can +enforce this by letting tests associated with a handle requiring a +context to even show up as a test in the list or be available for +execution. Instead of using ``KTF_INIT()`` or ``KTF_HANDLE_INIT()``, +use ``KTF_CTX_INIT()`` or ``KTF_HANDLE_CTX_INIT()``. + +Configurable contexts +~~~~~~~~~~~~~~~~~~~~~ +Sometimes it might be useful to be able to configure a context for the +execution of some (or all) of the tests using the context. +This can be because the system the tests are running on might have +different hardware or software capabilities, or might rely on +differing device or network setup or naming. Typically we want a unit +test suite to have as little configuration and parameterization as +possible, so recommended use is for parameters that is not directly +related to the operation of the test, but more for situations where +parameters outside the test itself needs to be set up, such as connect +details for a network service to test against, or a peer unit test +process for network related tests that require more than one +system to run. To specify a configurable context, use:: + + int my_cfg_callback(struct ktf_context *ctx, const void* data, size_t data_sz); + + KTF_CONTEXT_ADD_CFG(&my_struct.k, "mycfg", my_cfg_callback, type_id) + +The ``data`` pointer (and it's length) should be provided from user +space, and it is up to the test specific user space and kernel space +code to decide with the configuration is all about. If 0 is returned, +KTF considers the context to be configured, otherwise it will retain +it's current state, which will initially be unconfigured. +The callback return value is stored as an errno value in ``ktf_context`` in the +variable ``config_errno``, which will initially be set to ``ENOENT``, +to indicate unconfigured. The test can use this value +to decide what to do, such as failing with a message about missing +configuration or just silently pass and ignore the case if not +configured. The ``type_id`` parameter is used as a unique +identifier for the kernel side to decide how to interpret the +parameter, which is useful if different contexts wants to implement +very different configuration options. It also allows two different +test modules to use the same context names but with different +parameters by using different context types. + +In the user space part of the test, configuration information +can be set for a context using:: + + KTF_CONTEXT_CFG(name, type_id, parameter_type, parameter_ref) + +A simple example of a configurable test can be seen in +the selftests test in ``selftest/context.c`` (kernel part) and +``user/context.cpp`` (user part) and the header file +``selftest/context_self.h`` shared between user space and kernel space. + +Context types and user space created contexts +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Contexts belong to a ``context type``, which is a mechanism to group +contexts into types that have similar properties. It is up to the +kernel test module using these contexts what the meaning of this is, +but a simple semantics can be that all contexts of a certain type has +the same parameter block and the type ID can be used to check what +type of context it is before trying to resolve or verify the +parameters. For contexts pre-created by the kernel module, type IDs +can be freely selected and does not have any further meaning beyond +this. + +Context types can however also be used to selectively allow user +space to dynamically create new contexts of a specific type. To enable +such functionality, the kernel test module will need to enable it for +one or more context types. This is done by means of the following call:: + + ktf_handle_add_ctx_type(struct ktf_handle *handle, + struct ktf_context_type *type) + +kernel side call, which tells KTF that a new context type with a given type ID +permits user applications to create new contexts. This is useful for +instance if user parameters or other information most easily +obtainable from user land at test runtime is most easily available +from user space. + +Handles +******* + +Unlike user land unit test frameworks, which can rely on everything +being cleaned up when the test program finished, KTF and test writers +must pay the normal kernel level attention to allocations, references and +deallocations. + +KTF itself uses the concept of a *handle* to track tests, +test suites and contexts associated with a kernel module. +Contexts are also associated with a handle. Since the availability of +contexts for a handle determines the availability of tests and the +naming of them, it can be useful to have separate spaces for tests +that relies on some context and tests that do not, to avoid +aggregating up multiple test cases that are identical. +Handles thus also have a namespace effect in that it is possible to +have two contexts with the same name, and possibly a different type, +by putting them in different handles. + +The simplest mode of usage is for each module to use KTF_INIT() and +KTF_CLEANUP() in it's __init and __exit functions. KTF_INIT implicitly declares and +initializes a global handle __test_handle that gets cleaned up again +in KTF_CLEANUP, making the handle something a test developer does not +need to think too much about. However, sometimes a KTF kernel module +may be such organized that it makes sense to use more than one handle. +KTF allows the creation/cleanup of explicitly named handles by means of +KTF_HANDLE_INIT(name) and KTF_HANDLE_CLEANUP(name). This can be used +as an alternative to KTF_INIT()/KTF_CLEANUP() but requires the use of +ADD_TEST_TO(handle, testname) instead of the normal ADD_TEST(testname) +for adding tests to be executed. diff --git a/Documentation/dev-tools/ktf/debugging.rst b/Documentation/dev-tools/ktf/debugging.rst new file mode 100644 index 0000000..4f1d0ff --- /dev/null +++ b/Documentation/dev-tools/ktf/debugging.rst @@ -0,0 +1,248 @@ +8. Debugging KTF +-------------------- + +Structured debugging/tracing by printf +====================================== + +The kernel side of KTF implements it's own simple printk based logging +abstraction. You can create log entries by means of calls to the macro +tlog(level, format,...) - level is a bitmask where individual bits +represents log classes. You can read and set this bitmask via a +writable ``/sys`` file:: + + # cat /sys/module/ktf/parameters/debug_mask + 0x1 + echo 0x3 > /sys/module/ktf/parameters/debug_mask + # cat /sys/module/ktf/parameters/debug_mask + 0x3 + +The default value of ``debug_mask`` is 0x1 = INFO level. +Bits are by convention supposed to be +ordered by verbosity, with the lowest bits reserved for low volume, +important messages, while the higher bist are left for more verbose +debugging. You can also use this mechanism for simple debugging of KTF's +interaction with your tests, as the core KTF code contains some log +statements to make it easier to follow and debug involved +instances of KTF objects. + +Similarly, the user library implementing the interaction with the +user land test runner can log details about this. You can enable such +logging by providing a similar bitmask via the environment variable +KTF_DEBUG_MASK. + +Debugging fatal errors in tests +=============================== + +So your KTF test crashed the kernel? Let's see how you can use crash to +examine KTF test cases, individual test logs and see which test is running. + +First step is we need to load symbols for KTF. To get text, data and +bss section locations for the ktf module (assuming it's currently +loaded):: + + # cd /sys/module/ktf/sections + # cat .text .data .bss + 0xffffffffa0bdb000 + 0xffffffffa0bdf000 + 0xffffffffa0bdf7a0 + +Now run crash on your corefile (or /proc/kcore for a live kernel):: + + # crash <path-to-Symbol.map> <path-to-vmlinux> <path-to-core> + crash> add-symbol-file /path/to/kernel/ktf.ko 0xffffffffa0bdb000 -s .data 0xffffffffa0bdf000 -s .bss 0xffffffffa0bdf7a0 + +Now we can see the global test_cases rbtree via the handy +"tree" command. It displays an rbtree, and because test_cases +is an rbtree under the hood we can display the set of test +cases as follows:: + + crash> tree -t rbtree -s ktf_case test_cases + ffff88036f710c00 + struct ktf_case { + kmap = { + node = { + __rb_parent_color = 1, + rb_right = 0x0, + rb_left = 0x0 + }, + key = "selftest\000cov\000probereturn\000probeentry\000wrongversion\000dummy\000simplemap", + map = 0xffffffffa0bdd1a0 <test_cases>, + refcount = { + refcount = { + counter = 2 + } + } + }, + tests = { + root = { + rb_node = 0xffff880250ac4a00 + }, + size = 5, + lock = { + { + rlock = { + raw_lock = { + { + head_tail = 655370, + tickets = { + head = 10, + tail = 10 + } + } + } + } + } + }, + elem_comparefn = 0x0, + elem_freefn = 0xffffffffa0bd8760 <ktf_test_free> + }, + debugfs = { + debugfs_results_testset = 0xffff88021a18a3c0, + debugfs_results_test = 0xffff88021a18aa80, + debugfs_run_testset = 0xffff88021a18a300, + debugfs_run_test = 0xffff88021a18a840 + } + } + +Here we had 1 test case - from the "key" field +we can see it is called "selftest" - in fact it is +KTF's self tests. Within that one test cases we see +the rbtree for the indivdual selftest tests has a root +rb_node:: + + tests = { + root = { + rb_node = 0xffff880250ac4a00 + }, + +By printing _that_ tree of ktf_test structures from +root node (-N) 0xffff880250ac4a00 we can see our +individual tests:: + + crash> tree -t rbtree -s ktf_test -N 0xffff880250ac4a00 + ffff880250ac4a00 + struct ktf_test { + kmap = { + node = { + __rb_parent_color = 1, + rb_right = 0xffff880250ac5b00, + rb_left = 0xffff880250ac5d00 + }, + key = "probeentry\000wrongversion\000dummy\000simplemap\000\000\000\000\000\020\276\240\377\377\377\377 \020\276\240\377\377\377\377@\020\276\240\377", + map = 0xffff88036f710c68, + refcount = { + refcount = { + counter = 2 + } + } + }, + tclass = 0xffffffffa0be41a4 "selftest", + name = 0xffffffffa0be41bd "probeentry", + fun = 0xffffffffa0be1920, + start = 0, + end = 1, + skb = 0xffff88003fc03800, + log = 0xffff88003fa58000 "", + lastrun = { + tv_sec = 1506072537, + tv_nsec = 289494591 + }, + debugfs = { + debugfs_results_testset = 0x0, + debugfs_results_test = 0xffff88021a18ac00, + debugfs_run_testset = 0x0, + debugfs_run_test = 0xffff88021a18af00 + }, + handle = 0xffffffffa0be5480 + } + ffff880250ac5d00 + struct ktf_test { + kmap = { + node = { + __rb_parent_color = 18446612142257621505, + rb_right = 0x0, + rb_left = 0xffff880250ac4b00 + }, + key = "dummy\000simplemap\000\000\000\000\000\020\276\240\377\377\377\377 \020\276\240\377\377\377\377@\020\276\240\377\377\377\377`\020\276\240\377\377\377\377\200\020\276\240\377\377\377\377\320\020\276\240\377", + map = 0xffff88036f710c68, + refcount = { + refcount = { + counter = 2 + } + } + }, + tclass = 0xffffffffa0be41a4 "selftest", + name = 0xffffffffa0be41d5 "dummy", + fun = 0xffffffffa0be10f0, + start = 0, + end = 1, + skb = 0xffff88003fc03800, + log = 0xffff88003fa59800 "", + lastrun = { + tv_sec = 1506072537, + tv_nsec = 289477354 + }, + debugfs = { + debugfs_results_testset = 0x0, + debugfs_results_test = 0xffff88021a18a900, + debugfs_run_testset = 0x0, + debugfs_run_test = 0xffff88021a18a9c0 + }, + handle = 0xffffffffa0be5480 + } + ... + crash> + + +The "log" fields are empty as each test passed, but we can +see from the "lastrun" times when the tests were run. +Logs will contain assertion failures etc in case of failure. + +Note that each test has a "handle" field also - this is +the KTF handle which was used to register the test. Each +handle also shows the currently-executing (if in the middle +of a test run) test associated with it, so if we want to +see where test execution was we can simply print the handle:: + + crash> print *(struct ktf_handle *)0xffffffffa0be5480 + $13 = { + test_list = { + next = 0xffffffffa0be5480, + prev = 0xffffffffa0be5480 + }, + handle_list = { + next = 0xffffffffa0be5490, + prev = 0xffffffffa0be5490 + }, + ctx_map = { + root = { + rb_node = 0x0 + }, + size = 0, + lock = { + { + rlock = { + raw_lock = { + { + head_tail = 0, + tickets = { + head = 0, + tail = 0 + } + } + } + } + } + }, + elem_comparefn = 0x0, + elem_freefn = 0x0 + }, + id = 0, + version = 4294967296, + current_test = 0x0 + } + crash> + +In this case current_test is NULL, but if we crashed in the +middle of executing a test it would show us which struct ktf_test * +it was. diff --git a/Documentation/dev-tools/ktf/examples.rst b/Documentation/dev-tools/ktf/examples.rst new file mode 100644 index 0000000..ec1f6e9 --- /dev/null +++ b/Documentation/dev-tools/ktf/examples.rst @@ -0,0 +1,26 @@ +6. Example test code +-------------------- + +Here is a minimal dummy example of a KTF unit test suite that defines +two tests, ``hello_ok`` and ``hello_fail``. The test is in the examples +directory and is built with KTF: + +.. literalinclude:: ../examples/hello.c + :language: c + +To run the test, cd to your KTF build tree and insmod the ktf module and +the module that provides the test:: + + insmod kernel/ktf.ko + insmod examples/hello.ko + +Now you should be able to run one or more of the tests by running the +application ``ktfrun`` built in ``user/ktfrun``. You should be able to run +that application as an ordinary user:: + + ktfrun --gtest_list_tests + ktfrun --gtest_filter='*fail' + ktfrun --gtest_filter='*ok' + +There are more examples in the examples directory. KTF also includes a +``selftest`` directory used to test/check the KTF implementation itself. diff --git a/Documentation/dev-tools/ktf/features.rst b/Documentation/dev-tools/ktf/features.rst new file mode 100644 index 0000000..2dc736c --- /dev/null +++ b/Documentation/dev-tools/ktf/features.rst @@ -0,0 +1,307 @@ +3. KTF kernel specific features +------------------------------- + +Reference to module internal symbols +************************************ + +When working with unit tests, the need to access non-public interfaces +often arises. In general non-public interfaces is of course not intended to +be used by outside programs, but a test framework is somewhat special here +in that it is often necessary or desirable to unit test internal +data structures or algorithms even if they are not exposed. The program +under test may be a complicated beast by itself and merely exercising the +public interfaces may not be flexible enough to stress the internal code. +Even if it is possible to get the necessary "pressure" from the outside +like that, it might be much more challenging or require a lot more work. + +The usual method to gain access to internal interfaces is to be part of the +internals. To some extent this is the way a lot of the kernel testing +utilities operate. The obvious advantages of this is that the test code +'automatically' follows the module and it's changes. The disadvantage is +that test code is tightly integrated with the code itself. One important +goal with KTF is to make it possible to write detailed and sophisticated +test code which does not affect the readability or complexity of the tested +code. + +KTF contains a small python program, ``resolve``, which +parses a list of symbol names on the form:: + + #module first_module + #header first_header.h + private_symbol1 + private_symbol2 + ... + #header second_header.h + #module next_module + ... + +The output is a header file and a struct containing function pointers and +some convenience macro definitions to make it possible to 'use' the +internal functions just as one would if within the module. This logic is +based on kallsyms, and would of course only work if that functionality is +enabled in the kernel KTF compiles against. Access to internal symbols +this way is controlled by the kernel config options CONFIG_KALLSYMS +and CONFIG_KALLSYMS_ALL, which must be set to "y". + +If you create a new test project using the ``ktfnew`` script, you can +put your private symbol definitions in a file ``ktf_syms.txt`` in the +kernel directory, and KTF will automatically generate ``ktf_syms.h``, +which you can then include in your test file to get to these symbols. +This functionality is also used by the KTF selftests, which might +serve as an example to get started. + +Note also that for exported symbols, if you build your module out-of-tree in +addition to KTF and the test modules, you might need to also add those +module's ``Module.symvers`` files to ``KBUILD_EXTRA_SYMBOLS`` +(See kernel documentation for this) to find them during test module build. + +Requesting callbacks when a certain function gets called/returns +**************************************************************** + +Tap into function entry using KTF entry probes. Many tests need to +move beyond kernel APIs and ensure that side effects (logging a +message etc) occur. A good way to do this is to probe entry of relevant +functions. In order to do so in KTF you need to: + + - define an entry probe function with the same return value and arguments + as the function to be probed + + - within the body of the entry probe function, ensure return is wrapped with + KTF_ENTRY_PROBE_RETURN(<return value>); + + - entry probes need to registered for use and de-registered when done via + KTF_[UN]REGISTER_ENTRY_PROBE(<kernel function name>, <handler function>). + +See example h4.c in examples/ for a simple case where we probe printk() and +ensure it is called. + +Sometimes is is also useful to check that an intermediate function is returning +an expected value. Return probes can be used to register/probe function +return. In order to probe function return: + + - define a return probe point; i.e + KTF_RETURN_PROBE(<kernel function>, <handler>) + + - within the body of the return probe the return value can be retrieved + via KTF_RETURN_VALUE(). Type will obviously depend on the function + probed so should be cast if dereferencing is required. + + - return probes need to be registered for use and unregistered when done + via KTF_[UN]REGISTER_RETURN_PROBE(<kernel function name>, <handler>). + +See example h4.c in examples/ for a simple case where we verify return value +of printk(). + +Note that this functionality is only available on kernels with CONFIG_KPPROBES +and CONFIG_KRETPROBES set to "y". + +Overriding functions +******************** +in some cases, we wish to override harmful functions when inducing failues in +tests (e.g. skb_panic()). Override is done via kprobes and we define as follows:: + + KTF_OVERRIDE(oldfunc, newfunc) + { + ... + KTF_SET_RETURN_VALUE(1); + KTF_OVERRIDE_RETURN; + } + + TEST(...) + { + KTF_REGISTER_OVERRIDE(oldfunc, newfunc); + ... + KTF_UNREGISTER_OVERRIDE(oldfunc, newfunc); + } + +Override should be used sparingly; we'd rather test the code as-is and use +entry/return probes where possible. + +Note that this functionality is only available on kernels with CONFIG_KPPROBES +and CONFIG_KRETPROBES set to "y". + +Coverage analytics +****************** + +While other coverage tools exist, they generally involve gcc-level support +which is required at compile-time. KTF offers kernel module coverage +support via kprobes instead. Tests can enable/disable coverage on a +per-module basis, and coverage data can be retrieved via:: + + # more /sys/kernel/debug/ktf/coverage + +For a given module we show how many of its functions were called versus the +total, e.g.:: + + # cat /sys/kernel/debug/ktf/coverage + MODULE #FUNCTIONS #CALLED + selftest 14 1 + +We see 1 out of 14 functions was called when coverage was enabled. + +We can also see how many times each function was called:: + + MODULE FUNCTION COUNT + selftest myelem_free 0 + selftest myelem_cmp 0 + selftest ktf_return_printk 0 + selftest cov_counted 1 + selftest dummy 0 + +In addition, we can track memory allocated via kmem_cache_alloc()/kmalloc() +originating from module functions we have enabled coverage for. This +allows us to track memory associated with the module specifically to find +leaks etc. If memory tracking is enabled, /sys/kernel/debug/ktf/coverage +will show outstanding allocations - the stack at allocation time; the +memory address and size. + +Coverage can be enabled via the "ktfcov" utility. Syntax is as follows:: + + ktfcov [-d module] [-e module [-m]] + +"-e" enables coverage for the specified module; "-d" disables coverage. +"-m" in combination with "-e" enables memory tracking for the module under +test. + +Note that this functionality is only available on kernels with CONFIG_KPPROBES +and CONFIG_KRETPROBES set to "y", and that CONFIG_KALLSYMS and +CONFIG_KALLSYMS_ALL should be set to "y" also to get all exported and +non-exported symbols. + +Thread execution +**************** + +KTF provides easy mechanisms to create and use kernel threads. +Assertions can then be carried out in the created thread context +also. Threads can be created as follows, and we can if we wish +wait for thread completion:: + + + TEST(foo, bar) + { + struct ktf_thread t; + + ... + KTF_THREAD_INIT(mythread, &t); + KTF_THREAD_RUN(&t); + KTF_THREAD_WAIT_COMPLETED(&t); + ... + } + +The thread itself is defined as follows:: + + KTF_THREAD(mythread) + { + ... + } + +We can add assertions to the thread and they will be recorded/logged +as part of the test. + +Hybrid tests +************ + +KTF also allows mixing of user and kernel side code in the same test. +This is useful if one wants for instance to verify that user land operations +has certain effects in the kernel, for instance verify that a parameter is +transferred or handled correctly in the kernel. + +Hybrid tests are specified by writing a user mode test using the special +``HTEST()`` macro instead of the normal ``TEST()`` macro. This macro takes +Inside the macro, the special variable ``self`` can be used to refer to the +test itself, and the macro ``KTF_USERDATA()`` can be used to get a pointer to +an allocated instance of a test specific parameter struct. The user land test +can then call the kernel side directly using ``ktf::run_kernel_test(self)`` An +optional context name can be specified as a second argument to the call if +needed. This can be done any number of times during the user land test and +each call will transmit the struct value out-of-band to the kernel side. To +the kernel this appears as separate test calls, but the kernel side have the +option of aggregating or otherwise maintain state for the duration of the +test. + +Declare the data structure to use for user/kernel out-of-band communication +in a header file that is included both by the user and the kernel side:: + + struct my_params + { + char expected[128]; + unsigned long mode; + }; + +The user land side of the test itself can then look like this:: + + HTEST(foo, hybrid) + { + KTF_USERDATA(self, my_params, data); + + <normal gtest code> + + strcpy(data->expected, "something"); + data->mode = 0; + ktf::run_kernel_test(self); + + strcpy(data->expected, "something_else"); + ktf::run_kernel_test(self); + + <normal gtest code> + + ... + } + +On the kernel side, a hybrid test is written as a normal kernel test using +the ``TEST()`` macro, and the test must be added using ``ADD_TEST()`` as +usual. Include the user land header file to know the data type of the +out-of-band parameter block. Invoke the macro ``KTF_USERDATA()`` to get a +size validated pointer to the user land provided data. If no data is +available, the test will silently exit. This is by purpose - if the kernel +test is executed from a test program that does not have the associated user +land code, such as for instance ``ktfrun``, it will just appear as a test +with no assertions in it, and not create any errors. If on the other hand the +parameter block does not match in size, an assertion is thrown and the test +exits:: + + TEST(foo, hybrid) + { + KTF_USERDATA(self, my_params, data); + + ... + if (strcmp(data->expected, "something") == 0) + ... + EXPECT( ... ) + + ... + } + + +Running tests and examining results via debugfs +*********************************************** + +In addition to the netlink interface used by the Googletest integrated frontend code, +we provide debugfs interfaces for examining the results of the +last test run and for running tests which do not require configuration +specification. Individual ktf testsets can be run via:: + + cat /sys/kernel/debug/ktf/run/<testset> + +Individual tests can be run via:: + + cat /sys/kernel/debug/ktf/run/<testset>-tests/<test> + +Results can be displayed for the last run via:: + + cat /sys/kernel/debug/ktf/results/<testset> + +Individual tests can be run via:: + + cat /sys/kernel/debug/ktf/results/<testset>-tests/<test> + +These interfaces bypasses use of the netlink socket API +and provide a simple way to keep track of test failures. It can +be useful to log into a machine and examine what tests were run +without having console history available. + +In particular:: + + cat /sys/kernel/debug/ktf/run/* + +...is a useful way of running all KTF tests. diff --git a/Documentation/dev-tools/ktf/implementation.rst b/Documentation/dev-tools/ktf/implementation.rst new file mode 100644 index 0000000..2bc4335 --- /dev/null +++ b/Documentation/dev-tools/ktf/implementation.rst @@ -0,0 +1,70 @@ + +2. Implementation +----------------- + +KTF consists of a kernel part and a user part. The role of the user part is to query the kernel +for available tests, and provide mechanisms for executing a selected set or all the available +tests, and report the results. The core ktf kernel module simply provides some APIs to write +assertions and run tests and to communicate about tests and results with user mode. +A simple generic Netlink protocol is used for the communication. + +User mode implementation +************************ + +Since test filtering and reporting is something existing unit test frameworks for +user space code already does well, the implementation of KTF simply leverages that. +The current version supports an integration with gtest (Googletest), which provides a lot of +these features in a flexible way, but in principle alternative implementations could +use the reporting of any other user level unit test framework. The use of gtest also allows this +documentation to be shorter, as many of the features in gtest are automatically available for KTF as well. +More information about Googletest features can be found here: https://github.com/google/googletest + +Kernel mode implementation +************************** + +The kernel side of KTF implements a simple API for tracking test modules, +writing tests, support functions and and a set of assertion macros, some +tailored for typical kernel usage, such as ``ASSERT_OK_ADDR_GOTO()`` +as a kernel specific macro to check for a valid address with a label to jump to if the +assertion fails. After all as we are still in the kernel, tests would always need to clean up for +themselves even though in the context of ktf. + +KTF supports two distinct classes of tests: + +* Pure kernel mode tests +* Hybrid tests + +Pure kernel mode tests are tests that are fully implemented in kernel space. +This is the most straightforward mode and resembles ordinary user land unit testing +in kernel mode. If you only have kernel mode tests, you will only ever need one user level program +similar to user/ktfrun.cpp, since all test development takes place on the kernel side. + +Hybrid tests are for testing and making assumptions about the user/kernel communication, for instance +if a parameter supplied from user mode is interpreted the intended way when it arrives at it's kernel +destination. For such tests you need to tell ktf (from user space) when the kernel part of the test +is going to be executed - this can happen multiple times depending on your test needs. +Apart from that it works mostly like a normal gtest user level test. + +Kernel integration of KTF or KTF as a separate git project? +*********************************************************** + +Yes. A lot of test infrastructure and utilities for the Linux kernel +is implemented as part of the linux kernel git project. +This has some obvious benefits, such as + +* Always being included +* When APIs are changed, test code can be updated atomically with the rest of the kernel +* Higher visibility and easier access +* Easier integration with internal kernel interfaces useful for testing. + +On the other hand providing KTF as a separate project allows + +* With some use of ``KERNEL_VERSION`` and ``LINUX_VERSION_CODE``, up-to-date KTF code and tests + can be allowed to work across kernel versions. +* This in turn allows a single set of newly developed tests to be + simultaneously tested against multiple older kernels, possibly + detecting more bugs, or instances of bugs not backported. + +So we will continue to support both, and have work in progress to simplify +the maintenance and synchronization of the two versions, and allow the +additional tooling to extend to KTF client test suites as well. diff --git a/Documentation/dev-tools/ktf/index.rst b/Documentation/dev-tools/ktf/index.rst new file mode 100644 index 0000000..25db49b --- /dev/null +++ b/Documentation/dev-tools/ktf/index.rst @@ -0,0 +1,14 @@ +Kernel Test Framework documentation +=================================== + +.. toctree:: + :maxdepth: 2 + + introduction + implementation + features + installation + concepts + examples + progref + debugging diff --git a/Documentation/dev-tools/ktf/installation.rst b/Documentation/dev-tools/ktf/installation.rst new file mode 100644 index 0000000..1bfccc1 --- /dev/null +++ b/Documentation/dev-tools/ktf/installation.rst @@ -0,0 +1,73 @@ +4. Building and installing KTF +------------------------------ + +KTF's user land side depends on googletest. +The googletest project has seen some structural changes in moving from a +project specific gtest-config via no package management support at all to +recently introduce pkgconfig support. This version of KTF only supports +building against a googletest (gtest) with pkgconfig support, which means +that as of February 2018 you have to build googletest from source at +github. + +Googletest has also recently been fairly in flux, and while we +try to keep up to date with the official googletest version on Github, +we have seen issues with changes that breaks KTF. We also have a small +queue of enhancements and fixes to Googletest based on our experience +and use of it a.o. with KTF. You can find the latest rebase of this +version in the ktf branch of knuto/googletest at Github, but expect it +to rebase as we move forward to keep it up-to-date. +This version will at any time have been tested with KTF by us, since +we use it internally. Let's assume for the rest of these instructions +that your source trees are below ``~/src`` and your build trees are +under ``~/build``:: + + cd ~/src + git clone https://github.com/knuto/googletest.git + +or:: + + cd ~/src + git clone https://github.com/google/googletest.git + +then:: + + mkdir -p ~/build/$(uname -r) + cd ~/build/$(uname -r) + mkdir googletest + cd googletest + cmake ~/src/googletest + make + sudo make install + +Default for googletest is to use static libraries. If you want to use shared +libraries for googletest, you can specify ``-DBUILD_SHARED_LIBS=ON`` to +cmake. If you don't want to install googletest into /usr/local, you can +specify an alternate install path using ``-DCMAKE_INSTALL_PREFIX=<your path>`` +to cmake for googletest, and similarly use ``--prefix=<your path>`` both for +KTF and your own test modules. Note that on some distros, cmake version +2 and 3 comes as different packages, make sure you get version 3, which may +require you to use ``cmake3`` as command instead of cmake above. + +Building the in-kernel version of KTF and running KTF selftests +*************************************************************** + +The environment needs to have the path to the +gtest (Googletest) build set to the directory above the lib and +include directories:: + + export GTEST_PATH=$HOME/install + +KTF can then be built using the module target, eg. from the top level +kernel build tree. + + make M=tools/testing/selftests/ktf + +You can run also build (and run) KTF tests as selftests tests +via the kselftest target:: + + make TARGETS="ktf" kselftest + +You can invoke this command to let the tests run as a normal user, but +root access is needed to load and unload ktf.ko and the test +module(s). This will happen as part of the kselftest target even as a +normal user if the user has sudo privileges. diff --git a/Documentation/dev-tools/ktf/introduction.rst b/Documentation/dev-tools/ktf/introduction.rst new file mode 100644 index 0000000..5c861f4 --- /dev/null +++ b/Documentation/dev-tools/ktf/introduction.rst @@ -0,0 +1,134 @@ +:Author: Knut Omang knut.omang@oracle.com +:Last Updated: Alan Maguire alan.maguire@oracle.com + +1. Background and motivation +---------------------------- + +Kernel Test Framework (KTF) implements a unit test framework for the Linux kernel. +There's a wide selection of unit test frameworks available for normal user land +code testing, but we have so far not seen any similar frameworks that can be used +with kernel code, to test details of both exported and non-exported kernel APIs. +The hope is that providing an easy to use and convenient way to write simple unit +tests for kernel internals, that this can promote a more test driven approach to +kernel development, where appropriate. + +An important design goal is to make KTF in a way that it lend itself well to a normal kernel +developer cycle, and that it integrates well with user land unit testing, to allow kernel and +user land tests to behave, look and feel as similar as possible. This should hopefully make it +more intuitive to use as well as more rewarding. We also believe that even a kernel test that +passes should have a nice, easy to read and pleasant output, and that a test framework must have +good observability, that is good mechanisms for debugging what went wrong, both in case of bugs +in the tests and and the test framework itself. + +KTF is designed to test the kernel in the same ways it runs. This means we want to stay away from +changing configuration options, or otherwise make changes that makes it hard to logically tell +from a high level perspective whether the kernel with KTF is really logically "the same" as the +kernel our users are exposed to. Of course we all know that it is very hard to test anything +without affecting it, with quantum mechanics as the extreme, but at least we want to make an +effort to make the footprint as small as possible. + +KTF tests kernel code by running tests in kernel context - or in the case of hybrid tests - in +both user- and kernel contexts. Doing this ensures that we test kernel codepaths in a real way, +without emulating a kernel execution environment. This gives us vastly more control over what +tests can do versus user-space driven testing, and increases confidence that what the tests test +matches what the kernel does since the test execution environment is identical. + +KTF is a product of a refactoring of code used as part of test driven development of a Linux +driver for an Infiniband HCA. It is in active use for kernel component testing within Oracle. + +Test driven development +*********************** + +Unit testing is an important component of Test driven development (TDD). +The idea of test driven development is that when you have some code to write, +whether it is a bug to fix, or a new feature or enhancement, that you start by writing +one or more tests for it, have those tests fail, and then do the actual development. + +Typically a test driven development cycle would have several rounds of development and +test using the new unit tests, and once the (new) tests pass, you would +also run all or a suitable subset (limited by execution time) of the old tests to verify +that you have not broken any old functionality by the new code. + +At this stage it is important that the tests that are run can be run quickly to allow +them to be actively used in the development cycle. When time comes for +submission of the code, a full, extensive set of both the tests the developer thinks +can touch the affected code *and* all the other tests should be run, and a longer time +to run tests can be afforded. + +KTF tries to support this by using the module system of the kernel to support +modularized test suites, where a user only need to insmod the test subsets that he/she wants +to use right then. Different test suites may touch and require different kernel APIs and have +lots of different module and device requirements. To enable as much reuse of the functionality +of code developed within KTF it is important that any piece of test code has as few dependencies +as possible. + +Good use cases for KTF +********************** + +Unit testing is at it's most valuable when the code to test is relatively error prone, but still +might be difficult to test in a systematic and reproducable way from a normal application level. +It can be difficult to trigger corner cases from a high abstraction layer, +the code paths we want to exercise might only be used occasionally, or we want to exercise +that error/exception scenarios are handled gracefully. + +KTF comes to it's strength in testing kernel APIs that are fairly integrated into the kernel, +and depend upon lots of components, making them difficult or error prone to mock. Good examples +are module APIs not easily testable from user land. Exported module APIs are usually only used +by one or a few other kernel clients, and hitting buggy corner cases with these might be hard or +impossible. This typically leads to bugs detected "down the road", when some new client appears +and starts using the API in a new way, or instabilities that go undetected because underlying +semantics that the implementation implicitly depend upon changes in subtle ways. + +KTF can use mechanisms such as KTF probes in cases where calls to other functions needs to be +intercepted and/or modified to create the right test condition, whether it means waiting for a +potential race condition to occur, or return an error value, or just collect state to make assertions. + +Typical classical use cases that lend itself well to unit testing are simple APIs with a relativ +complex implementation - such as container implementations. Typical kernel examples of these +in the kernel are scatterlist, rbtree, list, XArray etc. When testing the base implementations of such +containers, bringing them entirely out into user space and compiling them standalone require some +additional work up-front to implement mock interfaces to the services provided by the kernel, +but may nonetheless be rewarding in the longer run, as such tests have at it's disposal the whole +arsenal of user land tools, such as gdb, valgrind etc. This, however does not guarantee against +wrong use of a container, such as with interactions between a container and a driver +datastructure. + +Testing the *instantiations* of these container implementations inside drivers or +the kernels's own internals might not be that easy with a user land approach, as it very quickly +requires a prohibitive amount of mock interfaces to be written. And even when such mock +interfaces can be written, one cannot be sure that they implement exactly the same as the +environment that the code executes in within the kernel. Having the ability to make tests within +a release kernel, even run the same tests against multiple such kernels is something KTF +supports well. Our experience is that even error scenarios that are hard to reproduce by +running applications on the kernel can often be reproduced with a surprisingly small +number of lines of code in a KTF test, once the problem is understood. And writing that code can +be a very rewarding way of narrowing down a hard bug. + +When *not* to use KTF +********************* + +Writing kernel code has some challenges compared to user land code. +KTF is intended for the cases where it is not easy to get coverage by writing +simple tests from user land, using an existing rich and well proven user land unit test +framework. + +Why *you* would want to write and run KTF tests +*********************************************** + +Besides the normal write test, write code, run test cycle of development and the obvious benefits of +delivering better quality code with fewer embarrassments, there's a few other upsides from +developing unit test for a particular area of the kernel: + +* A test becomes an invariant for how the code is supposed to work. + If someone breaks it, they should detect it and either document the changes that caused the breakage + by fixing the test or realize that their fix is broken before you even get to spend time on it. + +* Kernel documentation while quite good in some places, does not always + cover the full picture, or you might not find that sentence you needed while looking for it. + If you want to better understand how a particular kernel module actually works, a good way is to + write a test that codes your assumptions. If it passes, all is well, if not, then you have gained some + understanding of the kernel. + +* Sometimes you may find yourself relying on some specific feature or property of the kernel. + If you encode a test that guards the assumptions you have made, you will capture if someone + changes it, or if your code is ported to an older kernel which does not support it. diff --git a/Documentation/dev-tools/ktf/progref.rst b/Documentation/dev-tools/ktf/progref.rst new file mode 100644 index 0000000..2f0fa48 --- /dev/null +++ b/Documentation/dev-tools/ktf/progref.rst @@ -0,0 +1,144 @@ +7. KTF programming reference +---------------------------- + +KTF itself contains no tests but provides primitives and data structures to +allow tests to be maintained and written in separate test modules that +depend on the KTF APIs. + +KTF API Overview +**************** + +For reference, the following table lists a few terms and classes of +abstractions provided by KTF. These are kernel side, if not otherwise noted: + ++----------------------------+--------------------------------------------------+ +| **Item** | **description** | ++============================+==================================================+ +| Test module | A kernel object file (.ko) with ktf tests in it | ++----------------------------+--------------------------------------------------+ +| struct ktf_handle | At least 1 per test module. | +| | Use macros KTF_INIT() and KTF_CLEANUP() to set up| +| | and tear down handles. | ++----------------------------+--------------------------------------------------+ +| struct ktf_context | 0-n per test module - test module specific | +| | context for the test, such as eg. a device or | +| | another kernel object. | ++----------------------------+--------------------------------------------------+ +| KTF_INIT() | Call this at the global level in the main file | +| | for each test module. Declares an implicit, | +| | default test handle used by macros which do not | +| | provide a handle argument. | ++----------------------------+--------------------------------------------------+ +| KTF_CTX_INIT() | Use this instead of KTF_INIT if the tests require| +| | a context to execute. Tests will only show up as | +| | options if a context has been provided. | ++----------------------------+--------------------------------------------------+ +| KTF_HANDLE_INIT(handle) | Declare a named handle to associate tests and | +| | contexts with. This is an alternative to | +| | KTF_INIT() to allow the use of separate test | +| | handles for separate sets of tests. | ++----------------------------+--------------------------------------------------+ +| KTF_HANDLE_CTX_INIT(handle)| Equivalent of KTF_CTX_INIT for a named handle | ++----------------------------+--------------------------------------------------+ +| KTF_CLEANUP() | Call this in the __exit function to clean up | ++----------------------------+--------------------------------------------------+ +| KTF_CONTEXT_ADD(ctx, name) | Add a new context to the default handle | ++----------------------------+--------------------------------------------------+ +| KTF_CONTEXT_FIND(name) | Return a struct ktf_context reference to context | +| | 'name', if it exists | ++----------------------------+--------------------------------------------------+ +| KTF_CONTEXT_GET(name,type) | Return the structure of type 'type' containing | +| | the ktf_context named 'name', if 'name' exists. | ++----------------------------+--------------------------------------------------+ +| KTF_CONTEXT_REMOVE(ctx) | Remove a previously added context from KTF | ++----------------------------+--------------------------------------------------+ +| EXPECT_* | non-fatal assertions | ++----------------------------+--------------------------------------------------+ +| ASSERT_* | "fatal" assertions that would cause return/goto | ++----------------------------+--------------------------------------------------+ +| TEST(s, n) {...} | Define a simple test named 's.n' with implicit | +| | arguments 'ctx' and '_i' for context/iteration. | ++----------------------------+--------------------------------------------------+ +| DECLARE_F(f) {...} | Declare a new test fixture named 'f' with | +| | additional data structure | ++----------------------------+--------------------------------------------------+ +| SETUP_F(f, s) {...} | Define setup function for the fixture | ++----------------------------+--------------------------------------------------+ +| TEARDOWN_F(f, t) {...} | Define teardown function for the fixture | ++----------------------------+--------------------------------------------------+ +| INIT_F(f, s, t) {...} | Declare the setup and tear down functions for the| +| | fixture | ++----------------------------+--------------------------------------------------+ +| TEST_F(s, f, n) {...} | Define a test named 's.n' operating in fixture f | ++----------------------------+--------------------------------------------------+ +| ADD_TEST(n) | Add a test previously declared with TEST or | +| | TEST_F to the default handle. | ++----------------------------+--------------------------------------------------+ +| ADD_LOOP_TEST(n, from, to) | Add a test to be executed repeatedly with a range| +| | of values [from,to] to the implicit variable _i | ++----------------------------+--------------------------------------------------+ +| DEL_TEST(n) | Remove a test previously added with ADD_TEST | ++----------------------------+--------------------------------------------------+ +| KTF_ENTRY_PROBE(f, h) | Define function entry probe for function f with | +| {...} | handler function h. Must be used at global level.| ++----------------------------+--------------------------------------------------+ +| KTF_ENTRY_PROBE_RETURN(r) | Return from probed function with return value r. | +| | Must be called within KTF_ENTRY_PROBE(). | ++----------------------------+--------------------------------------------------+ +| KTF_REGISTER_ENTRY_PROBE | Enable probe on entry to kernel function f | +| (f, h) | with handler h. | ++----------------------------+--------------------------------------------------+ +| KTF_UNREGISTER_ENTRY_PROBE | Disable probe on entry to kernel function f | +| (f, h) | which used handler h. | ++----------------------------+--------------------------------------------------+ +| KTF_RETURN_PROBE(f, h) | Define function return probe for function f with | +| {..} | handler h. Must be used at a global level. | ++----------------------------+--------------------------------------------------+ +| KTF_RETURN_VALUE() | Retrieve return value in body of return probe. | ++----------------------------+--------------------------------------------------+ +| KTF_REGISTER_RETURN_PROBE | Enable probe for return of function f with | +| (f, h) | handler h. | ++----------------------------+--------------------------------------------------+ +| KTF_UNREGISTER_RETURN_PROBE| Disable probe for return of function f and | +| (f, h) | handler h. | ++----------------------------+--------------------------------------------------+ +| ktf_cov_enable(m, flags) | Enable coverage analytics for module m. | +| | Flag must be either 0 or KTF_COV_OPT_MEM. | ++----------------------------+--------------------------------------------------+ +| ktf_cov_disable(m) | Disable coverage analytics for module m. | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_INIT(name, t) | Initialize thread name, struct ktf_thread * t. | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_RUN(t) | Run initialized struct ktf_thread * t. | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_STOP(t) | Stop thread via kthread_stop() | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_WAIT_STARTED(t) | Wait for start of struct ktf_thread * t. | ++----------------------------+--------------------------------------------------+ +| KTF_THREAD_WAIT_COMPLETED | Wait for completion of struct ktf_thread * t. | +| (t) | | ++----------------------------+--------------------------------------------------+ +| HTEST(s, n) { ... } | Declares a hybrid test. A correspondingly named | +| (NB! User mode only!) | test must be declared using TEST() from kernel | +| | space for the hybrid test to be executed. | ++----------------------------+--------------------------------------------------+ +| KTF_USERDATA(self, type, d)| Declare/get a pointer to user/kernel aux.data | +| (NB! both kernel and | for a test that declares such extra data. Used | +| user space!) | for hybrid tests. | ++----------------------------+--------------------------------------------------+ + +The ``KTF_INIT()`` macro must be called at a global level as it just +defines a variable ``__test_handle`` which is referred to, and which existence +is assumed to continue until the call to KTF_CLEANUP(), typically done in +the ``__exit`` function of the test module. + + + +Assertions +********** + +Below is example documentation for some of the available assertion macros. +For a full overview, see ``kernel/ktf.h`` + +.. kernel-doc:: kernel/ktf.h + :internal:
context.c: Parameterized context test case, kernel side: context.h: Parameterized context test case, kernel side. context_self.h: The data structure passed between user level and kernel for the hybrid.c: Hybrid (combined user level and kernel) self tests, hybrid.h: Hybrid (combined user level and kernel) self tests, hybrid_self.h: The data structure passed between user level and kernel for the self.c: Some simple self tests for KTF
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/selftest/Makefile | 17 +- tools/testing/selftests/ktf/selftest/context.c | 149 +++- tools/testing/selftests/ktf/selftest/context.h | 15 +- tools/testing/selftests/ktf/selftest/context_self.h | 34 +- tools/testing/selftests/ktf/selftest/hybrid.c | 35 +- tools/testing/selftests/ktf/selftest/hybrid.h | 24 +- tools/testing/selftests/ktf/selftest/hybrid_self.h | 27 +- tools/testing/selftests/ktf/selftest/ktf_syms.txt | 17 +- tools/testing/selftests/ktf/selftest/self.c | 661 +++++++++++++- 9 files changed, 979 insertions(+) create mode 100644 tools/testing/selftests/ktf/selftest/Makefile create mode 100644 tools/testing/selftests/ktf/selftest/context.c create mode 100644 tools/testing/selftests/ktf/selftest/context.h create mode 100644 tools/testing/selftests/ktf/selftest/context_self.h create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.c create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.h create mode 100644 tools/testing/selftests/ktf/selftest/hybrid_self.h create mode 100644 tools/testing/selftests/ktf/selftest/ktf_syms.txt create mode 100644 tools/testing/selftests/ktf/selftest/self.c
diff --git a/tools/testing/selftests/ktf/selftest/Makefile b/tools/testing/selftests/ktf/selftest/Makefile new file mode 100644 index 0000000..8737bf4 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/Makefile @@ -0,0 +1,17 @@ +# Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 +# +# Kernel module implementing a test suite for testing KTF itself +# + +ccflags-y += -Wno-vla + +ccflags-y += -I$(srctree)/$(src)/../kernel -I$(src) + +obj-m := selftest.o + +include $(srctree)/$(src)/../scripts/ktf_syms.mk + +selftest-y := self.o hybrid.o context.o + diff --git a/tools/testing/selftests/ktf/selftest/context.c b/tools/testing/selftests/ktf/selftest/context.c new file mode 100644 index 0000000..9129b5b --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/context.c @@ -0,0 +1,149 @@ +/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * context.c: Parameterized context test case, kernel side: + */ + +#include "ktf.h" +#include "context.h" + +/* Declare a specific handle for this test to avoid interfering with the + * other tests: + */ +static KTF_HANDLE_INIT(ct_handle); + +struct param_test_ctx { + struct ktf_context k; + struct test_parameter_block p; +}; + +struct param_test_ctx param_ctx[2]; + +#define MYVALUE 0xdabadaba + +/* Declare the callback that accepts a parameter block */ +static int param_ctx_cb(struct ktf_context *ctx, const void *data, size_t data_sz) +{ + struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k); + struct test_parameter_block *pb = (struct test_parameter_block *)data; + long orig_myvalue; + + if (data_sz != sizeof(*pb)) + return -EINVAL; + /* check data validity here, if possible.. */ + orig_myvalue = px->p.myvalue; + memcpy(&px->p, pb, data_sz); + /* Enforce "policies" */ + px->p.myvalue = orig_myvalue; + return 0; +} + +TEST(selftest, param) +{ + struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k); + + /* Now, here we can fail (using ASSERT) or ignore by silently return + * depending on what's most useful, if a test hasn't been configured. + * For this selftest we just use EXPECT so we can have the actual current + * parameter values reported as well. + * + * Notice that these parameters are + * persistent throughout the instance 'life' of the kernel test module, + * so if one user program has configured them, then + * programs ignorant of the parameters may still end up + * executing the tests with previously configured parameters: + * + * This simplified example uses the same configuration struct for both + * context type IDs, but the idea is that they can be completely different. + */ + EXPECT_INT_EQ(ctx->config_errno, 0); + if (KTF_CONTEXT_CFG_OK(ctx)) { + switch (ctx->type->name[13]) { + case '1': + EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC1); + break; + case '2': + EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC2); + break; + case '3': + EXPECT_LONG_EQ(px->p.magic, CONTEXT_MAGIC3); + EXPECT_LONG_EQ(px->p.myvalue, MYVALUE); + break; + } + EXPECT_STREQ(px->p.s, CONTEXT_MSG); + } else { + EXPECT_LONG_EQ(px->p.magic, 0); + EXPECT_STREQ(px->p.s, ""); + } +} + +struct param_test_type { + struct ktf_context_type kt; + /* space for cfg data (such as constraints) for the context type */ + long myvalue; +}; + +static struct ktf_context *type3_alloc(struct ktf_context_type *ct) +{ + struct param_test_type *pct = container_of(ct, struct param_test_type, kt); + struct param_test_ctx *ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + + ctx->p.myvalue = pct->myvalue; + return &ctx->k; +} + +static void type3_cleanup(struct ktf_context *ctx) +{ + struct param_test_ctx *px = container_of(ctx, struct param_test_ctx, k); + + kfree(px); +} + +TEST(selftest, dupltype) +{ + /* Verify that we cannot add the same context type twice */ + + static struct param_test_type dupltype = { + .myvalue = 0, + .kt.alloc = type3_alloc, + .kt.config_cb = param_ctx_cb, + .kt.cleanup = type3_cleanup, + .kt.name = "context_type_3" + }; + + ASSERT_INT_EQ(-EEXIST, ktf_handle_add_ctx_type(&ct_handle, &dupltype.kt)); +} + +void add_context_tests(void) +{ + int ret = KTF_CONTEXT_ADD_TO_CFG(ct_handle, ¶m_ctx[0].k, "context1", + param_ctx_cb, "context_type_1"); + + if (ret) + return; + + ret = KTF_CONTEXT_ADD_TO_CFG(ct_handle, ¶m_ctx[1].k, "context2", + param_ctx_cb, "context_type_2"); + if (ret) + return; + + { + static struct param_test_type ctx_type3 = { + .myvalue = MYVALUE, + .kt.alloc = type3_alloc, + .kt.config_cb = param_ctx_cb, + .kt.cleanup = type3_cleanup, + .kt.name = "context_type_3" + }; + ret = ktf_handle_add_ctx_type(&ct_handle, &ctx_type3.kt); + } + + ADD_TEST_TO(ct_handle, param); + ADD_TEST(dupltype); +} + +void context_tests_cleanup(void) +{ + KTF_HANDLE_CLEANUP(ct_handle); +} diff --git a/tools/testing/selftests/ktf/selftest/context.h b/tools/testing/selftests/ktf/selftest/context.h new file mode 100644 index 0000000..69b970a --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/context.h @@ -0,0 +1,15 @@ +/* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * context.h: Parameterized context test case, kernel side. + */ +#ifndef _CONTEXT_H +#define _CONTEXT_H + +#include "context_self.h" + +void add_context_tests(void); +void context_tests_cleanup(void); + +#endif diff --git a/tools/testing/selftests/ktf/selftest/context_self.h b/tools/testing/selftests/ktf/selftest/context_self.h new file mode 100644 index 0000000..3939559 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/context_self.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * context_self.h: The data structure passed between user level and kernel for the + * hybrid self tests. Included both from user space and kernel space and + * needs to be a C struct. + */ + +#ifndef KTF_CONTEXT_SELF_H +#define KTF_CONTEXT_SELF_H + +#define CONTEXT_SELF_MAX_TEXT 30 + +/* A simple example parameter block: + * For verification purposes it can be useful to have a field + * like 'magic' below, which serves for the purpose of + * a sanity check that the parameters sent by the user program + * actually corresponds to what the kernel expects: + */ +struct test_parameter_block { + long magic; + long myvalue; + char s[CONTEXT_SELF_MAX_TEXT+1]; +}; + +/* Constants for the selftest.param_context test: */ +#define CONTEXT_MSG "from user to kernel" +#define CONTEXT_MAGIC1 0xfaaa1234UL +#define CONTEXT_MAGIC2 0xaabbccUL +#define CONTEXT_MAGIC3 0x123456UL + +#endif diff --git a/tools/testing/selftests/ktf/selftest/hybrid.c b/tools/testing/selftests/ktf/selftest/hybrid.c new file mode 100644 index 0000000..999a7d8 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/hybrid.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * hybrid.c: Hybrid (combined user level and kernel) self tests, + * kernel side: + */ + +#include "ktf.h" +#include "hybrid.h" + +/* First a simple message passing test that just verifies that we receive + * "out-of-band" data from user space: + */ + +TEST(selftest, msg) +{ + /* Accept data of type 'struct hybrid_self_params' (defined in hybrid_self.h) + * from user mode. This functionality is to allow user mode to test something, + * for instance that a certain parameter is handled in a specific way in the kernel. + * The user then has the option to provide data to the kernel out-of-band to + * tell the kernel side what to expect. + * In this test, just verify that data has been transmitted correctly: + */ + KTF_USERDATA(self, hybrid_self_params, data); + + EXPECT_STREQ(data->text_val, HYBRID_MSG); + EXPECT_LONG_EQ(data->val, HYBRID_MSG_VAL); +} + +void add_hybrid_tests(void) +{ + ADD_TEST(msg); +} diff --git a/tools/testing/selftests/ktf/selftest/hybrid.h b/tools/testing/selftests/ktf/selftest/hybrid.h new file mode 100644 index 0000000..0ba6f72 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/hybrid.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * hybrid.h: Hybrid (combined user level and kernel) self tests, + * kernel side, internal interface: + */ + +#ifndef KTF_HYBRID_H +#define KTF_HYBRID_H + +#include "hybrid_self.h" + +/* The kernel part of hybrid tests must be added to KTFs set of tests like any other tests, + * in fact from KTF's kernel perspective it is like any other test, except that it likely will + * fail if called without the context provided from the user space side. + * + * This function adds the tests declared in hybrid.c + */ +void add_hybrid_tests(void); + + +#endif diff --git a/tools/testing/selftests/ktf/selftest/hybrid_self.h b/tools/testing/selftests/ktf/selftest/hybrid_self.h new file mode 100644 index 0000000..21c6c92 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/hybrid_self.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * hybrid_self.h: The data structure passed between user level and kernel for the + * hybrid self tests. Included both from user space and kernel space and + * needs to be a C struct. + */ + +#ifndef KTF_HYBRID_SELF_H +#define KTF_HYBRID_SELF_H + +#define HYBRID_SELF_MAX_TEXT 127 + +struct hybrid_self_params +{ + char text_val[HYBRID_SELF_MAX_TEXT+1]; + unsigned long val; +}; + + +/* Constants for the selftest.msg test: */ +#define HYBRID_MSG "a little test string" +#define HYBRID_MSG_VAL 0xffUL + +#endif diff --git a/tools/testing/selftests/ktf/selftest/ktf_syms.txt b/tools/testing/selftests/ktf/selftest/ktf_syms.txt new file mode 100644 index 0000000..721ae98 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/ktf_syms.txt @@ -0,0 +1,17 @@ +#module ktf +#header ktf_map.h +ktf_map_init +ktf_map_elem_init +ktf_map_insert +ktf_map_find +ktf_map_find_first +ktf_map_remove +ktf_map_elem_get +ktf_map_elem_put +ktf_map_find_next +ktf_map_delete_all +#header ktf_cov.h +ktf_cov_entry_find +ktf_cov_entry_put +ktf_cov_enable +ktf_cov_disable diff --git a/tools/testing/selftests/ktf/selftest/self.c b/tools/testing/selftests/ktf/selftest/self.c new file mode 100644 index 0000000..8b7a582 --- /dev/null +++ b/tools/testing/selftests/ktf/selftest/self.c @@ -0,0 +1,661 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0 + * + * self.c: Some simple self tests for KTF + */ +#include <linux/module.h> +#include <linux/mm_types.h> +#include <linux/slab.h> +#include <linux/slab_def.h> + +#include "ktf.h" +#include "ktf_map.h" +#include "ktf_cov.h" +#include "ktf_syms.h" + +#include "hybrid.h" +#include "context.h" + +MODULE_LICENSE("GPL"); + +struct map_test_ctx { + struct ktf_context k; +}; + +static struct map_test_ctx s_mctx[4]; + +/* Declare a simple handle with no contexts for simple (unparameterized) tests: */ +KTF_INIT(); + +/* For tests that defines multiple test cases + * (e.g. if the test scope requires application of each test on several devices or + * other abstract contexts, definable by the test module) + */ +static KTF_HANDLE_INIT(dual_handle); +static KTF_HANDLE_INIT(single_handle); +static KTF_HANDLE_INIT(no_handle); +static KTF_HANDLE_INIT_VERSION(wrongversion_handle, 0, false); + +static struct map_test_ctx *to_mctx(struct ktf_context *ctx) +{ + return container_of(ctx, struct map_test_ctx, k); +} + +struct myelem { + struct ktf_map_elem foo; + int freed; + int order; +}; + +/* --- Simple insertion and removal test --- */ + +TEST(selftest, simplemap) +{ + int i; + const int nelems = 3; + struct map_test_ctx *mctx = to_mctx(ctx); + struct ktf_map tm; + struct myelem e[nelems]; + + if (mctx) + tlog(T_DEBUG, "ctx %s", mctx->k.elem.key); + else + tlog(T_DEBUG, "ctx <none>"); + + ktf_map_init(&tm, NULL, NULL); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax")); + + for (i = 0; i < nelems; i++) { + EXPECT_LONG_EQ(i, ktf_map_size(&tm)); + EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo)); + } + EXPECT_LONG_EQ(i, ktf_map_size(&tm)); + + /* Should be sorted alphabetically so we get 'bar' back: */ + EXPECT_ADDR_EQ(&e[1].foo, ktf_map_find_first(&tm)); + + for (i = 0; i < nelems; i++) { + EXPECT_LONG_EQ(nelems - i, ktf_map_size(&tm)); + EXPECT_ADDR_EQ(&e[i].foo, ktf_map_remove(&tm, e[i].foo.key)); + } + EXPECT_LONG_EQ(0, ktf_map_size(&tm)); +} + +/* --- Reference counting test --- */ + +/* should be called when refcount is 0. */ +static void myelem_free(struct ktf_map_elem *elem) +{ + struct myelem *myelem = container_of(elem, struct myelem, foo); + + myelem->freed = 1; +} + +TEST(selftest, mapref) +{ + int i; + const int nelems = 3; + struct myelem e[nelems], *ep; + struct ktf_map tm; + struct ktf_map_elem *elem; + + ktf_map_init(&tm, NULL, myelem_free); + /* Init map elems with "foo" "bar" "zax" */ + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax")); + + /* Insert elems and drop our refcounts (map still holds ref) */ + for (i = 0; i < nelems; i++) { + EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo)); + ktf_map_elem_put(&e[i].foo); + } + + /* This macro takes (and drops) refcount for each elem */ + ktf_map_for_each_entry(ep, &tm, foo) + ep->freed = 0; + + for (i = 0; i < nelems; i++) { + elem = ktf_map_remove(&tm, e[i].foo.key); + EXPECT_INT_EQ(0, e[i].freed); + /* free our ref, now free function should be called. */ + ktf_map_elem_put(elem); + EXPECT_INT_EQ(1, e[i].freed); + } + + ktf_map_delete_all(&tm); + EXPECT_LONG_EQ(0, ktf_map_size(&tm)); +} + +/* --- Test that the expect macros work as if-then-else single statement */ +TEST(selftest, statements) +{ + char c; + char *cp = &c; + /* These are mostly intended as compilation syntax tests */ + if (_i) + EXPECT_TRUE(true); + else + EXPECT_FALSE(false); + if (_i) + ASSERT_TRUE(true); + else + ASSERT_FALSE(false); + if (_i) + ASSERT_OK_ADDR(cp); + else + ASSERT_OK_ADDR_GOTO(cp, out); + if (_i) + ASSERT_OK_ADDR_BREAK(cp); +out: + EXPECT_TRUE(true); +} + +/* --- Compare function test --- */ + +/* key comparison function */ +static int myelem_cmp(const char *key1, const char *key2) +{ + int i1 = *((int *)key1); + int i2 = *((int *)key2); + + if (i1 < i2) + return -1; + else if (i1 > i2) + return 1; + return 0; +} + +TEST(selftest, mapcmpfunc) +{ + int i; + const int nelems = 3; + struct myelem e[nelems], *ep; + struct ktf_map tm; + + ktf_map_init(&tm, myelem_cmp, NULL); + /* Init map elems with keys "foo" "bar" "zax" */ + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[0].foo, "foo")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[1].foo, "bar")); + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[2].foo, "zax")); + + /* Insert elems with order values 3, 2, 1. Ensure we see order + * 1, 2, 3 on retrieval. + */ + for (i = 0; i < nelems; i++) { + e[i].order = nelems - i; + EXPECT_INT_EQ(0, ktf_map_elem_init(&e[i].foo, + (char *)&e[i].order)); + EXPECT_INT_EQ(0, ktf_map_insert(&tm, &e[i].foo)); + } + i = 1; + /* Ensure ordering via compare function is respected */ + ktf_map_for_each_entry(ep, &tm, foo) + EXPECT_INT_EQ(ep->order, i++); + + ktf_map_delete_all(&tm); + EXPECT_LONG_EQ(0, ktf_map_size(&tm)); +} + +/* --- Verify that key name is truncated at KTF_MAX_NAME length --- */ + +TEST(selftest, map_keyoverflow) +{ + struct myelem e; + struct ktf_map tm; + char jumbokey[KTF_MAX_NAME + 2]; + char jumbokey_truncated[KTF_MAX_NAME + 1]; + + ktf_map_init(&tm, NULL, NULL); + memset(jumbokey, 'x', KTF_MAX_NAME + 1); + memset(jumbokey_truncated, 'x', KTF_MAX_NAME); + jumbokey_truncated[KTF_MAX_NAME] = '\0'; + EXPECT_INT_EQ(0, ktf_map_elem_init(&e.foo, jumbokey)); + EXPECT_TRUE(strcmp(e.foo.key, jumbokey_truncated) == 0); +} + +struct mykey { + unsigned long address; + unsigned long size; +}; + +/* Comparison here is to check if k1's address falls in range + * [k2->address, k2->address + k2->size]. Similar compare used in + * ktf_cov to figure out if a function address lies within the function + * code. + */ +static int custom_compare(const char *key1, const char *key2) +{ + struct mykey *k1 = (struct mykey *)key1; + struct mykey *k2 = (struct mykey *)key2; + + if (k1->address < k2->address) + return -1; + if (k1->address >= (k2->address + k2->size)) + return 1; + return 0; +} + +/* --- Verify that opaque keys with custom compare function work --- */ + +TEST(selftest, map_customkey) +{ + const int nelems = 3; + int baseaddr = 1024; + struct ktf_map cm; + struct mykey keys[nelems], search; + struct myelem elems[nelems]; + int i, j; + + ktf_map_init(&cm, custom_compare, NULL); + + /* Ensure we can add entries and then retrieve them via search key. */ + for (i = 0; i < nelems; i++) { + baseaddr += (i << 2); + keys[i].address = baseaddr; + keys[i].size = (i + 1) << 2; + ASSERT_INT_EQ_GOTO(ktf_map_elem_init(&elems[i].foo, + (char *)&keys[i]), + 0, done); + ASSERT_INT_EQ_GOTO(ktf_map_insert(&cm, &elems[i].foo), 0, done); + } + + baseaddr = 1024; + + /* Ensure all search addresses within range of [base address, size] + * find appropriate entries. + */ + for (i = 0; i < nelems; i++) { + baseaddr += (i << 2); + for (j = 0; j < (i + 1) << 2; j++) { + search.address = baseaddr + j; + search.size = 0; + ASSERT_ADDR_EQ_GOTO(ktf_map_find_entry(&cm, + (char *)&search, + struct myelem, + foo), + &elems[i], done); + } + } + +done: + ktf_map_delete_all(&cm); +} + +TEST(selftest, dummy) +{ + /* The default handle does not have any contexts in this test set */ + ASSERT_FALSE(ctx); +} + +TEST(selftest, wrongversion) +{ + tlog(T_INFO, "This test should never have run - wrong version\n!!!"); + EXPECT_TRUE(false); +} + +static void add_map_tests(void) +{ + ADD_TEST(dummy); + ADD_LOOP_TEST(statements, 0, 2); + ADD_TEST_TO(dual_handle, simplemap); + ADD_TEST_TO(dual_handle, mapref); + ADD_TEST_TO(dual_handle, mapcmpfunc); + ADD_TEST(map_keyoverflow); + ADD_TEST(map_customkey); + + terr("-- version check test: --"); + /* This should fail */ + ADD_TEST_TO(wrongversion_handle, wrongversion); +} + +static int probecount; +static int proberet; + +KTF_ENTRY_PROBE(printk, printkhandler) +{ + probecount++; + + KTF_ENTRY_PROBE_RETURN(0); +} + +static int entryarg0, entryarg1; + +KTF_ENTRY_PROBE(probeargtest, probeargtesthandler) +{ + entryarg0 = (int)KTF_ENTRY_PROBE_ARG0; + entryarg1 = (int)KTF_ENTRY_PROBE_ARG1; + KTF_ENTRY_PROBE_RETURN(0); +} + +noinline void probeargtest(int arg0, int arg1) +{ + tlog(T_INFO, "got args %d, %d\n", arg0, arg1); +} + +TEST(selftest, probeentry) +{ + probecount = 0; + ASSERT_INT_EQ(KTF_REGISTER_ENTRY_PROBE(printk, printkhandler), 0); + /* Need T_WARN for unconditional printk() */ + twarn("Testing kprobe entry..."); + ASSERT_INT_GT_GOTO(probecount, 0, done); + ASSERT_INT_EQ_GOTO(KTF_REGISTER_ENTRY_PROBE(probeargtest, + probeargtesthandler), + 0, done); + probeargtest(1, 2); + ASSERT_INT_EQ_GOTO(entryarg0, 1, done); + ASSERT_INT_EQ_GOTO(entryarg1, 2, done); +done: + KTF_UNREGISTER_ENTRY_PROBE(probeargtest, probeargtesthandler); + KTF_UNREGISTER_ENTRY_PROBE(printk, printkhandler); +} + +static int override_failed; + +noinline int myfunc(int i) +{ + override_failed = 1; + return i; +} + +KTF_OVERRIDE(myfunc, myfunc_override) +{ + KTF_SET_RETURN_VALUE(0); + KTF_OVERRIDE_RETURN; +} + +TEST(selftest, override) +{ + override_failed = 0; + + ASSERT_INT_EQ(KTF_REGISTER_OVERRIDE(myfunc, myfunc_override), 0); + + (void)myfunc(0); + + /* Verify override function runs instead. */ + ASSERT_TRUE_GOTO(override_failed == 0, done); + + /* Verify override function modifies return value. */ + ASSERT_INT_EQ_GOTO(myfunc(100), 0, done); + ASSERT_TRUE_GOTO(override_failed == 0, done); +done: + KTF_UNREGISTER_OVERRIDE(myfunc, myfunc_override); +} + +noinline int probesum(int a, int b) +{ + tlog(T_INFO, "Adding %d + %d", a, b); + return a + b; +} + +KTF_RETURN_PROBE(probesum, probesumhandler) +{ + tlog(T_DEBUG, "return value before modifying %ld", + regs_return_value(regs)); + KTF_SET_RETURN_VALUE(-1); + tlog(T_DEBUG, "return value after modifying %ld", + regs_return_value(regs)); + return 0; +} + +KTF_RETURN_PROBE(printk, printkrethandler) +{ + proberet = KTF_RETURN_VALUE(); + + return 0; +} + +TEST(selftest, probereturn) +{ + char *teststr = "Testing kprobe return..."; + + proberet = -1; + ASSERT_INT_EQ_GOTO(KTF_REGISTER_RETURN_PROBE(printk, printkrethandler), + 0, done); + printk(KERN_INFO "%s", teststr); + ASSERT_INT_EQ_GOTO(proberet, strlen(teststr), done); + + /* Now test modification of return value */ + ASSERT_INT_EQ_GOTO(probesum(1, 1), 2, done); + ASSERT_INT_EQ_GOTO(KTF_REGISTER_RETURN_PROBE(probesum, probesumhandler), + 0, done); + ASSERT_INT_EQ_GOTO(probesum(1, 1), -1, done); +done: + KTF_UNREGISTER_RETURN_PROBE(printk, printkrethandler); + KTF_UNREGISTER_RETURN_PROBE(probesum, probesumhandler); +} + +static void add_probe_tests(void) +{ + ADD_TEST(probeentry); + ADD_TEST(probereturn); + ADD_TEST(override); +} + +noinline void cov_counted(void) +{ + tlog(T_INFO, "got called!"); +} + +noinline void *doalloc(struct kmem_cache *c, size_t sz) +{ + if (c) + return kmem_cache_alloc(c, GFP_KERNEL); + return kmalloc(sz, GFP_KERNEL); +} + +TEST(selftest, acov) +{ + /* A very basic test just to enable and disable the coverage support, + * without the memory tracking option and without making use of it: + */ + ASSERT_INT_EQ(0, ktf_cov_enable((THIS_MODULE)->name, 0)); + ktf_cov_disable((THIS_MODULE)->name); +} + +TEST(selftest, cov) +{ + int foundp1 = 0, foundp2 = 0, foundp3 = 0, foundp4 = 0; + struct ktf_cov_entry *e; + struct ktf_cov_mem *m; + char *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL; + struct kmem_cache *c = NULL; + int oldcount; + + c = kmem_cache_create("selftest_cov_cache", + 32, 0, + SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); + + ASSERT_ADDR_NE(NULL, c); + + tlog(T_INFO, "Allocated cache %p : %s %u\n", c, c->name, c->object_size); + ASSERT_INT_EQ(0, ktf_cov_enable((THIS_MODULE)->name, KTF_COV_OPT_MEM)); + + e = ktf_cov_entry_find((unsigned long)cov_counted, 0); + ASSERT_ADDR_NE_GOTO(e, NULL, done); + oldcount = e->count; + ktf_cov_entry_put(e); + cov_counted(); + e = ktf_cov_entry_find((unsigned long)cov_counted, 0); + ASSERT_ADDR_NE_GOTO(e, NULL, done); + if (e) { + ASSERT_INT_EQ(e->count, oldcount + 1); + ktf_cov_entry_put(e); + } + + /* Need to call a noinline fn to do allocs since this test function + * will be inlined; and to track allocations they need to come + * from this module. Don't need to do the same for kfree since + * we check every kfree() to see if it is freeing a tracked allocation. + */ + p1 = doalloc(NULL, 8); + ASSERT_ADDR_NE_GOTO(p1, NULL, done); + p2 = doalloc(NULL, 16); + ASSERT_ADDR_NE_GOTO(p2, NULL, done); + p3 = doalloc(c, 0); + ASSERT_ADDR_NE_GOTO(p3, NULL, done); + p4 = doalloc(c, 0); + ASSERT_ADDR_NE_GOTO(p4, NULL, done); + + ktf_for_each_cov_mem(m) { + if (m->key.address == (unsigned long)p1) + foundp1 = 1; + if (m->key.address == (unsigned long)p2 && m->key.size == 16) + foundp2 = 1; + if (m->key.address == (unsigned long)p3 && m->key.size == 32) + foundp3 = 1; + if (m->key.address == (unsigned long)p4) + foundp4 = 1; + } + ASSERT_INT_EQ_GOTO(foundp1, 1, done); + ASSERT_INT_EQ_GOTO(foundp2, 1, done); + ASSERT_INT_EQ_GOTO(foundp3, 1, done); + ASSERT_INT_EQ_GOTO(foundp4, 1, done); + kfree(p1); + kmem_cache_free(c, p4); + /* Didn't free p2/p3 - should still be on our cov_mem list */ + foundp1 = 0; + foundp2 = 0; + foundp3 = 0; + foundp4 = 0; + ktf_for_each_cov_mem(m) { + if (m->key.address == (unsigned long)p1) + foundp1 = 1; + if (m->key.address == (unsigned long)p2) + foundp2 = 1; + if (m->key.address == (unsigned long)p3) + foundp3 = 1; + if (m->key.address == (unsigned long)p4) + foundp4 = 1; + } + ASSERT_INT_EQ_GOTO(foundp2, 1, done); + ASSERT_INT_EQ_GOTO(foundp3, 1, done); + ASSERT_INT_EQ_GOTO(foundp1, 0, done); + ASSERT_INT_EQ_GOTO(foundp4, 0, done); +done: + kfree(p2); + if (p3) + kmem_cache_free(c, p3); + ktf_cov_disable((THIS_MODULE)->name); + kmem_cache_destroy(c); +} + +static void add_cov_tests(void) +{ + ADD_TEST(acov); + /* We still seem to have some subtle issues with the memory coverage test feature, + * as sometimes allocations made by the coverage framework itself, + * for this particular test survives the cleanup function. + * Whether it is our attempt to test ourselves or a more generic problem + * is not fully understood yet, so disable this test for now: + */ + /* ADD_TEST(cov); */ +} + +KTF_THREAD(test_thread) +{ + /* ensure assertions can work in thread context */ + ASSERT_INT_EQ(1, 1); +} + +#define NUM_TEST_THREADS 20 + +static struct ktf_thread test_threads[NUM_TEST_THREADS]; + +TEST(selftest, thread) +{ + int assertions, i; + + for (i = 0; i < NUM_TEST_THREADS; i++) { + KTF_THREAD_INIT(test_thread, &test_threads[i]); + KTF_THREAD_RUN(&test_threads[i]); + } + for (i = 0; i < NUM_TEST_THREADS; i++) + KTF_THREAD_WAIT_COMPLETED(&test_threads[i]); + + assertions = (int)ktf_get_assertion_count(); + + /* Verify assertion in thread */ + ASSERT_INT_EQ(assertions, NUM_TEST_THREADS); +} + +static void add_thread_tests(void) +{ + ADD_TEST(thread); +} + +static int selftest_module_var; + +/* + * Test that ktf_find_symbol works both for module symbols and + * core kernel symbols: + */ +TEST(selftest, symbol) +{ + /* Verify finding kernel-internal symbol works. */ + ASSERT_ADDR_NE(ktf_find_symbol(NULL, "skbuff_head_cache"), NULL); + + /* Verify finding module symbols works, both when we specify the + * module name and when we don't. + */ + ASSERT_ADDR_EQ(ktf_find_symbol(NULL, "selftest_module_var"), + &selftest_module_var); + + ASSERT_ADDR_EQ(ktf_find_symbol("selftest", "selftest_module_var"), + &selftest_module_var); +} + +static void add_symbol_tests(void) +{ + ADD_TEST(symbol); +} + +static int __init selftest_init(void) +{ + int ret = KTF_CONTEXT_ADD_TO(dual_handle, &s_mctx[1].k, "map1"); + + tlog(T_DEBUG, "map1 gets %d", ret); + if (ret) + return ret; + + ret = KTF_CONTEXT_ADD_TO(dual_handle, &s_mctx[2].k, "map2"); + if (ret) + goto fail; + + ret = KTF_CONTEXT_ADD_TO(single_handle, &s_mctx[3].k, "map3"); + if (ret) + goto fail; + + ktf_resolve_symbols(); + + add_map_tests(); + add_probe_tests(); + add_cov_tests(); + add_thread_tests(); + add_hybrid_tests(); + add_context_tests(); + add_symbol_tests(); + tlog(T_INFO, "selftest: loaded"); + return 0; +fail: + KTF_CLEANUP(); + return ret; +} + +static void __exit selftest_exit(void) +{ + context_tests_cleanup(); + KTF_HANDLE_CLEANUP(single_handle); + KTF_HANDLE_CLEANUP(dual_handle); + KTF_HANDLE_CLEANUP(no_handle); + KTF_CLEANUP(); + tlog(T_INFO, "selftest: unloaded"); +} + +module_init(selftest_init); +module_exit(selftest_exit);
Implementation of the main part of the user library to communicate with the kernel side of ktf about tests, configuration and test results.
ktf.h: User mode side of KTF extensions to the gtest unit test framework. ktf_int.cc: Implementation of Gtest user land test management ktf_int.h: User mode side of extension to the gtest unit test framework:
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/lib/Makefile | 21 +- tools/testing/selftests/ktf/lib/ktf.h | 114 ++- tools/testing/selftests/ktf/lib/ktf_int.cc | 1031 +++++++++++++++++++++- tools/testing/selftests/ktf/lib/ktf_int.h | 84 ++- 4 files changed, 1250 insertions(+) create mode 100644 tools/testing/selftests/ktf/lib/Makefile create mode 100644 tools/testing/selftests/ktf/lib/ktf.h create mode 100644 tools/testing/selftests/ktf/lib/ktf_int.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_int.h
diff --git a/tools/testing/selftests/ktf/lib/Makefile b/tools/testing/selftests/ktf/lib/Makefile new file mode 100644 index 0000000..c2be04b --- /dev/null +++ b/tools/testing/selftests/ktf/lib/Makefile @@ -0,0 +1,21 @@ + +GTEST_CFLAGS ?= -I$(GTEST_PATH)/include -DGTEST_HAS_PTHREAD=1 -lpthread +GTEST_LIBS ?= -L$(GTEST_PATH)/lib64 -lgtest -lpthread +NETLINK_CFLAGS ?= $(shell pkgconf --cflags libnl-genl-3.0) +HOST_EXTRACFLAGS = -I$(srctree)/$(src)/.. $(NETLINK_CFLAGS) $(GTEST_CFLAGS) \ + -Wall -Werror -Wno-packed-bitfield-compat -D_GNU_SOURCE +HOST_EXTRACXXFLAGS = -I$(srctree)/$(src)/.. $(NETLINK_CFLAGS) $(GTEST_CFLAGS) \ + -Wall \ + -Wno-packed-bitfield-compat \ + -Wno-pointer-arith -Werror \ + -D__FILENAME__="`basename $<`" + +hostcxxlibs-y := libktf.so +libktf-cshobjs = ktf_unlproto.o +libktf-cxxshobjs = ktf_int.o ktf_run.o ktf_debug.o + +targets := $(addprefix $(obj)/,$(libktf-cshobjs)) \ + $(addprefix $(obj)/,$(libktf-cxxshobjs)) \ + $(addprefix $(obj)/,$(hostcxxlibs-y)) + +__build: $(targets) diff --git a/tools/testing/selftests/ktf/lib/ktf.h b/tools/testing/selftests/ktf/lib/ktf.h new file mode 100644 index 0000000..942eb28 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf.h: User mode side of KTF extensions to the gtest unit test framework. + * Include this to write hybrid tests + * + */ +#ifndef _KTF_H +#define _KTF_H +#include <gtest/gtest.h> + +namespace ktf +{ + + /* Interfaces intended to be used directly by programs: + * ---------------------------------------------------- + */ + class KernelTest; + + /* Invoke the kernel test - to be called directly from user mode + * hybrid tests: + */ + void run(KernelTest* kt, std::string ctx = ""); + + /* Function for enabling/disabling coverage for module */ + int set_coverage(std::string module, unsigned int opts, bool enabled); + + typedef void (*configurator)(void); + + // Initialize KTF: + // If necessary, supply a callback that uses the KTF_CONTEXT_CFG* macros below + // to configure any necessary contexts: + void setup(configurator c = NULL); + +} // end namespace ktf + +/* HTEST: Define user part of a hybrid test. + * Hybrid tests are tests that have a user and a kernel counterpart, + * to allow testing of interaction between user mode and the kernel: + */ +#define HTEST(__setname,__testname) \ + class __setname ## _ ## __testname : public ktf::test_cb \ + {\ + public:\ + __setname ## _ ## __testname() {\ + ktf::add_wrapper(#__setname,#__testname,as_test_cb()); \ + }\ + virtual void fun(ktf::KernelTest* kt); \ + }; \ + __setname ## _ ## __testname \ + __setname ## _ ## __testname ## _value;\ + void __setname ## _ ## __testname::fun(ktf::KernelTest* self) + + +/* Part of KTF support for hybrid tests: allocate/get a reference to + * an out-of-band user data pointer: + */ +#define KTF_USERDATA(__kt_ptr, __priv_datatype, __priv_data) \ + struct __priv_datatype *__priv_data = \ + (struct __priv_datatype *)get_priv(__kt_ptr, sizeof(struct __priv_datatype)); \ + ASSERT_TRUE(__priv_data); \ + ASSERT_EQ(get_priv_sz(__kt_ptr), sizeof(struct __priv_datatype)) + +/* KTF support for configurable contexts: + * Send a configuation data structure to the given context name. + */ +#define KTF_CONTEXT_CFG(__context_name, __context_type_name, __priv_datatype, __priv_data) \ + ktf::configure_context(__context_name, __context_type_name, \ + (struct __priv_datatype *)__priv_data, \ + sizeof(__priv_datatype)) +/* Alternative to KTF_CONTEXT_CFG: If there are multiple contexts with the same name + * (but with different handles) use a test name to identify the context to be configured + */ +#define KTF_CONTEXT_CFG_FOR_TEST(__test_name, __context_type_name, __priv_datatype, __priv_data) \ + ktf::configure_context_for_test(__test_name, __context_type_name, \ + (struct __priv_datatype *)__priv_data, \ + sizeof(__priv_datatype)) + + + +/* Private interfaces (needed by macro definitions above) + * ------------------------------------------------------ + */ + +namespace ktf { + class test_cb + { + public: + virtual ~test_cb() {} + virtual test_cb* as_test_cb() { return this; } + virtual void fun(KernelTest* kt) {} + }; + + /* Function for adding a user level test wrapper */ + void add_wrapper(const std::string setname, const std::string testname, + test_cb* tcb); + + /* get a priv pointer of the given size, allocate if necessary */ + void* get_priv(KernelTest* kt, size_t priv_sz); + + /* Get the size of the existing priv data */ + size_t get_priv_sz(KernelTest *kt); + + // Configure ktf context - to be used via KTF_CONTEXT_CFG*(): + void configure_context(const std::string context, const std::string type_name, + void *data, size_t data_sz); + void configure_context_for_test(const std::string testname, const std::string type_name, + void *data, size_t data_sz); +} // end namespace ktf + +#endif diff --git a/tools/testing/selftests/ktf/lib/ktf_int.cc b/tools/testing/selftests/ktf/lib/ktf_int.cc new file mode 100644 index 0000000..6ac1f54 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_int.cc @@ -0,0 +1,1031 @@ +/* + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_int.cpp: Implementation of Gtest user land test management + * for kernel and hybrid test functionality provided by KTF. + */ +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <unistd.h> +#include "kernel/ktf_unlproto.h" +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <map> +#include <set> +#include <string> +#include "ktf_int.h" +#include "ktf_debug.h" + +#include <netlink/version.h> + +int devcnt = 0; + +namespace ktf +{ + +struct nl_sock* sock = NULL; +int family = -1; + +int printed_header = 0; + +typedef std::map<std::string, KernelTest*> testmap; +typedef std::map<std::string, test_cb*> wrappermap; + +class testset +{ +public: + testset() : setnum(0) + { } + + ~testset() + { + for (testmap::iterator it = tests.begin(); it != tests.end(); ++it) + delete it->second; + } + + testmap tests; + stringvec test_names; + wrappermap wrapper; + int setnum; +}; + +/* ConfigurableContext keeps track of a ktf_context that requires configuration. + * Context names are unique within a handle, so a handle ID is necessary to + * identify the context. The actual configuration data must be agreed upon between + * user mode and kernel mode on a per context basis. They can use type_id + * to identify which type of parameter a context needs. + * The type_id is also used to create new contexts in the kernel. + * The kernel implementation must enable such dynamically extensible context sets + * on a per type_id basis. + */ +class ConfigurableContext +{ +public: + ConfigurableContext(const std::string& name, const std::string& type_name, + unsigned int hid, int cfg_stat); + + std::string str_state(); + int Configure(void *data, size_t data_sz); + + const std::string& Type() + { + return type_name; + } + + std::string name; + int handle_id; + std::string type_name; + int cfg_stat; +}; + +typedef std::map<std::string, testset> setmap; +typedef std::setstd::string stringset; +typedef std::vector<ConfigurableContext*> context_vector; + +struct name_iter +{ + setmap::iterator it; + std::string setname; +}; + +class ContextType +{ +public: + ContextType(int handle_id, const std::string& type_name); + int handle_id; + std::string type_name; +}; + + ContextType::ContextType(int hid, const std::string& tn) + : handle_id(hid), + type_name(tn) +{} + +/* We trick the gtest template framework + * to get a new set of test names as a side effect of + * invocation of get_test_names() + */ + +/* Wrap globals in an object to control init order and + * memory cleanup: + */ +class KernelTestMgr +{ +public: + KernelTestMgr() : next_set(0), cur(NULL) + { } + + ~KernelTestMgr(); + + testset& find_add_set(std::string& setname); + testset& find_add_test(std::string& setname, std::string& testname); + void add_test(const std::string& setname, const char* tname, unsigned int handle_id); + KernelTest* find_test(const std::string&setname, const std::string& testname, std::string* ctx); + void add_wrapper(const std::string setname, const std::string testname, test_cb* tcb); + + stringvec& get_set_names() { return set_names; } + stringvec get_test_names(); + + stringvec get_testsets() + { + return set_names; + } + + std::string get_current_setname() + { + return cur->setname; + } + + stringvec& get_contexts(unsigned int id) + { + return handle_to_ctxvec[id]; + } + + void add_cset(unsigned int hid, stringvec& ctxs); + void add_ctype(unsigned int hid, const std::string& type_name); + std::vector<ConfigurableContext*> add_configurable_context(const std::string& ctx, + const std::string& type_name, + unsigned int hid, int cfg_stat); + std::vector<ConfigurableContext*> add_configurable_contexts(const std::string& ctx, + std::vector<ContextType*> type_vec); + std::vector<ConfigurableContext*> find_contexts(const std::string& ctx, const std::string& type_name); + + /* Contexts may be created on the fly if the kernel supports it for this type_name: */ + std::vector<ConfigurableContext*> maybe_create_context(const std::string& ctx, + const std::string& type_name); + + /* Update the list of contexts returned from the kernel with a newly created one */ + void add_context(unsigned int hid, const std::string& ctx); +private: + setmap sets; + stringvec test_names; + stringvec set_names; + stringset kernelsets; + std::map<unsigned int, stringvec> handle_to_ctxvec; + std::map<std::string, context_vector> cfg_contexts; + + // Context types that allows dynamically created contexts: + std::map<std::string, std::vector<ContextType*> > ctx_types; + int next_set; + name_iter* cur; +}; + +KernelTestMgr::~KernelTestMgr() +{ + std::map<std::string, context_vector>::iterator it; + for (it = cfg_contexts.begin(); it != cfg_contexts.end(); ++it) + { + context_vector::iterator vit; + for (vit = it->second.begin(); vit != it->second.end(); ++vit) + delete *vit; + } + + std::map<std::string, std::vector<ContextType*> >::iterator tit; + for (tit = ctx_types.begin(); tit != ctx_types.end(); ++tit) + { + std::vector<ContextType*>::iterator ttit; + for (ttit = tit->second.begin(); ttit != tit->second.end(); ++ttit) + delete *ttit; + } +} + +context_vector KernelTestMgr::find_contexts(const std::string& ctx, const std::string& type_name) +{ + std::mapstd::string,context_vector::iterator it; + it = cfg_contexts.find(ctx); + if (it == cfg_contexts.end()) + return maybe_create_context(ctx, type_name); + else + return it->second; +} + +context_vector KernelTestMgr::maybe_create_context(const std::string& ctx, const std::string& type_name) +{ + std::map<std::string, std::vector<ContextType*> >::iterator it; + it = ctx_types.find(type_name); + if (it == ctx_types.end()) + return context_vector(); + else + return add_configurable_contexts(ctx, it->second); +} + +void KernelTestMgr::add_context(unsigned int hid, const std::string& ctx) +{ + handle_to_ctxvec[hid].push_back(ctx); +} + + +KernelTestMgr& kmgr() +{ + static KernelTestMgr kmgr_; + return kmgr_; +} + +testset& KernelTestMgr::find_add_test(std::string& setname, std::string& testname) +{ + testset& ts(find_add_set(setname)); + test_names.push_back(testname); + return ts; +} + +testset& KernelTestMgr::find_add_set(std::string& setname) +{ + bool new_set = false; + + log(KTF_DEBUG, "find_add_set(%s)\n", setname.c_str()); + + stringset::iterator it = kernelsets.find(setname); + if (it == kernelsets.end()) { + kernelsets.insert(setname); + set_names.push_back(setname); + new_set = true; + } + + /* This implicitly adds a new testset to sets, if it's not there: */ + testset& ts = sets[setname]; + if (new_set) + { + ts.setnum = next_set++; + log(KTF_INFO, "added %s (set %d) total %lu sets\n", setname.c_str(), ts.setnum, sets.size()); + } + return ts; +} + + +void KernelTestMgr::add_test(const std::string& setname, const char* tname, + unsigned int handle_id) +{ + log(KTF_INFO_V, "add_test: %s.%s", setname.c_str(),tname); + logs(KTF_INFO_V, + if (handle_id) + fprintf(stderr, " [id %d]\n", handle_id); + else + fprintf(stderr, "\n")); + std::string name(tname); + new KernelTest(setname, tname, handle_id); +} + + +/* Here we might get called with test names expanded with context names */ +KernelTest* KernelTestMgr::find_test(const std::string&setname, + const std::string& testname, + std::string* pctx) +{ + size_t pos; + log(KTF_DEBUG, "find test %s.%s\n", setname.c_str(), testname.c_str()); + + /* Try direct lookup first: */ + KernelTest* kt = sets[setname].tests[testname]; + if (kt) { + *pctx = std::string(); + return kt; + } + + /* If we don't have any contexts set, no need to parse name: */ + if (handle_to_ctxvec.empty()) + return NULL; + + pos = testname.find_last_of('_'); + while (pos >= 0) { + std::string tname = testname.substr(0,pos); + std::string ctx = testname.substr(pos + 1, testname.npos); + *pctx = ctx; + kt = sets[setname].tests[tname]; + if (kt) + return kt; + /* context name might contain an '_' , iterate on: */ + pos = tname.find_last_of('_'); + } + return NULL; +} + + +void KernelTestMgr::add_cset(unsigned int hid, stringvec& ctxs) +{ + log(KTF_INFO, "hid %d: ", hid); + logs(KTF_INFO, for (stringvec::iterator it = ctxs.begin(); it != ctxs.end(); ++it) + fprintf(stderr, "%s ", it->c_str()); + fprintf(stderr, "\n")); + handle_to_ctxvec[hid] = ctxs; +} + +void KernelTestMgr::add_ctype(unsigned int hid, const std::string& type_name) +{ + log(KTF_INFO, "hid %d: dynamical type: %s\n", hid, type_name.c_str()); + ctx_types[type_name].push_back(new ContextType(hid, type_name)); +} + +std::vector<ConfigurableContext*> KernelTestMgr::add_configurable_context(const std::string& ctx, + const std::string& type_name, + unsigned int hid, int cfg_stat) +{ + cfg_contexts[ctx].push_back(new ConfigurableContext(ctx, type_name, hid, cfg_stat)); + return cfg_contexts[ctx]; +} + +/* Function for adding a wrapper user level test */ +void KernelTestMgr::add_wrapper(const std::string setname, const std::string testname, + test_cb* tcb) +{ + log(KTF_DEBUG, "add_wrapper: %s.%s\n", setname.c_str(),testname.c_str()); + testset& ts = sets[setname]; + + /* Depending on C++ initialization order which vary between compiler version + * (sigh!) either the kernel tests have already been processed or we have to store + * this object in wrapper for later insertion: + */ + KernelTest *kt = ts.tests[testname]; + if (kt) { + log(KTF_DEBUG_V, "Assigning user_test for %s.%s\n", + setname.c_str(), testname.c_str()); + kt->user_test = tcb; + } else { + log(KTF_DEBUG_V, "Set wrapper for %s.%s\n", + setname.c_str(), testname.c_str()); + ts.wrapper[testname] = tcb; + } +} + +std::vector<ConfigurableContext*> KernelTestMgr::add_configurable_contexts(const std::string& ctx, + std::vector<ContextType*> type_vec) +{ + std::vector<ContextType*>::iterator it = type_vec.begin(); + for (; it != type_vec.end(); ++it) { + /* We use ENODEV (instead of the kernel's ENOENT to indicate to ConfigurableContext that + * this context was not reported in the query, and thus need to be added locally upon a + * successful configuration: + */ + cfg_contexts[ctx].push_back(new ConfigurableContext(ctx, (*it)->type_name, (*it)->handle_id, ENODEV)); + } + return cfg_contexts[ctx]; +} + + +stringvec KernelTestMgr::get_test_names() +{ + if (!cur) { + cur = new name_iter(); + cur->it = sets.begin(); + } + + /* Filter out any combined tests that do not have a kernel counterpart loaded */ + while (cur->it->second.wrapper.size() != 0 && cur->it != sets.end()) { + if (cur->it->second.test_names.size() == 0) + log(KTF_INFO, "Note: Skipping test suite %s which has combined tests with no kernel counterpart\n", + cur->it->first.c_str()); + ++(cur->it); + } + + if (cur->it == sets.end()) { + delete cur; + cur = NULL; + return stringvec(); + } + + stringvec& v = cur->it->second.test_names; + cur->setname = cur->it->first; + + ++(cur->it); + return v; +} + +ConfigurableContext::ConfigurableContext(const std::string& name_, const std::string& type_name_, + unsigned int hid, int cfg_stat_) + : name(name_), + handle_id(hid), + type_name(type_name_), + cfg_stat(cfg_stat_) +{ + log(KTF_INFO, "%s[%s] (hid %d): state: %s\n", + name.c_str(), type_name.c_str(), hid, str_state().c_str()); +} + +std::string ConfigurableContext::str_state() +{ + switch (cfg_stat) { + case 0: + return std::string("READY"); + case ENOENT: + return std::string("UNCONFIGURED"); + case ENODEV: + return std::string("UNCREATED"); + default: + char tmp[100]; + sprintf(tmp, "ERROR(%d)", cfg_stat); + return std::string(tmp); + } +} + +int ConfigurableContext::Configure(void *data, size_t data_sz) +{ + struct nl_msg *msg = nlmsg_alloc(); + int err; + + log(KTF_INFO, "%s, data_sz %lu\n", name.c_str(), data_sz); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, + KTF_C_REQ, 1); + nla_put_u32(msg, KTF_A_TYPE, KTF_CT_CTX_CFG); + nla_put_u64(msg, KTF_A_VERSION, KTF_VERSION_LATEST); + nla_put_string(msg, KTF_A_STR, name.c_str()); + nla_put_u32(msg, KTF_A_HID, handle_id); + nla_put_string(msg, KTF_A_FILE, type_name.c_str()); + nla_put(msg, KTF_A_DATA, data_sz, data); + + // Send message over netlink socket + nl_send_auto_complete(sock, msg); + + // Free message + nlmsg_free(msg); + + // Wait for acknowledgement: + // This function also returns error status if the message + // was not deemed ok by the kernel, but the error status + // does not resemble what the netlink recipient returned. + // + // This message receives no response beyond the error code. + // + err = nl_wait_for_ack(sock); + + if (!err && cfg_stat == ENODEV) { + // Successfully added a new context, update it's state and + // tell kmgr() about it: + kmgr().add_context(handle_id, name); + cfg_stat = 0; + } + return err; +} + +void *get_priv(KernelTest *kt, size_t sz) +{ + return kt->get_priv(sz); +} + +size_t get_priv_sz(KernelTest *kt) +{ + return kt->user_priv_sz; +} + +int set_coverage(std::string module, unsigned int opts, bool enabled) +{ + struct nl_msg *msg; + int err; + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, + KTF_C_REQ, 1); + nla_put_u32(msg, KTF_A_TYPE, + enabled ? KTF_CT_COV_ENABLE : KTF_CT_COV_DISABLE); + nla_put_u32(msg, KTF_A_COVOPT, opts); + nla_put_u64(msg, KTF_A_VERSION, KTF_VERSION_LATEST); + nla_put_string(msg, KTF_A_MOD, module.c_str()); + + // Send message over netlink socket + nl_send_auto_complete(sock, msg); + + // Free message + nlmsg_free(msg); + + //Wait for acknowledgement: + // This function also returns error status if the message + // was not deemed ok by the kernel. + // + err = nl_wait_for_ack(sock); + if (err == 0) { + // Then wait for the answer and receive it + nl_recvmsgs_default(sock); + } + return err; +} + + KernelTest::KernelTest(const std::string& sn, const char* tn, unsigned int handle_id) + : setname(sn), + testname(tn), + setnum(0), + testnum(0), + user_priv(NULL), + user_priv_sz(0), + user_test(NULL), + file(NULL), + line(-1) +{ + + name = setname; + name.append("."); + name.append(testname); + + testset& ts(kmgr().find_add_test(setname, testname)); + setnum = ts.setnum; + ts.tests[testname] = this; + + if (!handle_id) + ts.test_names.push_back(testname); + else { + stringvec& ctxv = kmgr().get_contexts(handle_id); + for (stringvec::iterator it = ctxv.begin(); it != ctxv.end(); ++it) + ts.test_names.push_back(testname + "_" + *it); + } + testnum = ts.tests.size(); + + wrappermap::iterator hit = ts.wrapper.find(testname); + if (hit != ts.wrapper.end()) { + log(KTF_DEBUG_V, "Assigning user_test from wrapper for %s.%s\n", + setname.c_str(), testname.c_str()); + user_test = hit->second; + /* Clear out wrapper entry as we skip any test sets + * with nonempty wrapper lists during test execution: + */ + ts.wrapper.erase(hit); + } +} + + +KernelTest::~KernelTest() +{ + if (user_priv) + free(user_priv); +} + +void* KernelTest::get_priv(size_t p_sz) +{ + if (!user_priv) { + user_priv = malloc(p_sz); + if (user_priv) + user_priv_sz = p_sz; + } + return user_priv; +} + +static int parse_cb(struct nl_msg *msg, void *arg); +static int debug_cb(struct nl_msg *msg, void *arg); +static int error_cb(struct nl_msg *msg, void *arg); + +int nl_connect(void) +{ + /* Allocate a new netlink socket */ + sock = nl_socket_alloc(); + if (sock == NULL){ + fprintf(stderr, "Failed to allocate a nl socket"); + exit(1); + } + + /* Connect to generic netlink socket on kernel side */ + int stat = genl_connect(sock); + if (stat) { + fprintf(stderr, "Failed to open generic netlink connection"); + exit(1); + } + + /* Ask kernel to resolve family name to family id */ + family = genl_ctrl_resolve(sock, "ktf"); + if (family <= 0) { + fprintf(stderr, "Netlink protocol family for ktf not found - is the ktf module loaded?\n"); + exit(1); + } + + /* Specify the generic callback functions for messages */ + nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL); + nl_socket_modify_cb(sock, NL_CB_INVALID, NL_CB_CUSTOM, error_cb, NULL); + return 0; +} + + +void default_test_handler(int result, const char* file, int line, const char* report) +{ + if (result >= 0) { + fprintf(stderr, "default_test_handler: Result %d: %s,%d\n",result,file,line); + } else { + fprintf(stderr, "default_test_handler: Result %d\n",result); + } +} + +test_handler handle_test = default_test_handler; + +bool setup(test_handler ht) +{ + ktf_debug_init(); + handle_test = ht; + return nl_connect() == 0; +} + + +configurator do_context_configure = NULL; + +void set_configurator(configurator c) +{ + do_context_configure = c; +} + +/* Query kernel for available tests in index order */ +stringvec& query_testsets() +{ + struct nl_msg *msg; + int err; + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, + KTF_C_REQ, 1); + nla_put_u32(msg, KTF_A_TYPE, KTF_CT_QUERY); + nla_put_u64(msg, KTF_A_VERSION, KTF_VERSION_LATEST); + + // Send message over netlink socket + nl_send_auto_complete(sock, msg); + + // Free message + nlmsg_free(msg); + + // Wait for acknowledgement: + // This function also returns error status if the message + // was not deemed ok by the kernel. + // + err = nl_wait_for_ack(sock); + if (err < 0) { + errno = -err; + return kmgr().get_set_names(); + } + + // Then wait for the answer and receive it + nl_recvmsgs_default(sock); + return kmgr().get_set_names(); +} + +stringvec get_test_names() +{ + return kmgr().get_test_names(); +} + +std::string get_current_setname() +{ + return kmgr().get_current_setname(); +} + +KernelTest* find_test(const std::string&setname, const std::string& testname, std::string* ctx) +{ + return kmgr().find_test(setname, testname, ctx); +} + +void add_wrapper(const std::string setname, const std::string testname, test_cb* tcb) +{ + kmgr().add_wrapper(setname, testname, tcb); +} + +void run_test(KernelTest* kt, std::string& ctx) +{ + if (kt->user_test) + kt->user_test->fun(kt); + else + run(kt, ctx); +} + +/* Run the kernel test */ +void run(KernelTest* kt, std::string context) +{ + struct nl_msg *msg; + + log(KTF_DEBUG_V, "START kernel test (%ld,%ld): %s\n", kt->setnum, + kt->testnum, kt->name.c_str()); + + msg = nlmsg_alloc(); + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_REQUEST, + KTF_C_REQ, 1); + nla_put_u32(msg, KTF_A_TYPE, KTF_CT_RUN); + nla_put_u64(msg, KTF_A_VERSION, KTF_VERSION_LATEST); + nla_put_string(msg, KTF_A_SNAM, kt->setname.c_str()); + nla_put_string(msg, KTF_A_TNAM, kt->testname.c_str()); + + if (!context.empty()) + nla_put_string(msg, KTF_A_STR, context.c_str()); + + /* Send any test specific out-of-band data */ + if (kt->user_priv) + nla_put(msg, KTF_A_DATA, kt->user_priv_sz, kt->user_priv); + + // Send message over netlink socket + nl_send_auto_complete(sock, msg); + + // Free message + nlmsg_free(msg); + + // Wait for acknowledgement - otherwise + // nl_recvmsg_default will sometimes take the ack for the next message.. + int err = nl_wait_for_ack(sock); + if (err < 0) { + errno = -err; + return; + } + + // Wait for the answer and receive it + nl_recvmsgs_default(sock); + + log(KTF_DEBUG_V, "END ktf::run_kernel_test %s\n", kt->name.c_str()); +} + + +void configure_context(const std::string context, const std::string type_name, void *data, size_t data_sz) +{ + context_vector ct = kmgr().find_contexts(context, type_name); + ASSERT_GE(ct.size(), 1UL) << " - no context found named " << context; + ASSERT_EQ(ct.size(), 1UL) << " - More than one context named " << context + << " - use KTF_CONTEXT_CFG_FOR_TEST to uniquely identify context."; + ASSERT_EQ(type_name, ct[0]->Type()); + ASSERT_EQ(ct[0]->Configure(data, data_sz), 0); +} + +void configure_context_for_test(const std::string& setname, const std::string& testname, + const std::string& type_name, void *data, size_t data_sz) +{ + std::string context; + KernelTest *kt = kmgr().find_test(setname, testname, &context); + context_vector ct = kmgr().find_contexts(context, type_name); + ASSERT_TRUE(kt) << " Could not find test " << setname << "." << testname; + int handle_id = kt->handle_id; + ASSERT_NE(handle_id, 0) << " test " << setname << "." << testname << " does not have a context"; + + for (context_vector::iterator it = ct.begin(); it != ct.end(); ++it) + if ((*it)->handle_id == handle_id) + { + ASSERT_EQ(type_name, (*it)->Type()); + ASSERT_EQ((*it)->Configure(data, data_sz), 0); + return; + } + ASSERT_TRUE(false) << " unconfigurable context found for test " << setname << "." << testname << "?"; +} + + +static nl_cb_action parse_one_set(std::string& setname, + std::string& testname, struct nlattr* attr) +{ + int rem = 0; + struct nlattr *nla; + const char* msg; + unsigned int handle_id = 0; + + nla_for_each_nested(nla, attr, rem) { + switch (nla_type(nla)) { + case KTF_A_HID: + handle_id = nla_get_u32(nla); + break; + case KTF_A_STR: + msg = nla_get_string(nla); + kmgr().add_test(setname, msg, handle_id); + handle_id = 0; + break; + default: + fprintf(stderr,"parse_result: Unexpected attribute type %d\n", nla_type(nla)); + return NL_SKIP; + } + } + return NL_OK; +} + + + +static int parse_query(struct nl_msg *msg, struct nlattr** attrs) +{ + int alloc = 0, rem = 0, rem2 = 0, cfg_stat; + nl_cb_action stat; + std::string setname,testname,ctx; + + /* Version 0.1.0.0 did not report version back from the kernel */ + uint64_t kernel_version = (KTF_VERSION_SET(MAJOR, 0ULL) | KTF_VERSION_SET(MINOR, 1ULL)); + + if (attrs[KTF_A_VERSION]) + kernel_version = nla_get_u64(attrs[KTF_A_VERSION]); + + /* We only got here if we were compatible enough, log that we had differences */ + if (kernel_version != KTF_VERSION_LATEST) + { + const char* note = "Note"; + bool is_compatible = + KTF_VERSION(MAJOR, KTF_VERSION_LATEST) == KTF_VERSION(MAJOR, kernel_version) && + KTF_VERSION(MINOR, KTF_VERSION_LATEST) == KTF_VERSION(MINOR, kernel_version); + if (!is_compatible) + note = "Error"; + + fprintf(stderr, + "%s: KTF version difference - user lib %llu.%llu.%llu.%llu, kernel has %llu.%llu.%llu.%llu\n", + note, + KTF_VERSION(MAJOR, KTF_VERSION_LATEST), + KTF_VERSION(MINOR, KTF_VERSION_LATEST), + KTF_VERSION(MICRO, KTF_VERSION_LATEST), + KTF_VERSION(BUILD, KTF_VERSION_LATEST), + KTF_VERSION(MAJOR, kernel_version), + KTF_VERSION(MINOR, kernel_version), + KTF_VERSION(MICRO, kernel_version), + KTF_VERSION(BUILD, kernel_version)); + if (!is_compatible) + return NL_SKIP; + } + + if (attrs[KTF_A_HLIST]) { + struct nlattr *nla, *nla2; + stringvec contexts; + unsigned int handle_id = 0; + const char* type_name = NULL; + + /* Parse info on handle IDs and associated contexts and/or + * types that allows dynamical creation of new contexts + * (defined here via KTF_A_FILE): + */ + nla_for_each_nested(nla, attrs[KTF_A_HLIST], rem) { + switch (nla_type(nla)) { + case KTF_A_HID: + handle_id = nla_get_u32(nla); + break; + case KTF_A_LIST: + nla_for_each_nested(nla2, nla, rem2) { + switch (nla_type(nla2)) { + case KTF_A_FILE: + type_name = nla_get_string(nla2); + kmgr().add_ctype(handle_id, type_name); + break; + case KTF_A_STR: + ctx = nla_get_string(nla2); + contexts.push_back(ctx); + break; + case KTF_A_MOD: + type_name = nla_get_string(nla2); + break; + case KTF_A_STAT: + cfg_stat = nla_get_u32(nla2); + kmgr().add_configurable_context(ctx, type_name, handle_id, cfg_stat); + break; + } + } + /* Add this set of contexts for the handle_id */ + kmgr().add_cset(handle_id, contexts); + handle_id = 0; + contexts.clear(); + break; + default: + fprintf(stderr,"parse_query[HLIST]: Unexpected attribute type %d\n", nla_type(nla)); + return NL_SKIP; + } + } + } + + // Now we know enough about contexts and type_ids to actually configure + // any contexts that needs to be configured, and this must be + // done before the list of tests gets spanned out because addition + // of new contexts can lead to more tests being "generated": + // + if (do_context_configure) + do_context_configure(); + + if (attrs[KTF_A_NUM]) { + alloc = nla_get_u32(attrs[KTF_A_NUM]); + log(KTF_DEBUG, "Kernel offers %d test sets:\n", alloc); + } else { + fprintf(stderr,"No test set count in kernel response??\n"); + return -1; + } + + if (attrs[KTF_A_LIST]) { + struct nlattr *nla; + + /* Parse info on test sets */ + nla_for_each_nested(nla, attrs[KTF_A_LIST], rem) { + switch (nla_type(nla)) { + case KTF_A_STR: + setname = nla_get_string(nla); + break; + case KTF_A_TEST: + stat = parse_one_set(setname, testname, nla); + if (stat != NL_OK) + return stat; + break; + default: + fprintf(stderr,"parse_query[LIST]: Unexpected attribute type %d\n", nla_type(nla)); + return NL_SKIP; + } + kmgr().find_add_set(setname); /* Just to make sure empty sets are also added */ + } + } + + return NL_OK; +} + + +static enum nl_cb_action parse_result(struct nl_msg *msg, struct nlattr** attrs) +{ + int assert_cnt = 0, fail_cnt = 0; + int rem = 0, stat; + const char *file = "no_file",*report = "no_report"; + + if (attrs[KTF_A_STAT]) { + stat = nla_get_u32(attrs[KTF_A_STAT]); + log(KTF_DEBUG, "parsed test status %d\n", stat); + if (stat) { + fprintf(stderr, "Failed to execute test in kernel - status %d\n", stat); + } + } + if (attrs[KTF_A_LIST]) { + /* Parse list of test results */ + struct nlattr *nla; + int result = -1, line = 0; + nla_for_each_nested(nla, attrs[KTF_A_LIST], rem) { + switch (nla_type(nla)) { + case KTF_A_STAT: + /* Flush previous test, if any */ + handle_test(result,file,line,report); + result = nla_get_u32(nla); + /* Our own count and report since check does such a lousy + * job in counting individual checks */ + if (result) + assert_cnt += result; + else { + fail_cnt++; + assert_cnt++; + } + break; + case KTF_A_FILE: + file = nla_get_string(nla); + if (!file) + file = "no_file"; + break; + case KTF_A_NUM: + line = nla_get_u32(nla); + break; + case KTF_A_STR: + report = nla_get_string(nla); + if (!report) + report = "no_report"; + break; + default: + fprintf(stderr,"parse_result: Unexpected attribute type %d\n", nla_type(nla)); + return NL_SKIP; + } + } + /* Handle last test */ + handle_test(result,file,line,report); + } + + return NL_OK; +} + +static enum nl_cb_action parse_cov_endis(struct nl_msg *msg, struct nlattr** attrs) +{ + enum ktf_cmd_type type = (ktf_cmd_type)nla_get_u32(attrs[KTF_A_TYPE]); + const char *cmd = type == KTF_CT_COV_ENABLE ? "enable" : "disable"; + int retval = nla_get_u32(attrs[KTF_A_STAT]); + + if (retval) + fprintf(stderr, "Coverage %s operation failed with status %d\n", cmd, retval); + return NL_OK; +} + +static int parse_cb(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + int maxtype = KTF_A_MAX+10; + struct nlattr *attrs[maxtype]; + enum ktf_cmd_type type; + + // memset(attrs, 0, sizeof(attrs)); + + /* Validate message and parse attributes */ + int err = genlmsg_parse(nlh, 0, attrs, KTF_A_MAX, ktf_get_gnl_policy()); + if (err < 0) return err; + + if (!attrs[KTF_A_TYPE]) { + fprintf(stderr, "Received kernel response without a type\n"); + return NL_SKIP; + } + + type = (ktf_cmd_type)nla_get_u32(attrs[KTF_A_TYPE]); + switch (type) { + case KTF_CT_QUERY: + return parse_query(msg, attrs); + case KTF_CT_RUN: + return parse_result(msg, attrs); + case KTF_CT_COV_ENABLE: + case KTF_CT_COV_DISABLE: + return parse_cov_endis(msg, attrs); + default: + debug_cb(msg, attrs); + } + return NL_SKIP; +} + + +static int error_cb(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + fprintf(stderr, "Received invalid netlink message - type %d\n", nlh->nlmsg_type); + return NL_OK; +} + + +static int debug_cb(struct nl_msg *msg, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + fprintf(stderr, "[Received netlink message of type %d]\n", nlh->nlmsg_type); + nl_msg_dump(msg, stderr); + return NL_OK; +} + +} // end namespace ktf diff --git a/tools/testing/selftests/ktf/lib/ktf_int.h b/tools/testing/selftests/ktf/lib/ktf_int.h new file mode 100644 index 0000000..1a06533 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_int.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_int.h: User mode side of extension to the gtest unit test framework: + * 1) Kernel test support via netlink + * 2) Standard command line parameters + * + * This file exposes some internals - for users of hybrid tests including + * ktf.h should be sufficient: + */ + +#ifndef KTF_INT_H +#define KTF_INT_H +#include <string> +#include <vector> +#include "ktf.h" + +typedef std::vectorstd::string stringvec; + +namespace ktf +{ + + /* A callback handler to be called for each assertion result */ + typedef void (*test_handler)(int result, const char* file, int line, const char* report); + + class KernelTest + { + public: + KernelTest(const std::string& setname, const char* testname, unsigned int handle_id); + ~KernelTest(); + void* get_priv(size_t priv_sz); + size_t get_priv_sz(KernelTest *kt); + std::string setname; + std::string testname; + unsigned int handle_id; + std::string name; + size_t setnum; /* This test belongs to this set in the kernel */ + size_t testnum; /* This test's index (test number) in the kernel */ + void* user_priv; /* Optional private data for the test */ + size_t user_priv_sz; /* Size of the user_priv data if used */ + test_cb* user_test; /* Optional user level wrapper function for the kernel test */ + char* file; + int line; + }; + + void *get_priv(KernelTest *kt, size_t priv_sz); + + // Set up connection to the kernel test driver: + // @handle_test contains the test framework's handling code for test assertions */ + bool setup(test_handler handle_test); + + void set_configurator(configurator c); + + // Parse command line args (call after gtest arg parsing) + char** parse_opts(int argc, char** argv); + + /* Query kernel for available tests in index order */ + stringvec& query_testsets(); + + stringvec get_testsets(); + std::string get_current_setname(); + stringvec get_test_names(); + + KernelTest* find_test(const std::string& setname, const std::string& testname, + std::string* ctx); + + /* "private" - only run from gtest framework */ + void run_test(KernelTest* test, std::string& ctx); +} // end namespace ktf + + +/* Redefine for C++ until we can get it patched - type mismatch by default */ +#ifdef nla_for_each_nested +#undef nla_for_each_nested +#endif +#define nla_for_each_nested(pos, nla, rem) \ + for (pos = (struct nlattr*)nla_data(nla), rem = nla_len(nla); \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +#endif
Currently ktf only supports integration with googletest on the user side, but there's nothing that prevents integration towards other user land frameworks for running and reporting, if so desired.
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/lib/ktf_run.cc | 177 ++++++++++++++++++- tools/testing/selftests/ktf/lib/ktf_unlproto.c | 21 ++- 2 files changed, 198 insertions(+) create mode 100644 tools/testing/selftests/ktf/lib/ktf_run.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_unlproto.c
diff --git a/tools/testing/selftests/ktf/lib/ktf_run.cc b/tools/testing/selftests/ktf/lib/ktf_run.cc new file mode 100644 index 0000000..a26e04c --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_run.cc @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_run.cpp: + * Gtest integration of ktf kernel tests - + * e.g. tests that are fully implemented on the test driver side + * and only initiated via run_test below + */ + +#include "ktf_int.h" +#include <assert.h> +#include <errno.h> +#include "ktf_debug.h" + +namespace ktf +{ + +class KernelMetaFactory; + +class Kernel : public ::testing::TestWithParamstd::string +{ +public: + Kernel() + { + assert(false); // Should not be hit but is needed for template resolving + } + + Kernel(std::string& setname, std::string& testname) + { + log(KTF_INFO, "%s.%s\n", setname.c_str(), testname.c_str()); + + ukt = ktf::find_test(setname,testname,&ctx); + if (!ukt) { + fprintf(stderr, "**** Internal error: Could not find test %s.%s (set %s, name %s) ****\n", + setname.c_str(), testname.c_str(), setname.c_str(), testname.c_str()); + exit(7); + } + log(KTF_INFO, "### Kernel ctor %s (%ld,%ld)\n", ukt->name.c_str(), ukt->setnum, ukt->testnum); + } + + virtual ~Kernel() + { + log(KTF_INFO, "### Kernel dtor %s\n", ukt->name.c_str()); + + /* For some reason errno sometimes get set + * TBD: Figure out why - for now just reset it to avoid confusing the next test! + */ + if (errno) { + log(KTF_INFO, "### %s: errno was set to %d - resetting..\n", ukt->name.c_str(), errno); + errno = 0; + } + } + + virtual void TestBody(); +private: + ktf::KernelTest* ukt; + std::string ctx; + friend void setup(configurator c); + static int AddToRegistry(); + static configurator configurator_; +}; + + + +class TFactory : public ::testing::internal::ParameterizedTestFactory<Kernel> +{ +public: + TFactory(std::string s, ParamType parameter) + : ::testing::internal::ParameterizedTestFactory<Kernel>(parameter), + setname(s) + { + testname = parameter.c_str(); + } + + virtual ::testing::Test* CreateTest() + { + return new Kernel(setname,testname); + } + +private: + std::string setname; + std::string testname; +}; + + +class KernelMetaFactory : public ::testing::internal::TestMetaFactory<Kernel> +{ +public: + virtual ::testing::internal::TestFactoryBase* CreateTestFactory(ParamType parameter) { + TFactory* tf; + std::string setname = get_current_setname(); + tf = new TFactory(setname, parameter.c_str()); + return tf; + } +}; + +testing::internal::ParamGeneratorKernel::ParamType gtest_query_tests(void); +std::string gtest_name_from_info(const testing::TestParamInfoKernel::ParamType&); +void gtest_handle_test(int result, const char* file, int line, const char* report); + +#ifndef INSTANTIATE_TEST_SUITE_P +/* This rename happens in Googletest commit 3a460a26b7. + * Make sure we compile both before and after it: + */ +#define AddTestSuiteInstantiation AddTestCaseInstantiation +#endif + +int Kernel::AddToRegistry() +{ + if (!ktf::setup(ktf::gtest_handle_test)) return 1; + + /* Run query against kernel to figure out which tests that exists: */ + stringvec& t = ktf::query_testsets(); + + ::testing::internal::ParameterizedTestCaseInfo<Kernel>* tci = + ::testing::UnitTest::GetInstance()->parameterized_test_registry() + .GetTestCasePatternHolder<Kernel>( "Kernel", ::testing::internal::CodeLocation("", 0)); + + for (stringvec::iterator it = t.begin(); it != t.end(); ++it) + { + ::testing::internal::TestMetaFactory<Kernel>* mf = new KernelMetaFactory(); + tci->AddTestPattern(it->c_str(), "", mf); + } + + tci->AddTestSuiteInstantiation("", >est_query_tests, >est_name_from_info, NULL, 0); + return 0; +} + +void setup(configurator c) +{ + ktf::set_configurator(c); + Kernel::AddToRegistry(); +} + + +void Kernel::TestBody() +{ + run_test(ukt, ctx); +} + + +void gtest_handle_test(int result, const char* file, int line, const char* report) +{ + if (result >= 0) { + const ::testing::AssertionResult gtest_ar = + !result ? (testing::AssertionFailure() << report) : testing::AssertionSuccess(); + + if (result) { + /* We might get multiple partial results from the kernel in one positive + * result report: + */ +#if HAVE_ASSERT_COUNT + ::testing::UnitTest::GetInstance()->increment_success_assert_count(result); +#else + GTEST_SUCCEED(); +#endif + } else { + ::testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure, + file, line, gtest_ar.failure_message()) = ::testing::Message(); + } + } +} + +testing::internal::ParamGeneratorKernel::ParamType gtest_query_tests() +{ + return testing::ValuesIn(ktf::get_test_names()); +} + +std::string gtest_name_from_info(const testing::TestParamInfoKernel::ParamType& info) +{ + return info.param; +} + +} // end namespace ktf diff --git a/tools/testing/selftests/ktf/lib/ktf_unlproto.c b/tools/testing/selftests/ktf/lib/ktf_unlproto.c new file mode 100644 index 0000000..3929b03 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_unlproto.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * unlproto.c: This file is needed because the C struct init + * used in kernel/unlproto.h is not allowed in C++ + */ + +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#define NL_INTERNAL 1 +#include "kernel/ktf_unlproto.h" + + +struct nla_policy *ktf_get_gnl_policy(void) +{ + return ktf_gnl_policy; +}
Utilities for convenient and runtime enabled/disabled printk debugging mainly intended for debugging ktf itself and subtle early issues with execution/running of tests.
ktf_debug.h: User mode debug function definitions
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/lib/ktf_debug.cc | 20 +++++++- tools/testing/selftests/ktf/lib/ktf_debug.h | 59 +++++++++++++++++++++- 2 files changed, 79 insertions(+) create mode 100644 tools/testing/selftests/ktf/lib/ktf_debug.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_debug.h
diff --git a/tools/testing/selftests/ktf/lib/ktf_debug.cc b/tools/testing/selftests/ktf/lib/ktf_debug.cc new file mode 100644 index 0000000..18ff443 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_debug.cc @@ -0,0 +1,20 @@ +/* Copyright (c) 2012 Oracle Corporation. All rights reserved + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include "ktf_debug.h" +#include <stdlib.h> + +unsigned long ktf_debug_mask = 0; + + +void ktf_debug_init() +{ + ktf_debug_mask = 0; + char* dbg_mask_str = getenv("KTF_DEBUG_MASK"); + if (dbg_mask_str) { + ktf_debug_mask = strtol(dbg_mask_str, NULL, 0); + log(KTF_INFO_V, "debug mask set to 0x%lx\n", ktf_debug_mask); + } +} diff --git a/tools/testing/selftests/ktf/lib/ktf_debug.h b/tools/testing/selftests/ktf/lib/ktf_debug.h new file mode 100644 index 0000000..dc761a4 --- /dev/null +++ b/tools/testing/selftests/ktf/lib/ktf_debug.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2012 Oracle Corporation. All rights reserved + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktf_debug.h: User mode debug function definitions + * - intended for test debugging. + * + * Enabled by setting bits in the environment variable KTF_DEBUG_MASK + */ + +#ifndef _KTF_DEBUG_H +#define _KTF_DEBUG_H +#include <time.h> +#include <stdio.h> +#include <sys/types.h> +#include <pthread.h> +#include <unistd.h> +#include <sys/syscall.h> + +extern unsigned long ktf_debug_mask; + + +#define KTF_ERR 0x1 +#define KTF_WARN 0x2 +#define KTF_INFO 0x4 +#define KTF_INFO_V 0x100 +#define KTF_MR 0x2000 +#define KTF_DEBUG 0x10000 +#define KTF_POLL 0x20000 +#define KTF_EVENT 0x40000 +#define KTF_DEBUG_V 0x1000000 +#define KTF_DUMP 0x2000000 + +/* Call this to initialize the debug logic from + * environment KTF_DEBUG_MASK + */ +void ktf_debug_init(); + +#define log(level, format, arg...) \ +do {\ + if (level & ktf_debug_mask) {\ + char _tm[30]; \ + time_t _tv = time(NULL);\ + ctime_r(&_tv,_tm);\ + _tm[24] = '\0';\ + fprintf(stderr, "%s [%ld] %s: " format, \ + _tm, (long unsigned int) pthread_self(), __func__, ## arg); \ + }\ +} while (0) + +#define logs(class, stmt_list) \ + do { \ + if (ktf_debug_mask & class) { \ + stmt_list; \ + } \ + } while (0) + +#endif
A few simple examples, and example of other test modules to make it easier to get started with ktf.
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/examples/Makefile | 17 ++++- tools/testing/selftests/ktf/examples/h2.c | 45 +++++++++++- tools/testing/selftests/ktf/examples/h3.c | 84 ++++++++++++++++++++- tools/testing/selftests/ktf/examples/h4.c | 62 +++++++++++++++- tools/testing/selftests/ktf/examples/hello.c | 38 +++++++++- tools/testing/selftests/ktf/examples/kgdemo.c | 61 +++++++++++++++- 6 files changed, 307 insertions(+) create mode 100644 tools/testing/selftests/ktf/examples/Makefile create mode 100644 tools/testing/selftests/ktf/examples/h2.c create mode 100644 tools/testing/selftests/ktf/examples/h3.c create mode 100644 tools/testing/selftests/ktf/examples/h4.c create mode 100644 tools/testing/selftests/ktf/examples/hello.c create mode 100644 tools/testing/selftests/ktf/examples/kgdemo.c
diff --git a/tools/testing/selftests/ktf/examples/Makefile b/tools/testing/selftests/ktf/examples/Makefile new file mode 100644 index 0000000..f3cfcc9 --- /dev/null +++ b/tools/testing/selftests/ktf/examples/Makefile @@ -0,0 +1,17 @@ +# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. +# +# SPDX-License-Identifier: GPL-2.0 +# +# Kernel module implementing a few simple examples of KTF tests +# + +include $(srctree)/$(src)/../scripts/ktf_syms.mk + +ccflags-y += -I$(srctree)/$(src)/../kernel -I$(src) + +obj-m := hello.o h2.o h3.o h4.o + +ifdef CONFIG_KGDB +obj-m += kgdemo.o +endif + diff --git a/tools/testing/selftests/ktf/examples/h2.c b/tools/testing/selftests/ktf/examples/h2.c new file mode 100644 index 0000000..37a6fbb --- /dev/null +++ b/tools/testing/selftests/ktf/examples/h2.c @@ -0,0 +1,45 @@ +#include <linux/module.h> +#include "ktf.h" + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +#define MAX_CNT 3 + +struct hello_ctx { + struct ktf_context k; + int value[MAX_CNT]; +}; + +static struct hello_ctx myctx = { .value = { 0, 1, 4 } }; + +TEST(examples, cmp) +{ + struct hello_ctx *hctx = KTF_CONTEXT_GET("value", struct hello_ctx); + + EXPECT_INT_EQ(_i, hctx->value[_i]); +} + +static void add_tests(void) +{ + ADD_LOOP_TEST(cmp, 0, MAX_CNT); +} + +static int __init hello_init(void) +{ + KTF_CONTEXT_ADD(&myctx.k, "value"); + add_tests(); + return 0; +} + +static void __exit hello_exit(void) +{ + struct ktf_context *kctx = KTF_CONTEXT_FIND("value"); + + KTF_CONTEXT_REMOVE(kctx); + KTF_CLEANUP(); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/tools/testing/selftests/ktf/examples/h3.c b/tools/testing/selftests/ktf/examples/h3.c new file mode 100644 index 0000000..a6aca98 --- /dev/null +++ b/tools/testing/selftests/ktf/examples/h3.c @@ -0,0 +1,84 @@ +#include <linux/module.h> +#include "ktf.h" + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +DECLARE_F(hello_fixture) + struct list_head head; +}; + +struct my_element { + struct list_head list; + int value; +}; + +SETUP_F(hello_fixture, hello_setup) +{ + int i; + + INIT_LIST_HEAD(&hello_fixture->head); + for (i = 0; i < 10; i++) { + struct my_element *e = kzalloc(sizeof(*e), GFP_KERNEL); + + e->value = i; + list_add_tail(&e->list, &hello_fixture->head); + } + hello_fixture->ok = true; +} + +TEARDOWN_F(hello_fixture, hello_teardown) +{ + struct list_head *p, *next_p; + + /* Just cleanup whatever is left after the test */ + list_for_each_safe(p, next_p, &hello_fixture->head) { + struct my_element *e = list_entry(p, struct my_element, list); + + list_del(&e->list); + kfree(e); + } + EXPECT_TRUE(list_empty(&hello_fixture->head)); +} + +INIT_F(hello_fixture, hello_setup, hello_teardown); + +TEST_F(hello_fixture, examples, hello_del) +{ + int cnt = 0; + int cnt_ones = 0; + struct my_element *e = kzalloc(sizeof(*e), GFP_KERNEL); + + e->value = 1; + list_add(&e->list, &ctx->head); + + list_for_each_entry(e, &ctx->head, list) { + if (e->value == 1) + cnt_ones++; + cnt++; + } + EXPECT_INT_EQ(11, cnt); + EXPECT_INT_EQ(2, cnt_ones); +} + +static void add_tests(void) +{ + ADD_TEST(hello_del); +} + +static int __init hello_init(void) +{ + add_tests(); + tlog(T_INFO, "hello: loaded"); + return 0; +} + +static void __exit hello_exit(void) +{ + KTF_CLEANUP(); + tlog(T_INFO, "hello: unloaded"); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/tools/testing/selftests/ktf/examples/h4.c b/tools/testing/selftests/ktf/examples/h4.c new file mode 100644 index 0000000..3e85fef --- /dev/null +++ b/tools/testing/selftests/ktf/examples/h4.c @@ -0,0 +1,62 @@ +#include <linux/module.h> +#include "ktf.h" + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +static int count; +static int ret; + +KTF_ENTRY_PROBE(printk, printkhandler) +{ + count++; + + KTF_ENTRY_PROBE_RETURN(0); +} + +TEST(examples, entrycheck) +{ + count = 0; + ASSERT_INT_EQ_GOTO(KTF_REGISTER_ENTRY_PROBE(printk, printkhandler), + 0, done); + printk(KERN_INFO "Testing kprobe entry..."); + ASSERT_INT_GT_GOTO(count, 0, done); +done: + KTF_UNREGISTER_ENTRY_PROBE(printk, printkhandler); +} + +KTF_RETURN_PROBE(printk, printkrethandler) +{ + ret = KTF_RETURN_VALUE(); + + return 0; +} + +TEST(examples, returncheck) +{ + char *teststr = "Testing kprobe return..."; + + ret = -1; + ASSERT_INT_EQ_GOTO(KTF_REGISTER_RETURN_PROBE(printk, printkrethandler), + 0, done); + printk(KERN_INFO "%s", teststr); + ASSERT_INT_EQ_GOTO(ret, strlen(teststr), done); +done: + KTF_UNREGISTER_RETURN_PROBE(printk, printkrethandler); +} + +static int __init hello_init(void) +{ + ADD_TEST(entrycheck); + ADD_TEST(returncheck); + return 0; +} + +static void __exit hello_exit(void) +{ + KTF_CLEANUP(); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/tools/testing/selftests/ktf/examples/hello.c b/tools/testing/selftests/ktf/examples/hello.c new file mode 100644 index 0000000..9c4713f --- /dev/null +++ b/tools/testing/selftests/ktf/examples/hello.c @@ -0,0 +1,38 @@ +#include <linux/module.h> +#include "ktf.h" + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +TEST(examples, hello_ok) +{ + EXPECT_TRUE(true); +} + +TEST(examples, hello_fail) +{ + EXPECT_TRUE(false); +} + +static void add_tests(void) +{ + ADD_TEST(hello_ok); + ADD_TEST(hello_fail); +} + +static int __init hello_init(void) +{ + add_tests(); + tlog(T_INFO, "hello: loaded"); + return 0; +} + +static void __exit hello_exit(void) +{ + KTF_CLEANUP(); + tlog(T_INFO, "hello: unloaded"); +} + +module_init(hello_init); +module_exit(hello_exit); diff --git a/tools/testing/selftests/ktf/examples/kgdemo.c b/tools/testing/selftests/ktf/examples/kgdemo.c new file mode 100644 index 0000000..9ce19ff --- /dev/null +++ b/tools/testing/selftests/ktf/examples/kgdemo.c @@ -0,0 +1,61 @@ +#include <linux/module.h> +#include "ktf.h" + +/* + * A trivial and somewhat rough example used by the author + * for pedagogical purposes, to demonstrate + * interactive debugging with kgdb. + * + * Requires a kernel built with CONFIG_KGDB + * + * Note: these test breaks into kgdb and/or creates a NULL + * pointer exception and corresponding stack dump, so + * try out in a test environment only! + */ + +MODULE_LICENSE("GPL"); + +KTF_INIT(); + +#define MAX_CNT 3 +#include <linux/kgdb.h> + +static int kgdemo_cnt; +static int *bogus_ref; + +TEST(kgdb, breakpoint) +{ + kgdemo_cnt = 0; + printk(KERN_INFO "** Please set kgdemo_cnt = 1 **\n"); + kgdb_breakpoint(); + EXPECT_INT_EQ(1, kgdemo_cnt); +} + +TEST(kgdb, nullpointer) +{ + int pre = kgdemo_cnt; + + int b = *bogus_ref++; + + EXPECT_INT_EQ(pre + 1, b); +} + +static void add_tests(void) +{ + ADD_TEST(breakpoint); + ADD_TEST(nullpointer); +} + +static int __init kgdemo_init(void) +{ + add_tests(); + return 0; +} + +static void __exit kgdemo_exit(void) +{ + KTF_CLEANUP(); +} + +module_init(kgdemo_init); +module_exit(kgdemo_exit);
Some minimal user land executables to run tests and to enable/disable coverage:
ktfrun: Simple generic test runner for generic ktf tests ktftest: A test runner for the ktf selftests. Contains code to configure specific selftest context objects. ktfcov: A utility to selectively enable coverage support for a kernel module. Coverage support can also be enabled in code by tests, if desired. hybrun: A test that implements a hybrid test runner.
hybrid.cc: User mode part of the hybrid_self ktfrun.cc: Generic user level application to run kernel tests
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/user/Makefile | 26 ++++++++- tools/testing/selftests/ktf/user/hybrid.cc | 39 +++++++++++++- tools/testing/selftests/ktf/user/ktfcov.cc | 68 ++++++++++++++++++++++- tools/testing/selftests/ktf/user/ktfrun.cc | 20 ++++++- tools/testing/selftests/ktf/user/ktftest.cc | 46 +++++++++++++++- 5 files changed, 199 insertions(+) create mode 100644 tools/testing/selftests/ktf/user/Makefile create mode 100644 tools/testing/selftests/ktf/user/hybrid.cc create mode 100644 tools/testing/selftests/ktf/user/ktfcov.cc create mode 100644 tools/testing/selftests/ktf/user/ktfrun.cc create mode 100644 tools/testing/selftests/ktf/user/ktftest.cc
diff --git a/tools/testing/selftests/ktf/user/Makefile b/tools/testing/selftests/ktf/user/Makefile new file mode 100644 index 0000000..04c8e7e --- /dev/null +++ b/tools/testing/selftests/ktf/user/Makefile @@ -0,0 +1,26 @@ + +GTEST_CFLAGS ?= -I$(GTEST_PATH)/include -DGTEST_HAS_PTHREAD=1 -lpthread +GTEST_LIBS ?= -L$(GTEST_PATH)/lib64 -lgtest -lpthread +NETLINK_CFLAGS ?= $(shell pkgconf --cflags libnl-genl-3.0) +HOST_EXTRACFLAGS = -I$(srctree)/$(src)/../lib $(NETLINK_CFLAGS) $(GTEST_CFLAGS) \ + -Wall -Werror \ + -Wno-packed-bitfield-compat -D_GNU_SOURCE + +HOST_EXTRACXXFLAGS = -I$(srctree)/$(src)/../lib $(NETLINK_CFLAGS) $(GTEST_CFLAGS) \ + -Wall \ + -Wno-packed-bitfield-compat \ + -Wno-pointer-arith -Werror \ + -D__FILENAME__="`basename $<`" +NETLINK_LIBS ?= $(shell pkgconf --libs libnl-genl-3.0) +KBUILD_HOSTLDLIBS = -L$(obj)/../lib -lktf $(NETLINK_LIBS) $(GTEST_LIBS) + +hostprogs-y := ktfrun ktfcov ktftest + +__build: $(addprefix $(obj)/,$(hostprogs-y)) + +## Simple kernel test runner sample program: +ktfrun-cxxobjs = ktfrun.o +ktfcov-cxxobjs = ktfcov.o + +## Configure and run the KTF selftests: +ktftest-cxxobjs = ktftest.o hybrid.o diff --git a/tools/testing/selftests/ktf/user/hybrid.cc b/tools/testing/selftests/ktf/user/hybrid.cc new file mode 100644 index 0000000..6aa5ad2 --- /dev/null +++ b/tools/testing/selftests/ktf/user/hybrid.cc @@ -0,0 +1,39 @@ +/* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * hybrid.cpp: User mode part of the hybrid_self + * test in selftests + */ + +#include "ktf.h" +#include <string.h> + +extern "C" { +#include "../selftest/hybrid_self.h" +} + +/* User side of a simple hybrid test that just sends an out-of-band message + * to the kernel side - the kernel implementation picks it up and verifies + * that it is the expected string and integer values. + * + * This form of test allows the mixing of normal gtest user land assertions + * with one or more calls to the kernel side to run tests there: + */ + +HTEST(selftest, msg) +{ + KTF_USERDATA(self, hybrid_self_params, data); + + strcpy(data->text_val, HYBRID_MSG); + data->val = HYBRID_MSG_VAL; + + /* assertions can be specified here: */ + EXPECT_TRUE(true); + + ktf::run(self); + + /* and here.. */ + EXPECT_TRUE(true); +} diff --git a/tools/testing/selftests/ktf/user/ktfcov.cc b/tools/testing/selftests/ktf/user/ktfcov.cc new file mode 100644 index 0000000..d5a9ef4 --- /dev/null +++ b/tools/testing/selftests/ktf/user/ktfcov.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Alan Maguire alan.maguire@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktfcov.cpp: + * User level application to enable/disable coverage of kernel modules. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "ktf.h" +#include "../kernel/ktf_unlproto.h" + +using namespace std; + +void +usage(char *progname) +{ + cerr << "Usage: " << progname << " [-e module[-m]] [-d module]\n"; +} + +int main (int argc, char** argv) +{ + int opt, nopts = 0; + unsigned int cov_opts = 0; + std::string modname = std::string(); + bool enable = false; + + ktf::setup(); + testing::InitGoogleTest(&argc,argv); + + if (argc < 3) { + usage(argv[0]); + return -1; + } + + while ((opt = getopt(argc, argv, "e:d:m")) != -1) { + switch (opt) { + case 'e': + nopts++; + enable = true; + modname = optarg; + break; + case 'd': + nopts++; + enable = false; + modname = optarg; + break; + case 'm': + cov_opts |= KTF_COV_OPT_MEM; + break; + default: + cerr << "Unknown option '" << char(optopt) << "'"; + return -1; + } + } + /* Either enable or disable must be specified, and -m is only valid + * for enable. + */ + if (modname.size() == 0 || nopts != 1 || (cov_opts && !enable)) { + usage(argv[0]); + return -1; + } + return ktf::set_coverage(modname, cov_opts, enable); +} diff --git a/tools/testing/selftests/ktf/user/ktfrun.cc b/tools/testing/selftests/ktf/user/ktfrun.cc new file mode 100644 index 0000000..9229b21 --- /dev/null +++ b/tools/testing/selftests/ktf/user/ktfrun.cc @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktfrun.cpp: Generic user level application to run kernel tests + * provided by modules subscribing to ktf services. + */ +#include <stdio.h> +#include <stdlib.h> +#include <ktf.h> + +int main (int argc, char** argv) +{ + ktf::setup(); + testing::InitGoogleTest(&argc,argv); + + return RUN_ALL_TESTS(); +} diff --git a/tools/testing/selftests/ktf/user/ktftest.cc b/tools/testing/selftests/ktf/user/ktftest.cc new file mode 100644 index 0000000..fda625d --- /dev/null +++ b/tools/testing/selftests/ktf/user/ktftest.cc @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang knut.omang@oracle.com + * + * SPDX-License-Identifier: GPL-2.0 + * + * ktfrun.cpp: Generic user level application to run kernel tests + * provided by modules subscribing to ktf services. + */ +#include <stdio.h> +#include <stdlib.h> +#include <ktf.h> + +extern "C" { +#include "../selftest/context_self.h" +} + +void selftest_configure() +{ + struct test_parameter_block p; + memset(&p, 0, sizeof(p)); + strcpy(p.s, CONTEXT_MSG); + + /* First configure two contexts provided by the kernel part: */ + p.magic = CONTEXT_MAGIC1; + KTF_CONTEXT_CFG("context1", "context_type_1", test_parameter_block, &p); + p.magic = CONTEXT_MAGIC2; + KTF_CONTEXT_CFG("context2", "context_type_2", test_parameter_block, &p); + + /* Configure a 3rd, dynamically created context, using CONTEXT3_TYPE_ID + * which the kernel part has enabled for dynamic creation of contexts + * from user space (see kernel/context.c: add_context_tests() + * for details of setup) + */ + p.magic = CONTEXT_MAGIC3; + KTF_CONTEXT_CFG("context3", "context_type_3", test_parameter_block, &p); +} + + +int main (int argc, char** argv) +{ + ktf::setup(selftest_configure); + testing::InitGoogleTest(&argc,argv); + + return RUN_ALL_TESTS(); +}
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/ktf/Makefile | 21 ++++- tools/testing/selftests/ktf/scripts/ktf_syms.mk | 16 +++- tools/testing/selftests/ktf/scripts/runtests.mk | 3 +- tools/testing/selftests/ktf/scripts/runtests.sh | 100 +++++++++++++++++- tools/testing/selftests/ktf/scripts/top_make.mk | 14 ++- 5 files changed, 154 insertions(+) create mode 100644 tools/testing/selftests/ktf/Makefile create mode 100644 tools/testing/selftests/ktf/scripts/ktf_syms.mk create mode 100644 tools/testing/selftests/ktf/scripts/runtests.mk create mode 100755 tools/testing/selftests/ktf/scripts/runtests.sh create mode 100644 tools/testing/selftests/ktf/scripts/top_make.mk
diff --git a/tools/testing/selftests/ktf/Makefile b/tools/testing/selftests/ktf/Makefile new file mode 100644 index 0000000..0fef39c --- /dev/null +++ b/tools/testing/selftests/ktf/Makefile @@ -0,0 +1,21 @@ + +ifneq ($(TARGETS),) +# We end up here if called from selftests/Makefile +# Invoke our "module target" to get everything built +all: + $(Q)$(MAKE) -C $(abs_objtree) M=tools/testing/selftests/ktf + +clean: + $(Q)$(MAKE) -C $(abs_objtree) M=tools/testing/selftests/ktf clean + +run_tests: + @echo "running tests" + $(MAKE) BUILD=$(abs_objtree)/tools/testing/selftests -f scripts/runtests.mk $@ + +endif +obj-m += kernel/ +obj-m += selftest/ +obj-m += examples/ +obj-m += lib/ +obj-m += user/ + diff --git a/tools/testing/selftests/ktf/scripts/ktf_syms.mk b/tools/testing/selftests/ktf/scripts/ktf_syms.mk new file mode 100644 index 0000000..a332223 --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/ktf_syms.mk @@ -0,0 +1,16 @@ +ktf_symfile=$(shell (cd $(srctree)/$(src) && ls ktf_syms.txt 2> /dev/null || true)) +ktf_syms = $(ktf_symfile:%.txt=%.h) + +ifneq ($(ktf_symfile),) + +$(obj)/self.o: $(obj)/$(ktf_syms) + +ktf_scripts = $(srctree)/$(src)/../scripts + +$(obj)/$(ktf_syms): $(srctree)/$(src)/ktf_syms.txt $(ktf_scripts)/resolve + @echo " KTFSYMS $@" + $(Q)$(ktf_scripts)/resolve $(ccflags-y) $< $@ + +clean-files += $(ktf_syms) + +endif diff --git a/tools/testing/selftests/ktf/scripts/runtests.mk b/tools/testing/selftests/ktf/scripts/runtests.mk new file mode 100644 index 0000000..7fd3651 --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/runtests.mk @@ -0,0 +1,3 @@ +TEST_PROGS := scripts/runtests.sh + +include ../lib.mk diff --git a/tools/testing/selftests/ktf/scripts/runtests.sh b/tools/testing/selftests/ktf/scripts/runtests.sh new file mode 100755 index 0000000..3396aec --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/runtests.sh @@ -0,0 +1,100 @@ +#!/bin/bash -e + +verbose=1 + +# Convenience function to return a string that +# is a reverse list of the incoming arguments: +# +reverse() +{ + args=($*) + for (( i=((${#args[*]} - 1)); i >= 0; i-- )); do + echo ${args[$i]} + done +} + +# Set paths to a particular module - if no path is set to a module, use modprobe: +# +declare -A a_mpath +mpath() +{ + local module="$1" + local mpath="$2" + [[ $mpath != "" ]] || fail "Usage: mpath module path" + + a_mpath[$module]="$BUILD/$mpath" +} + +# Set parameters to load a given module with for test purposes: +declare -A a_params +params() +{ + local module="$1" + shift + a_params[$module]="$*" +} + +log() +{ + (( $verbose )) && echo $* +} + +mod_probe() +{ + local fm="" + local name="$1" + shift + + mp=${a_mpath[$name]} + if [[ $mp != "" ]]; then + fm="$mp" + fi + + is_loaded=$(lsmod | egrep "^$name" || true) + if [[ $is_loaded != "" ]]; then + echo "Module "$name" is already loaded!" 1>&2 + return 0 + fi + + if [[ $fm == "" ]]; then + log "Modprobing $name" + $sudo modprobe $name ${a_params[$name]} + else + fm=${a_mpath[$name]} + log "Insmod'ing module "$name"" 1>&2 + $sudo insmod $fm ${a_params[$name]} + fi +} + +# If/when more modules are to be loaded, this could go in a config file +# but for the purpose of this example, just do it inline: +# +mpath ktf ktf/kernel/ktf.ko +mpath selftest ktf/selftest/selftest.ko + +load_modules="ktf selftest" + +unload_modules=$(reverse $load_modules) + +sudo="" +if [[ $USER != "root" ]]; then + sudo="sudo" +fi + +for m in $load_modules; do + mod_probe $m +done + +if [[ $GTEST_PATH == "" ]];then + echo "Set environment variable GTEST_PATH to point to your googletest build!" + exit 1 +fi + +export LD_LIBRARY_PATH="$BUILD/ktf/lib:$GTEST_PATH/lib64:$GTEST_PATH/lib" +$BUILD/ktf/user/ktftest || stat=$? + +for m in $unload_modules; do + $sudo rmmod $m +done + +exit $stat diff --git a/tools/testing/selftests/ktf/scripts/top_make.mk b/tools/testing/selftests/ktf/scripts/top_make.mk new file mode 100644 index 0000000..978068e --- /dev/null +++ b/tools/testing/selftests/ktf/scripts/top_make.mk @@ -0,0 +1,14 @@ +ifneq ($(TARGETS),) +# We end up here if called from selftests/Makefile +# Invoke our "module target" to get everything built +all: + $(Q)$(MAKE) -C $(abs_objtree) M=tools/testing/selftests/ktf + +clean: + $(Q)$(MAKE) -C $(abs_objtree) M=tools/testing/selftests/ktf clean + +run_tests: + @echo "running tests" + $(MAKE) BUILD=$(abs_objtree)/tools/testing/selftests -f scripts/runtests.mk $@ + +endif
Signed-off-by: Knut Omang knut.omang@oracle.com --- tools/testing/selftests/Makefile | 1 + 1 file changed, 1 insertion(+)
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 9781ca7..a24c2fe 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -20,6 +20,7 @@ TARGETS += ipc TARGETS += ir TARGETS += kcmp TARGETS += kexec +TARGETS += ktf TARGETS += kvm TARGETS += lib TARGETS += livepatch
Signed-off-by: Knut Omang knut.omang@oracle.com --- Documentation/dev-tools/index.rst | 1 + 1 file changed, 1 insertion(+)
diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst index b0522a4..f155205 100644 --- a/Documentation/dev-tools/index.rst +++ b/Documentation/dev-tools/index.rst @@ -24,6 +24,7 @@ whole; patches welcome! gdb-kernel-debugging kgdb kselftest + ktf/index
.. only:: subproject and html
On Mon, Aug 12, 2019 at 11:11 PM Knut Omang knut.omang@oracle.com wrote:
KTF has already been available for a while as a separate git repository with means to facilitate use with any kernel version.
KTF can be used both for "pure" unit testing and for more pragmatic approaches to component testing. Apart from some useful features that KTF uses from the kernel toolbox (such as kallsyms, kprobes), KTF does not depend on any special environment such as UML or on a large set of mocked up APIs to be useful. KTF basically allows test code to be inserted and managed as separate kernel modules, while providing the tests convenient access to almost the full range of kernel APIs, both exposed and private. And once a KTF test set exists, it should be fairly easy to compile and run it against older versions of the kernel as well.
This series proposes a non-intrusive integration of KTF into the kernel hopefully presented in digestable pieces. For convenience, the patch set is also available on top of v5.2 at https://github.com/knuto/linux/pull/new/ktf_v1 .
The high level structure of the KTF code is as follows:
External dependencies for the user land side:
- libnl3 for netlink communication
- googletest for test runner, reporting and test selection support.
So I take it that KTF depends on a fully booted kernel with an up and running userspace?
Kernel components:
- Simple core test and test suite related abstractions: core data structures ktf_case, ktf_test, an assertion macro "infrastructure" with ASSERT_* and EXPECT_* macros and helper functions
- Bookkeeping data structures:
- ktf_map - a (key, value) mapping container used to implement management of instances of the higher level abstraction needs, such as ktf_handle and ktf_context.
- ktf_handle: A global environment that a test runs within.
- ktf_context: a test suite specific abstraction to allow a test to execute within one or more contexts. An example use of context can be a device instance: A test can be instantiated to run on all available such devices, according to test suite defined criteria.
- A generic netlink protocol (ktf_nl, ktf_unlproto) which defines operations to allow a user space part of a test to query the kernel for test information, invoke tests and get feedback reports about results.
- An alternative debugfs interface to allow examining and executing kernel-only tests without a user level program.
- Support for overriding and modifying behaviour of kernel calls.
User mode components:
- A test executor based on and integrated with Googletest. Googletest is one of several mature user land unit test suites for C/C++. The choice allowed us to focus on kernel specific functionality rather than having to reinvent too many wheels.
- Tools to aid in creating new test modules (suites): To facilitate a developer friendly way of testing internals of a module or the kernel itself, one of the important features of KTF, we often need to access symbols deliberately not exposed from a module. KTF contains a script used to create definitions based on kallsyms lookup for easy access to symbols not exposed by a module or the kernel. The user just provides a simple text file with a list of the symbols by module.
This series is an attempt to address feedback from several people that having the functionality readily available within the kernel repository is desired.
An in-tree KTF allows test suites to be provided with the kernel, which makes it easy to use KTF based suites as part of the kernel selftests and driver test suites. Having the ability to still build and run the latest versions of test suites against older kernels should be of great value to stable maintainers, distributions and driver maintainers, who would want to have an easy path, with minimal backporting efforts to make sure that criterias implemented by new test logic is also valid for these kernels.
Our definite goal moving forward is to try to satisfy both needs in a transparent way. The plan is to let the standalone KTF repository follow the in-kernel one, and to allow test suites to be maintained similarly, and to support maintenance by proper tooling.
Mode of integration into the kernel
One feature of KTF is that it allows tests to work, look and feel similar whether they execute entirely in user mode, entirely in kernel mode, or half and half (hybrid tests). KTF consist of both user space and kernel code. Unlike e.g. kselftest, KTF in the Github version does not attempt to address the test runner aspects of testing.
Due to the need for building modules, KTF requires access to kernel module build facilities (obj-m). But KTF also has nontrivial needs for user land building, and we think it is good to keep the build structure in a way that allows KTF to be built both in-tree and out-of-tree without necessarily having to reconfigure the kernel.
This first version of kernel integration of KTF solves this challenge by co-locating everything associated with KTF under ktf/ as in the github version, but use the little used hostprogs-y and hostlibs-y features to build the user space side. The first patch in the series is fixes to make it work in a natural way to suit our needs.
Positioning for natural building within the kernel tree
Currently we find significant amount of C level tests within the following paths:
tools/testing/selftests/ (kselftests, almost entirely user space so far) lib/ (various kernel level mostly unit tests)
and in the making::
kunit/ (kernel only (UML))
So all kernel code are currently located directly within the kernel build paths, accessed from the top level Makefile, to allow everything to be controlled by config and from the main build targets for the kernel. But this also poses challenges, in that .config has to be modified to build tests. And once a .config is changed, we no longer in principle logically operate on the same kernel.
A better approach seems to be to follow the principle taken by kselftest: To have all the logic associated with the test inside the test tree, and make it available for building separately from the kernel itself. This require us to have a means to build kernel modules from within the test tree, separately from the main kernel paths. Currently this seems to only by supported via the M= option used to build out-of-tree modules. This was also easy to get to work for the kernel parts, based on the Github version of KTF, where we already do this. With the additional need to compile user land code, using the corresponding hostprogs-y and hostNNlibs-y seemed natural, but this has been challenging: The build macros does not really support hostprogs-y etc as "first class citizens" so some amount of hacking is in there in this first draft version. Using hostprogs-y etc is also a different approach that what is used for C code in kselftest today, but we imagine that there's room for unification here to get the best of both worlds, with the help of the wider Kbuild community.
As an initial proposal, we have positioned ktf as an additional kselftest target, under tools/testing/selftests/ktf, and the recommended:
make TARGETS="ktf" kselftest
way of building and running should work, even from normal user accounts, if the user running it has sudo rights (for the kernel module insertion and removal). This will run the selftests for KTF itself, and should be a good starting point for adding more test cases. We also have had activities going to take some of the existing test suites under lib/ and convert them into KTF based test suites, and will get back to this later.
A trimmed down output from the above make target would look like this:
... CC [M] tools/testing/selftests/ktf/kernel/ktf_override.o LD [M] tools/testing/selftests/ktf/kernel/ktf.o HOSTCC -fPIC tools/testing/selftests/ktf/lib/ktf_unlproto.o HOSTCXX -fPIC tools/testing/selftests/ktf/lib/ktf_int.o KTFSYMS tools/testing/selftests/ktf/selftest/ktf_syms.h CC [M] tools/testing/selftests/ktf/selftest/self.o LD [M] tools/testing/selftests/ktf/selftest/selftest.o HOSTCXX tools/testing/selftests/ktf/user/ktftest.o HOSTCXX tools/testing/selftests/ktf/user/hybrid.o HOSTLD tools/testing/selftests/ktf/user/ktftest Building modules, stage 2. MODPOST 7 modules LD [M] tools/testing/selftests/ktf/kernel/ktf.ko LD [M] tools/testing/selftests/ktf/selftest/selftest.ko running tests make BUILD=/net/abi/local/abi/build/kernel/ktf/tools/testing/selftests -f scripts/runtests.mk run_tests TAP version 13 1..1 ... ok 1 selftests: ktf: runtests.sh
We're looking forward to feedback on this, and also to more discussion around unit testing at the testing & fuzzing workshop at LPC!
Sounds good! Glad to have this on the lists!
Alan Maguire (3): ktf: Implementation of ktf support for overriding function entry and return. ktf: A simple debugfs interface to test results ktf: Simple coverage support
Knut Omang (16): kbuild: Fixes to rules for host-cshlib and host-cxxshlib ktf: Introduce the main part of the kernel side of ktf ktf: Introduce a generic netlink protocol for test result communication ktf: An implementation of a generic associative array container ktf: Configurable context support for network info setup ktf: resolve: A helper utility to aid in exposing private kernel symbols to KTF tests. ktf: Add documentation for Kernel Test Framework (KTF) ktf: Add a small test suite with a few tests to test KTF itself ktf: Main part of user land library for executing tests ktf: Integration logic for running ktf tests from googletest ktf: Internal debugging facilities ktf: Some simple examples ktf: Some user applications to run tests ktf: Toplevel ktf Makefile/makefile includes and scripts to run from kselftest kselftests: Enable building ktf Documentation/dev-tools: Add index entry for KTF documentation
Documentation/dev-tools/index.rst | 1 +- Documentation/dev-tools/ktf/concepts.rst | 242 +++- Documentation/dev-tools/ktf/debugging.rst | 248 +++- Documentation/dev-tools/ktf/examples.rst | 26 +- Documentation/dev-tools/ktf/features.rst | 307 ++++- Documentation/dev-tools/ktf/implementation.rst | 70 +- Documentation/dev-tools/ktf/index.rst | 14 +- Documentation/dev-tools/ktf/installation.rst | 73 +- Documentation/dev-tools/ktf/introduction.rst | 134 ++- Documentation/dev-tools/ktf/progref.rst | 144 ++- scripts/Makefile.host | 17 +- tools/testing/selftests/Makefile | 1 +- tools/testing/selftests/ktf/Makefile | 21 +- tools/testing/selftests/ktf/examples/Makefile | 17 +- tools/testing/selftests/ktf/examples/h2.c | 45 +- tools/testing/selftests/ktf/examples/h3.c | 84 +- tools/testing/selftests/ktf/examples/h4.c | 62 +- tools/testing/selftests/ktf/examples/hello.c | 38 +- tools/testing/selftests/ktf/examples/kgdemo.c | 61 +- tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 +++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++- tools/testing/selftests/ktf/kernel/ktf_cov.c | 690 ++++++++- tools/testing/selftests/ktf/kernel/ktf_cov.h | 94 +- tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 +- tools/testing/selftests/ktf/kernel/ktf_map.c | 261 +++- tools/testing/selftests/ktf/kernel/ktf_map.h | 154 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.c | 132 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.h | 64 +- tools/testing/selftests/ktf/kernel/ktf_nl.c | 516 ++++++- tools/testing/selftests/ktf/kernel/ktf_nl.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_override.c | 45 +- tools/testing/selftests/ktf/kernel/ktf_override.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++- tools/testing/selftests/ktf/kernel/ktf_unlproto.h | 105 +- tools/testing/selftests/ktf/lib/Makefile | 21 +- tools/testing/selftests/ktf/lib/ktf.h | 114 +- tools/testing/selftests/ktf/lib/ktf_debug.cc | 20 +- tools/testing/selftests/ktf/lib/ktf_debug.h | 59 +- tools/testing/selftests/ktf/lib/ktf_int.cc | 1031 ++++++++++++- tools/testing/selftests/ktf/lib/ktf_int.h | 84 +- tools/testing/selftests/ktf/lib/ktf_run.cc | 177 ++- tools/testing/selftests/ktf/lib/ktf_unlproto.c | 21 +- tools/testing/selftests/ktf/scripts/ktf_syms.mk | 16 +- tools/testing/selftests/ktf/scripts/resolve | 188 ++- tools/testing/selftests/ktf/scripts/runtests.mk | 3 +- tools/testing/selftests/ktf/scripts/runtests.sh | 100 +- tools/testing/selftests/ktf/scripts/top_make.mk | 14 +- tools/testing/selftests/ktf/selftest/Makefile | 17 +- tools/testing/selftests/ktf/selftest/context.c | 149 ++- tools/testing/selftests/ktf/selftest/context.h | 15 +- tools/testing/selftests/ktf/selftest/context_self.h | 34 +- tools/testing/selftests/ktf/selftest/hybrid.c | 35 +- tools/testing/selftests/ktf/selftest/hybrid.h | 24 +- tools/testing/selftests/ktf/selftest/hybrid_self.h | 27 +- tools/testing/selftests/ktf/selftest/ktf_syms.txt | 17 +- tools/testing/selftests/ktf/selftest/self.c | 661 ++++++++- tools/testing/selftests/ktf/user/Makefile | 26 +- tools/testing/selftests/ktf/user/hybrid.cc | 39 +- tools/testing/selftests/ktf/user/ktfcov.cc | 68 +- tools/testing/selftests/ktf/user/ktfrun.cc | 20 +- tools/testing/selftests/ktf/user/ktftest.cc | 46 +- 64 files changed, 8909 insertions(+), 9 deletions(-) create mode 100644 Documentation/dev-tools/ktf/concepts.rst create mode 100644 Documentation/dev-tools/ktf/debugging.rst create mode 100644 Documentation/dev-tools/ktf/examples.rst create mode 100644 Documentation/dev-tools/ktf/features.rst create mode 100644 Documentation/dev-tools/ktf/implementation.rst create mode 100644 Documentation/dev-tools/ktf/index.rst create mode 100644 Documentation/dev-tools/ktf/installation.rst create mode 100644 Documentation/dev-tools/ktf/introduction.rst create mode 100644 Documentation/dev-tools/ktf/progref.rst create mode 100644 tools/testing/selftests/ktf/Makefile create mode 100644 tools/testing/selftests/ktf/examples/Makefile create mode 100644 tools/testing/selftests/ktf/examples/h2.c create mode 100644 tools/testing/selftests/ktf/examples/h3.c create mode 100644 tools/testing/selftests/ktf/examples/h4.c create mode 100644 tools/testing/selftests/ktf/examples/hello.c create mode 100644 tools/testing/selftests/ktf/examples/kgdemo.c create mode 100644 tools/testing/selftests/ktf/kernel/Makefile create mode 100644 tools/testing/selftests/ktf/kernel/ktf.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_context.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_cov.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_cov.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_debugfs.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_map.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_map.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_netctx.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_netctx.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_nl.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_nl.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_override.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_override.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.c create mode 100644 tools/testing/selftests/ktf/kernel/ktf_test.h create mode 100644 tools/testing/selftests/ktf/kernel/ktf_unlproto.h create mode 100644 tools/testing/selftests/ktf/lib/Makefile create mode 100644 tools/testing/selftests/ktf/lib/ktf.h create mode 100644 tools/testing/selftests/ktf/lib/ktf_debug.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_debug.h create mode 100644 tools/testing/selftests/ktf/lib/ktf_int.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_int.h create mode 100644 tools/testing/selftests/ktf/lib/ktf_run.cc create mode 100644 tools/testing/selftests/ktf/lib/ktf_unlproto.c create mode 100644 tools/testing/selftests/ktf/scripts/ktf_syms.mk create mode 100755 tools/testing/selftests/ktf/scripts/resolve create mode 100644 tools/testing/selftests/ktf/scripts/runtests.mk create mode 100755 tools/testing/selftests/ktf/scripts/runtests.sh create mode 100644 tools/testing/selftests/ktf/scripts/top_make.mk create mode 100644 tools/testing/selftests/ktf/selftest/Makefile create mode 100644 tools/testing/selftests/ktf/selftest/context.c create mode 100644 tools/testing/selftests/ktf/selftest/context.h create mode 100644 tools/testing/selftests/ktf/selftest/context_self.h create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.c create mode 100644 tools/testing/selftests/ktf/selftest/hybrid.h create mode 100644 tools/testing/selftests/ktf/selftest/hybrid_self.h create mode 100644 tools/testing/selftests/ktf/selftest/ktf_syms.txt create mode 100644 tools/testing/selftests/ktf/selftest/self.c create mode 100644 tools/testing/selftests/ktf/user/Makefile create mode 100644 tools/testing/selftests/ktf/user/hybrid.cc create mode 100644 tools/testing/selftests/ktf/user/ktfcov.cc create mode 100644 tools/testing/selftests/ktf/user/ktfrun.cc create mode 100644 tools/testing/selftests/ktf/user/ktftest.cc
base-commit: 0ecfebd2b52404ae0c54a878c872bb93363ada36
git-series 0.9.1
On Mon, Aug 12, 2019 at 11:11 PM Knut Omang knut.omang@oracle.com wrote: [...]
Alan Maguire (3): ktf: Implementation of ktf support for overriding function entry and return. ktf: A simple debugfs interface to test results ktf: Simple coverage support
Knut Omang (16): kbuild: Fixes to rules for host-cshlib and host-cxxshlib ktf: Introduce the main part of the kernel side of ktf ktf: Introduce a generic netlink protocol for test result communication ktf: An implementation of a generic associative array container ktf: Configurable context support for network info setup ktf: resolve: A helper utility to aid in exposing private kernel symbols to KTF tests. ktf: Add documentation for Kernel Test Framework (KTF) ktf: Add a small test suite with a few tests to test KTF itself ktf: Main part of user land library for executing tests ktf: Integration logic for running ktf tests from googletest ktf: Internal debugging facilities ktf: Some simple examples ktf: Some user applications to run tests ktf: Toplevel ktf Makefile/makefile includes and scripts to run from kselftest kselftests: Enable building ktf Documentation/dev-tools: Add index entry for KTF documentation
Documentation/dev-tools/index.rst | 1 +- Documentation/dev-tools/ktf/concepts.rst | 242 +++- Documentation/dev-tools/ktf/debugging.rst | 248 +++- Documentation/dev-tools/ktf/examples.rst | 26 +- Documentation/dev-tools/ktf/features.rst | 307 ++++- Documentation/dev-tools/ktf/implementation.rst | 70 +- Documentation/dev-tools/ktf/index.rst | 14 +- Documentation/dev-tools/ktf/installation.rst | 73 +- Documentation/dev-tools/ktf/introduction.rst | 134 ++- Documentation/dev-tools/ktf/progref.rst | 144 ++- scripts/Makefile.host | 17 +- tools/testing/selftests/Makefile | 1 +- tools/testing/selftests/ktf/Makefile | 21 +- tools/testing/selftests/ktf/examples/Makefile | 17 +- tools/testing/selftests/ktf/examples/h2.c | 45 +- tools/testing/selftests/ktf/examples/h3.c | 84 +- tools/testing/selftests/ktf/examples/h4.c | 62 +- tools/testing/selftests/ktf/examples/hello.c | 38 +- tools/testing/selftests/ktf/examples/kgdemo.c | 61 +- tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 +++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++- tools/testing/selftests/ktf/kernel/ktf_cov.c | 690 ++++++++- tools/testing/selftests/ktf/kernel/ktf_cov.h | 94 +- tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 +- tools/testing/selftests/ktf/kernel/ktf_map.c | 261 +++- tools/testing/selftests/ktf/kernel/ktf_map.h | 154 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.c | 132 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.h | 64 +- tools/testing/selftests/ktf/kernel/ktf_nl.c | 516 ++++++- tools/testing/selftests/ktf/kernel/ktf_nl.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_override.c | 45 +- tools/testing/selftests/ktf/kernel/ktf_override.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++- tools/testing/selftests/ktf/kernel/ktf_unlproto.h | 105 +- tools/testing/selftests/ktf/lib/Makefile | 21 +- tools/testing/selftests/ktf/lib/ktf.h | 114 +- tools/testing/selftests/ktf/lib/ktf_debug.cc | 20 +- tools/testing/selftests/ktf/lib/ktf_debug.h | 59 +- tools/testing/selftests/ktf/lib/ktf_int.cc | 1031 ++++++++++++- tools/testing/selftests/ktf/lib/ktf_int.h | 84 +- tools/testing/selftests/ktf/lib/ktf_run.cc | 177 ++- tools/testing/selftests/ktf/lib/ktf_unlproto.c | 21 +- tools/testing/selftests/ktf/scripts/ktf_syms.mk | 16 +- tools/testing/selftests/ktf/scripts/resolve | 188 ++- tools/testing/selftests/ktf/scripts/runtests.mk | 3 +- tools/testing/selftests/ktf/scripts/runtests.sh | 100 +- tools/testing/selftests/ktf/scripts/top_make.mk | 14 +- tools/testing/selftests/ktf/selftest/Makefile | 17 +- tools/testing/selftests/ktf/selftest/context.c | 149 ++- tools/testing/selftests/ktf/selftest/context.h | 15 +- tools/testing/selftests/ktf/selftest/context_self.h | 34 +- tools/testing/selftests/ktf/selftest/hybrid.c | 35 +- tools/testing/selftests/ktf/selftest/hybrid.h | 24 +- tools/testing/selftests/ktf/selftest/hybrid_self.h | 27 +- tools/testing/selftests/ktf/selftest/ktf_syms.txt | 17 +- tools/testing/selftests/ktf/selftest/self.c | 661 ++++++++- tools/testing/selftests/ktf/user/Makefile | 26 +- tools/testing/selftests/ktf/user/hybrid.cc | 39 +- tools/testing/selftests/ktf/user/ktfcov.cc | 68 +- tools/testing/selftests/ktf/user/ktfrun.cc | 20 +- tools/testing/selftests/ktf/user/ktftest.cc | 46 +- 64 files changed, 8909 insertions(+), 9 deletions(-)
It also looks like all your test code lives outside of the kernel source dir. I take it you intend for tests to live in tools/testing/selftests/ktf/ ?
[...]
On Tue, 2019-08-13 at 01:17 -0700, Brendan Higgins wrote:
On Mon, Aug 12, 2019 at 11:11 PM Knut Omang knut.omang@oracle.com wrote: [...]
Alan Maguire (3): ktf: Implementation of ktf support for overriding function entry and return. ktf: A simple debugfs interface to test results ktf: Simple coverage support
Knut Omang (16): kbuild: Fixes to rules for host-cshlib and host-cxxshlib ktf: Introduce the main part of the kernel side of ktf ktf: Introduce a generic netlink protocol for test result communication ktf: An implementation of a generic associative array container ktf: Configurable context support for network info setup ktf: resolve: A helper utility to aid in exposing private kernel symbols to KTF tests. ktf: Add documentation for Kernel Test Framework (KTF) ktf: Add a small test suite with a few tests to test KTF itself ktf: Main part of user land library for executing tests ktf: Integration logic for running ktf tests from googletest ktf: Internal debugging facilities ktf: Some simple examples ktf: Some user applications to run tests ktf: Toplevel ktf Makefile/makefile includes and scripts to run from kselftest kselftests: Enable building ktf Documentation/dev-tools: Add index entry for KTF documentation
Documentation/dev-tools/index.rst | 1 +- Documentation/dev-tools/ktf/concepts.rst | 242 +++- Documentation/dev-tools/ktf/debugging.rst | 248 +++- Documentation/dev-tools/ktf/examples.rst | 26 +- Documentation/dev-tools/ktf/features.rst | 307 ++++- Documentation/dev-tools/ktf/implementation.rst | 70 +- Documentation/dev-tools/ktf/index.rst | 14 +- Documentation/dev-tools/ktf/installation.rst | 73 +- Documentation/dev-tools/ktf/introduction.rst | 134 ++- Documentation/dev-tools/ktf/progref.rst | 144 ++- scripts/Makefile.host | 17 +- tools/testing/selftests/Makefile | 1 +- tools/testing/selftests/ktf/Makefile | 21 +- tools/testing/selftests/ktf/examples/Makefile | 17 +- tools/testing/selftests/ktf/examples/h2.c | 45 +- tools/testing/selftests/ktf/examples/h3.c | 84 +- tools/testing/selftests/ktf/examples/h4.c | 62 +- tools/testing/selftests/ktf/examples/hello.c | 38 +- tools/testing/selftests/ktf/examples/kgdemo.c | 61 +- tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 +++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++- tools/testing/selftests/ktf/kernel/ktf_cov.c | 690 ++++++++- tools/testing/selftests/ktf/kernel/ktf_cov.h | 94 +- tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 +- tools/testing/selftests/ktf/kernel/ktf_map.c | 261 +++- tools/testing/selftests/ktf/kernel/ktf_map.h | 154 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.c | 132 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.h | 64 +- tools/testing/selftests/ktf/kernel/ktf_nl.c | 516 ++++++- tools/testing/selftests/ktf/kernel/ktf_nl.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_override.c | 45 +- tools/testing/selftests/ktf/kernel/ktf_override.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++- tools/testing/selftests/ktf/kernel/ktf_unlproto.h | 105 +- tools/testing/selftests/ktf/lib/Makefile | 21 +- tools/testing/selftests/ktf/lib/ktf.h | 114 +- tools/testing/selftests/ktf/lib/ktf_debug.cc | 20 +- tools/testing/selftests/ktf/lib/ktf_debug.h | 59 +- tools/testing/selftests/ktf/lib/ktf_int.cc | 1031 ++++++++++++- tools/testing/selftests/ktf/lib/ktf_int.h | 84 +- tools/testing/selftests/ktf/lib/ktf_run.cc | 177 ++- tools/testing/selftests/ktf/lib/ktf_unlproto.c | 21 +- tools/testing/selftests/ktf/scripts/ktf_syms.mk | 16 +- tools/testing/selftests/ktf/scripts/resolve | 188 ++- tools/testing/selftests/ktf/scripts/runtests.mk | 3 +- tools/testing/selftests/ktf/scripts/runtests.sh | 100 +- tools/testing/selftests/ktf/scripts/top_make.mk | 14 +- tools/testing/selftests/ktf/selftest/Makefile | 17 +- tools/testing/selftests/ktf/selftest/context.c | 149 ++- tools/testing/selftests/ktf/selftest/context.h | 15 +- tools/testing/selftests/ktf/selftest/context_self.h | 34 +- tools/testing/selftests/ktf/selftest/hybrid.c | 35 +- tools/testing/selftests/ktf/selftest/hybrid.h | 24 +- tools/testing/selftests/ktf/selftest/hybrid_self.h | 27 +- tools/testing/selftests/ktf/selftest/ktf_syms.txt | 17 +- tools/testing/selftests/ktf/selftest/self.c | 661 ++++++++- tools/testing/selftests/ktf/user/Makefile | 26 +- tools/testing/selftests/ktf/user/hybrid.cc | 39 +- tools/testing/selftests/ktf/user/ktfcov.cc | 68 +- tools/testing/selftests/ktf/user/ktfrun.cc | 20 +- tools/testing/selftests/ktf/user/ktftest.cc | 46 +- 64 files changed, 8909 insertions(+), 9 deletions(-)
It also looks like all your test code lives outside of the kernel source dir. I take it you intend for tests to live in tools/testing/selftests/ktf/ ?
Good point, with this RFC it would be just to add another directory under ktf/ but this was just to find a simple way to integrate it below selftests, without interfering with the current structure.
I imagine a tighter integration/unification between normal Kbuild targets and selftests targets that also took kernel module building into consideration would be a better solution.
I think this is a good topic for discussion. As I indicate above, I think it is problematic that test code has to be explicitly configured in as we configure code features of the kernel, which changes the "logical" kernel we build.
So some more "native" support for test modules are desired, IMHO.
Knut
[...]
On Tue, Aug 13, 2019 at 4:29 AM Knut Omang knut.omang@oracle.com wrote:
On Tue, 2019-08-13 at 01:17 -0700, Brendan Higgins wrote:
On Mon, Aug 12, 2019 at 11:11 PM Knut Omang knut.omang@oracle.com wrote: [...]
Alan Maguire (3): ktf: Implementation of ktf support for overriding function entry and return. ktf: A simple debugfs interface to test results ktf: Simple coverage support
Knut Omang (16): kbuild: Fixes to rules for host-cshlib and host-cxxshlib ktf: Introduce the main part of the kernel side of ktf ktf: Introduce a generic netlink protocol for test result communication ktf: An implementation of a generic associative array container ktf: Configurable context support for network info setup ktf: resolve: A helper utility to aid in exposing private kernel symbols to KTF tests. ktf: Add documentation for Kernel Test Framework (KTF) ktf: Add a small test suite with a few tests to test KTF itself ktf: Main part of user land library for executing tests ktf: Integration logic for running ktf tests from googletest ktf: Internal debugging facilities ktf: Some simple examples ktf: Some user applications to run tests ktf: Toplevel ktf Makefile/makefile includes and scripts to run from kselftest kselftests: Enable building ktf Documentation/dev-tools: Add index entry for KTF documentation
Documentation/dev-tools/index.rst | 1 +- Documentation/dev-tools/ktf/concepts.rst | 242 +++- Documentation/dev-tools/ktf/debugging.rst | 248 +++- Documentation/dev-tools/ktf/examples.rst | 26 +- Documentation/dev-tools/ktf/features.rst | 307 ++++- Documentation/dev-tools/ktf/implementation.rst | 70 +- Documentation/dev-tools/ktf/index.rst | 14 +- Documentation/dev-tools/ktf/installation.rst | 73 +- Documentation/dev-tools/ktf/introduction.rst | 134 ++- Documentation/dev-tools/ktf/progref.rst | 144 ++- scripts/Makefile.host | 17 +- tools/testing/selftests/Makefile | 1 +- tools/testing/selftests/ktf/Makefile | 21 +- tools/testing/selftests/ktf/examples/Makefile | 17 +- tools/testing/selftests/ktf/examples/h2.c | 45 +- tools/testing/selftests/ktf/examples/h3.c | 84 +- tools/testing/selftests/ktf/examples/h4.c | 62 +- tools/testing/selftests/ktf/examples/hello.c | 38 +- tools/testing/selftests/ktf/examples/kgdemo.c | 61 +- tools/testing/selftests/ktf/kernel/Makefile | 15 +- tools/testing/selftests/ktf/kernel/ktf.h | 604 +++++++- tools/testing/selftests/ktf/kernel/ktf_context.c | 409 +++++- tools/testing/selftests/ktf/kernel/ktf_cov.c | 690 ++++++++- tools/testing/selftests/ktf/kernel/ktf_cov.h | 94 +- tools/testing/selftests/ktf/kernel/ktf_debugfs.c | 356 ++++- tools/testing/selftests/ktf/kernel/ktf_debugfs.h | 34 +- tools/testing/selftests/ktf/kernel/ktf_map.c | 261 +++- tools/testing/selftests/ktf/kernel/ktf_map.h | 154 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.c | 132 ++- tools/testing/selftests/ktf/kernel/ktf_netctx.h | 64 +- tools/testing/selftests/ktf/kernel/ktf_nl.c | 516 ++++++- tools/testing/selftests/ktf/kernel/ktf_nl.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_override.c | 45 +- tools/testing/selftests/ktf/kernel/ktf_override.h | 15 +- tools/testing/selftests/ktf/kernel/ktf_test.c | 397 +++++- tools/testing/selftests/ktf/kernel/ktf_test.h | 381 ++++- tools/testing/selftests/ktf/kernel/ktf_unlproto.h | 105 +- tools/testing/selftests/ktf/lib/Makefile | 21 +- tools/testing/selftests/ktf/lib/ktf.h | 114 +- tools/testing/selftests/ktf/lib/ktf_debug.cc | 20 +- tools/testing/selftests/ktf/lib/ktf_debug.h | 59 +- tools/testing/selftests/ktf/lib/ktf_int.cc | 1031 ++++++++++++- tools/testing/selftests/ktf/lib/ktf_int.h | 84 +- tools/testing/selftests/ktf/lib/ktf_run.cc | 177 ++- tools/testing/selftests/ktf/lib/ktf_unlproto.c | 21 +- tools/testing/selftests/ktf/scripts/ktf_syms.mk | 16 +- tools/testing/selftests/ktf/scripts/resolve | 188 ++- tools/testing/selftests/ktf/scripts/runtests.mk | 3 +- tools/testing/selftests/ktf/scripts/runtests.sh | 100 +- tools/testing/selftests/ktf/scripts/top_make.mk | 14 +- tools/testing/selftests/ktf/selftest/Makefile | 17 +- tools/testing/selftests/ktf/selftest/context.c | 149 ++- tools/testing/selftests/ktf/selftest/context.h | 15 +- tools/testing/selftests/ktf/selftest/context_self.h | 34 +- tools/testing/selftests/ktf/selftest/hybrid.c | 35 +- tools/testing/selftests/ktf/selftest/hybrid.h | 24 +- tools/testing/selftests/ktf/selftest/hybrid_self.h | 27 +- tools/testing/selftests/ktf/selftest/ktf_syms.txt | 17 +- tools/testing/selftests/ktf/selftest/self.c | 661 ++++++++- tools/testing/selftests/ktf/user/Makefile | 26 +- tools/testing/selftests/ktf/user/hybrid.cc | 39 +- tools/testing/selftests/ktf/user/ktfcov.cc | 68 +- tools/testing/selftests/ktf/user/ktfrun.cc | 20 +- tools/testing/selftests/ktf/user/ktftest.cc | 46 +- 64 files changed, 8909 insertions(+), 9 deletions(-)
It also looks like all your test code lives outside of the kernel source dir. I take it you intend for tests to live in tools/testing/selftests/ktf/ ?
Good point, with this RFC it would be just to add another directory under ktf/ but this was just to find a simple way to integrate it below selftests, without interfering with the current structure.
I imagine a tighter integration/unification between normal Kbuild targets and selftests targets that also took kernel module building into consideration would be a better solution.
I think tests should live alongside the code that they test, so if I understand what you are saying, then I agree. Where do you think KTF should go then?
I think this is a good topic for discussion. As I indicate above, I think it is problematic that test code has to be explicitly configured in as we configure code features of the kernel, which changes the "logical" kernel we build.
Yep, Luis Chamberlain and I have been discussing this for a while (I should probably try to open up that conversation and add you to it); it's a very tricky problem. I think that in the long term a good goal is to have a way to express code dependencies separate from the configuration system, but that would probably mean some substantial changes to Kbuild and friends. Even then, we have all these macros that generate different code (not just on/off) depending on how the kernel is configured.
So some more "native" support for test modules are desired, IMHO.
What do you mean by "native"? Just having the tests be more aware of the code?
On Tue, Aug 13, 2019 at 08:09:15AM +0200, Knut Omang wrote:
and in the making::
kunit/ (kernel only (UML))
You are going to have to integrate this with kunit, to come up with a superset of both in the end.
And I do not think that kunit is only UML, it's just that seems to be what Brendan tests with, but should work with other arches as well.
thanks,
greg k-h
On Tue, 2019-08-13 at 10:23 +0200, Greg Kroah-Hartman wrote:
On Tue, Aug 13, 2019 at 08:09:15AM +0200, Knut Omang wrote:
and in the making::
kunit/ (kernel only (UML))
You are going to have to integrate this with kunit, to come up with a superset of both in the end.
Yes, I agree - getting to a unified approach has been my intention since I first brought this up at LPC'17.
And I do not think that kunit is only UML, it's just that seems to be what Brendan tests with, but should work with other arches as well.
If I get Brendan right, it is UML only now but can be extended to also support kernels running on real hardware. Still it is kernel only, while KTF also has the hybrid mode, where a test can have code and assertions both in user mode and kernel mode. This is made easier and more streamlined by letting all reporting happen from user mode.
Thanks! Knut
thanks,
greg k-h
On Tue, Aug 13, 2019 at 2:51 AM Knut Omang knut.omang@oracle.com wrote:
On Tue, 2019-08-13 at 10:23 +0200, Greg Kroah-Hartman wrote:
On Tue, Aug 13, 2019 at 08:09:15AM +0200, Knut Omang wrote:
and in the making::
kunit/ (kernel only (UML))
You are going to have to integrate this with kunit, to come up with a superset of both in the end.
Yes, I agree - getting to a unified approach has been my intention since I first brought this up at LPC'17.
And I do not think that kunit is only UML, it's just that seems to be what Brendan tests with, but should work with other arches as well.
If I get Brendan right, it is UML only now but can be extended to also support kernels running on real hardware. Still it is kernel only, while KTF also has the hybrid mode, where a test can have code and assertions both in user mode and kernel mode. This is made easier and more streamlined by letting all reporting happen from user mode.
Nope, the KUnit patchset currently under review *does* support any architecture; we have tested it on x86, ARM, and UML, but it should work on any architecture.
I added support for that a while ago due to popular demand.
linux-kselftest-mirror@lists.linaro.org