summaryrefslogtreecommitdiff
path: root/tools/testing/kunit/kunit_kernel.py
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-08-02 19:34:45 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-08-02 19:34:45 -0700
commit665fe72a7d4f0ad17923e0a5ff2e6cc64d57c970 (patch)
tree2579d083de9a7835af46a5fc4b5e5b24c14fab0a /tools/testing/kunit/kunit_kernel.py
parentaad26f55f47a33d6de3df65f0b18e2886059ed6d (diff)
parent4c392516accfe51a1aaf80ed163517646f8f0476 (diff)
Merge tag 'linux-kselftest-kunit-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
Pull KUnit updates from Shuah Khan: "This consists of several fixes and an important feature to discourage running KUnit tests on production systems. Running tests on a production system could leave the system in a bad state. Summary: - Add a new taint type, TAINT_TEST to signal that a test has been run. This should discourage people from running these tests on production systems, and to make it easier to tell if tests have been run accidentally (by loading the wrong configuration, etc) - Several documentation and tool enhancements and fixes" * tag 'linux-kselftest-kunit-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: (29 commits) Documentation: KUnit: Fix example with compilation error Documentation: kunit: Add CLI args for kunit_tool kcsan: test: Add a .kunitconfig to run KCSAN tests kunit: executor: Fix a memory leak on failure in kunit_filter_tests clk: explicitly disable CONFIG_UML_PCI_OVER_VIRTIO in .kunitconfig mmc: sdhci-of-aspeed: test: Use kunit_test_suite() macro nitro_enclaves: test: Use kunit_test_suite() macro thunderbolt: test: Use kunit_test_suite() macro kunit: flatten kunit_suite*** to kunit_suite** in .kunit_test_suites kunit: unify module and builtin suite definitions selftest: Taint kernel when test module loaded module: panic: Taint the kernel when selftest modules load Documentation: kunit: fix example run_kunit func to allow spaces in args Documentation: kunit: Cleanup run_wrapper, fix x-ref kunit: test.h: fix a kernel-doc markup kunit: tool: Enable virtio/PCI by default on UML kunit: tool: make --kunitconfig repeatable, blindly concat kunit: add coverage_uml.config to enable GCOV on UML kunit: tool: refactor internal kconfig handling, allow overriding kunit: tool: introduce --qemu_args ...
Diffstat (limited to 'tools/testing/kunit/kunit_kernel.py')
-rw-r--r--tools/testing/kunit/kunit_kernel.py104
1 files changed, 63 insertions, 41 deletions
diff --git a/tools/testing/kunit/kunit_kernel.py b/tools/testing/kunit/kunit_kernel.py
index 3539efaf99ba..f5c26ea89714 100644
--- a/tools/testing/kunit/kunit_kernel.py
+++ b/tools/testing/kunit/kunit_kernel.py
@@ -18,7 +18,7 @@ import threading
from typing import Iterator, List, Optional, Tuple
import kunit_config
-import kunit_parser
+from kunit_printer import stdout
import qemu_config
KCONFIG_PATH = '.config'
@@ -26,6 +26,7 @@ KUNITCONFIG_PATH = '.kunitconfig'
OLD_KUNITCONFIG_PATH = 'last_used_kunitconfig'
DEFAULT_KUNITCONFIG_PATH = 'tools/testing/kunit/configs/default.config'
BROKEN_ALLCONFIG_PATH = 'tools/testing/kunit/configs/broken_on_uml.config'
+UML_KCONFIG_PATH = 'tools/testing/kunit/configs/arch_uml.config'
OUTFILE_PATH = 'test.log'
ABS_TOOL_PATH = os.path.abspath(os.path.dirname(__file__))
QEMU_CONFIGS_DIR = os.path.join(ABS_TOOL_PATH, 'qemu_configs')
@@ -53,8 +54,8 @@ class LinuxSourceTreeOperations:
except subprocess.CalledProcessError as e:
raise ConfigError(e.output.decode())
- def make_arch_qemuconfig(self, base_kunitconfig: kunit_config.Kconfig) -> None:
- pass
+ def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig:
+ return base_kunitconfig
def make_allyesconfig(self, build_dir: str, make_options) -> None:
raise ConfigError('Only the "um" arch is supported for alltests')
@@ -109,9 +110,10 @@ class LinuxSourceTreeOperationsQemu(LinuxSourceTreeOperations):
self._kernel_command_line = qemu_arch_params.kernel_command_line + ' kunit_shutdown=reboot'
self._extra_qemu_params = qemu_arch_params.extra_qemu_params
- def make_arch_qemuconfig(self, base_kunitconfig: kunit_config.Kconfig) -> None:
+ def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig:
kconfig = kunit_config.parse_from_string(self._kconfig)
- base_kunitconfig.merge_in_entries(kconfig)
+ kconfig.merge_in_entries(base_kunitconfig)
+ return kconfig
def start(self, params: List[str], build_dir: str) -> subprocess.Popen:
kernel_path = os.path.join(build_dir, self._kernel_path)
@@ -137,8 +139,13 @@ class LinuxSourceTreeOperationsUml(LinuxSourceTreeOperations):
def __init__(self, cross_compile=None):
super().__init__(linux_arch='um', cross_compile=cross_compile)
+ def make_arch_config(self, base_kunitconfig: kunit_config.Kconfig) -> kunit_config.Kconfig:
+ kconfig = kunit_config.parse_file(UML_KCONFIG_PATH)
+ kconfig.merge_in_entries(base_kunitconfig)
+ return kconfig
+
def make_allyesconfig(self, build_dir: str, make_options) -> None:
- kunit_parser.print_with_timestamp(
+ stdout.print_with_timestamp(
'Enabling all CONFIGs for UML...')
command = ['make', 'ARCH=um', 'O=' + build_dir, 'allyesconfig']
if make_options:
@@ -148,18 +155,19 @@ class LinuxSourceTreeOperationsUml(LinuxSourceTreeOperations):
stdout=subprocess.DEVNULL,
stderr=subprocess.STDOUT)
process.wait()
- kunit_parser.print_with_timestamp(
+ stdout.print_with_timestamp(
'Disabling broken configs to run KUnit tests...')
with open(get_kconfig_path(build_dir), 'a') as config:
with open(BROKEN_ALLCONFIG_PATH, 'r') as disable:
config.write(disable.read())
- kunit_parser.print_with_timestamp(
+ stdout.print_with_timestamp(
'Starting Kernel with all configs takes a few minutes...')
def start(self, params: List[str], build_dir: str) -> subprocess.Popen:
"""Runs the Linux UML binary. Must be named 'linux'."""
linux_bin = os.path.join(build_dir, 'linux')
+ params.extend(['mem=1G', 'console=tty', 'kunit_shutdown=halt'])
return subprocess.Popen([linux_bin] + params,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
@@ -175,22 +183,44 @@ def get_kunitconfig_path(build_dir: str) -> str:
def get_old_kunitconfig_path(build_dir: str) -> str:
return os.path.join(build_dir, OLD_KUNITCONFIG_PATH)
+def get_parsed_kunitconfig(build_dir: str,
+ kunitconfig_paths: Optional[List[str]]=None) -> kunit_config.Kconfig:
+ if not kunitconfig_paths:
+ path = get_kunitconfig_path(build_dir)
+ if not os.path.exists(path):
+ shutil.copyfile(DEFAULT_KUNITCONFIG_PATH, path)
+ return kunit_config.parse_file(path)
+
+ merged = kunit_config.Kconfig()
+
+ for path in kunitconfig_paths:
+ if os.path.isdir(path):
+ path = os.path.join(path, KUNITCONFIG_PATH)
+ if not os.path.exists(path):
+ raise ConfigError(f'Specified kunitconfig ({path}) does not exist')
+
+ partial = kunit_config.parse_file(path)
+ diff = merged.conflicting_options(partial)
+ if diff:
+ diff_str = '\n\n'.join(f'{a}\n vs from {path}\n{b}' for a, b in diff)
+ raise ConfigError(f'Multiple values specified for {len(diff)} options in kunitconfig:\n{diff_str}')
+ merged.merge_in_entries(partial)
+ return merged
+
def get_outfile_path(build_dir: str) -> str:
return os.path.join(build_dir, OUTFILE_PATH)
-def get_source_tree_ops(arch: str, cross_compile: Optional[str]) -> LinuxSourceTreeOperations:
+def _default_qemu_config_path(arch: str) -> str:
config_path = os.path.join(QEMU_CONFIGS_DIR, arch + '.py')
- if arch == 'um':
- return LinuxSourceTreeOperationsUml(cross_compile=cross_compile)
if os.path.isfile(config_path):
- return get_source_tree_ops_from_qemu_config(config_path, cross_compile)[1]
+ return config_path
options = [f[:-3] for f in os.listdir(QEMU_CONFIGS_DIR) if f.endswith('.py')]
raise ConfigError(arch + ' is not a valid arch, options are ' + str(sorted(options)))
-def get_source_tree_ops_from_qemu_config(config_path: str,
- cross_compile: Optional[str]) -> Tuple[
- str, LinuxSourceTreeOperations]:
+def _get_qemu_ops(config_path: str,
+ extra_qemu_args: Optional[List[str]],
+ cross_compile: Optional[str]) -> Tuple[str, LinuxSourceTreeOperations]:
# The module name/path has very little to do with where the actual file
# exists (I learned this through experimentation and could not find it
# anywhere in the Python documentation).
@@ -210,6 +240,8 @@ def get_source_tree_ops_from_qemu_config(config_path: str,
if not hasattr(config, 'QEMU_ARCH'):
raise ValueError('qemu_config module missing "QEMU_ARCH": ' + config_path)
params: qemu_config.QemuArchParams = config.QEMU_ARCH # type: ignore
+ if extra_qemu_args:
+ params.extra_qemu_params.extend(extra_qemu_args)
return params.linux_arch, LinuxSourceTreeOperationsQemu(
params, cross_compile=cross_compile)
@@ -219,34 +251,24 @@ class LinuxSourceTree:
def __init__(
self,
build_dir: str,
- load_config=True,
- kunitconfig_path='',
+ kunitconfig_paths: Optional[List[str]]=None,
kconfig_add: Optional[List[str]]=None,
arch=None,
cross_compile=None,
- qemu_config_path=None) -> None:
+ qemu_config_path=None,
+ extra_qemu_args=None) -> None:
signal.signal(signal.SIGINT, self.signal_handler)
if qemu_config_path:
- self._arch, self._ops = get_source_tree_ops_from_qemu_config(
- qemu_config_path, cross_compile)
+ self._arch, self._ops = _get_qemu_ops(qemu_config_path, extra_qemu_args, cross_compile)
else:
self._arch = 'um' if arch is None else arch
- self._ops = get_source_tree_ops(self._arch, cross_compile)
-
- if not load_config:
- return
+ if self._arch == 'um':
+ self._ops = LinuxSourceTreeOperationsUml(cross_compile=cross_compile)
+ else:
+ qemu_config_path = _default_qemu_config_path(self._arch)
+ _, self._ops = _get_qemu_ops(qemu_config_path, extra_qemu_args, cross_compile)
- if kunitconfig_path:
- if os.path.isdir(kunitconfig_path):
- kunitconfig_path = os.path.join(kunitconfig_path, KUNITCONFIG_PATH)
- if not os.path.exists(kunitconfig_path):
- raise ConfigError(f'Specified kunitconfig ({kunitconfig_path}) does not exist')
- else:
- kunitconfig_path = get_kunitconfig_path(build_dir)
- if not os.path.exists(kunitconfig_path):
- shutil.copyfile(DEFAULT_KUNITCONFIG_PATH, kunitconfig_path)
-
- self._kconfig = kunit_config.parse_file(kunitconfig_path)
+ self._kconfig = get_parsed_kunitconfig(build_dir, kunitconfig_paths)
if kconfig_add:
kconfig = kunit_config.parse_from_string('\n'.join(kconfig_add))
self._kconfig.merge_in_entries(kconfig)
@@ -267,10 +289,10 @@ class LinuxSourceTree:
validated_kconfig = kunit_config.parse_file(kconfig_path)
if self._kconfig.is_subset_of(validated_kconfig):
return True
- invalid = self._kconfig.entries() - validated_kconfig.entries()
+ missing = set(self._kconfig.as_entries()) - set(validated_kconfig.as_entries())
message = 'Not all Kconfig options selected in kunitconfig were in the generated .config.\n' \
'This is probably due to unsatisfied dependencies.\n' \
- 'Missing: ' + ', '.join([str(e) for e in invalid])
+ 'Missing: ' + ', '.join(str(e) for e in missing)
if self._arch == 'um':
message += '\nNote: many Kconfig options aren\'t available on UML. You can try running ' \
'on a different architecture with something like "--arch=x86_64".'
@@ -282,7 +304,7 @@ class LinuxSourceTree:
if build_dir and not os.path.exists(build_dir):
os.mkdir(build_dir)
try:
- self._ops.make_arch_qemuconfig(self._kconfig)
+ self._kconfig = self._ops.make_arch_config(self._kconfig)
self._kconfig.write_to_file(kconfig_path)
self._ops.make_olddefconfig(build_dir, make_options)
except ConfigError as e:
@@ -303,7 +325,7 @@ class LinuxSourceTree:
return True
old_kconfig = kunit_config.parse_file(old_path)
- return old_kconfig.entries() != self._kconfig.entries()
+ return old_kconfig != self._kconfig
def build_reconfig(self, build_dir: str, make_options) -> bool:
"""Creates a new .config if it is not a subset of the .kunitconfig."""
@@ -313,7 +335,8 @@ class LinuxSourceTree:
return self.build_config(build_dir, make_options)
existing_kconfig = kunit_config.parse_file(kconfig_path)
- self._ops.make_arch_qemuconfig(self._kconfig)
+ self._kconfig = self._ops.make_arch_config(self._kconfig)
+
if self._kconfig.is_subset_of(existing_kconfig) and not self._kunitconfig_changed(build_dir):
return True
print('Regenerating .config ...')
@@ -334,7 +357,6 @@ class LinuxSourceTree:
def run_kernel(self, args=None, build_dir='', filter_glob='', timeout=None) -> Iterator[str]:
if not args:
args = []
- args.extend(['mem=1G', 'console=tty', 'kunit_shutdown=halt'])
if filter_glob:
args.append('kunit.filter_glob='+filter_glob)