From c64e96e575969ea75eb8974497dd4551f0971ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Tue, 22 Jan 2019 00:00:00 +0000 Subject: [PATCH] test-userland: rename to test-userland-full-system and port to LkmcCliFunction --- README.adoc | 46 +++++++++++++++++++++++--------- common.py | 53 ++++++++++++++++++++++++++++++------- copy-overlay | 1 - rootfs_overlay/gem5_exit.sh | 4 +++ run | 10 +++++++ test | 2 +- test-baremetal | 3 --- test-boot | 8 +----- test-gdb | 6 +---- test-user-mode | 3 --- test-userland | 29 -------------------- test-userland-full-system | 22 +++++++++++++++ trace-boot | 11 +++----- 13 files changed, 119 insertions(+), 79 deletions(-) create mode 100755 rootfs_overlay/gem5_exit.sh delete mode 100755 test-userland create mode 100755 test-userland-full-system diff --git a/README.adoc b/README.adoc index 83de6dc..890b9bc 100644 --- a/README.adoc +++ b/README.adoc @@ -2467,7 +2467,7 @@ asdf=qwer Source: link:userland/init_env_poweroff.c[]. -==== init environment args +==== init arguments The annoying dash `-` gets passed as a parameter to `init`, which makes it impossible to use this method for most non custom executables. @@ -2488,6 +2488,8 @@ ab so see how `a.b` is gone. +The simple workaround is to just create a shell script that does it, e.g. as we've done at: link:rootfs_overlay/gem5_exit.sh[]. + ==== init environment env Wait, where do `HOME` and `TERM` come from? (greps the kernel). Ah, OK, the kernel sets those by default: https://github.com/torvalds/linux/blob/94710cac0ef4ee177a63b5227664b38c95bbf703/init/main.c#L173 @@ -11886,11 +11888,16 @@ This directory is copied into the target filesystem by: .... ./copy-overlay +./build-buildroot .... -Source link:copy-overlay[] +Source: link:copy-overlay[] -Since this directory does not require compilation, we also make it <<9p>> available to the guest directly even without `copy-overlay` at: +`copy-overlay` by itself, only places the files into our intermediate `./getenv out_rootfs_overlay_dir` directory. + +This directory combines files from several sources, including for example link:build-userland[], which the final `./build-buildroot` puts into the root filesystem. + +Since the link:rootfs_overlay[] directory does not require compilation, unlike say link:userland[] we also make it <<9p>> available to the guest directly even without `copy-overlay` at: .... ls /mnt/9p/rootfs_overlay @@ -11900,10 +11907,6 @@ This way you can just hack away the scripts and try them out immediately without To add those scripts to the Buildroot root filesystem, you will need to run: -.... -./build-buildroot -.... - We could add that directory to `BR2_ROOTFS_OVERLAY` but we don't do this because this mechanism: * also works for non Buildroot root filesystesms @@ -11915,7 +11918,7 @@ and maintaining `BR2_ROOTFS_OVERLAY` in addition to our mechanism would duplicat ==== Automated tests -Run absolutely all tests: +Run almost all tests: .... ./build-test --size 3 && \ @@ -11929,15 +11932,20 @@ Sources: * link:build-test[] * link:test[] -* link:test-userland[] * <> * <> +This is not all tests, because there are too many possible variations and that would take forever. + +Instead, we currently select on magic arch, currently `aarch64`, and for that arch run more stuff than on others. + +We could in the future we will add an option to select the large arch, or do something smarter. + This full testing takes too much time to be feasible for every patch, but it should be done for every release. See the sources of those test scripts to learn how to run more specialized tests. -One important tip is that you can select multiple archs and emulators of interest with a command such as: +One important tip is that you can select multiple archs and emulators of interest with any command as in: .... ./test-user-mode \ @@ -11958,7 +11966,21 @@ Test that the Internet works: ./run --arch x86_64 --kernel-cli '- lkmc_eval="ifup -a;wget -S google.com;poweroff;"' .... -Source: link:rootfs_overlay/test_all.sh[]. +===== Test userland in full system + +Run all userland tests from inside full system simulation (i.e. not <>): + +.... +./test-userland-full-system +.... + +This includes, in particular, userland programs that test the kernel modules, which cannot be tested in user mode simulation. + +Basically just boots and runs: link:rootfs_overlay/test_all.sh[] + +Failure is detected by looking for the <> + +Most userland programs that don't rely on kernel modules can also be tested in user mode simulation as explained at: <>. ===== Test GDB @@ -12037,7 +12059,7 @@ to the terminal, then our run scripts detect that and exit with status `1`. This magic output string is notably used by: * the `common_assert_fail()` function, which is used by <> -* link:rootfs_overlay/test_fail.sh[], which is used by the link:test-userland[] +* link:rootfs_overlay/test_fail.sh[], which is used by <> === Bisection diff --git a/common.py b/common.py index 33ef6c2..d1b30c7 100644 --- a/common.py +++ b/common.py @@ -439,11 +439,15 @@ Valid emulators: {} env['machine'] = 'VExpress_GEM5_V1' else: if not env['_args_given']['machine']: - # highmem=off needed since v3.0.0 due to: - # http://lists.nongnu.org/archive/html/qemu-discuss/2018-08/msg00034.html - env['machine'] = 'virt,highmem=off' - if env['arch'] == 'aarch64': - env['machine'] += ',gic_version=3' + env['machine'] = 'virt' + if env['arch'] == 'arm': + # highmem=off needed since v3.0.0 due to: + # http://lists.nongnu.org/archive/html/qemu-discuss/2018-08/msg00034.html + env['machine2'] = 'highmem=off' + elif env['arch'] == 'aarch64': + env['machine2'] = 'gic_version=3' + else: + env['machine2'] = None # Buildroot env['buildroot_build_dir'] = join(env['buildroot_out_dir'], 'build', env['buildroot_build_id'], env['arch']) @@ -556,7 +560,7 @@ Valid emulators: {} env['trace_txt_file'] = env['qemu_trace_txt_file'] env['run_cmd_file'] = join(env['run_dir'], 'run.sh') - # Linux kernl. + # Linux kernel. if not env['_args_given']['linux_build_dir']: env['linux_build_dir'] = join(env['out_dir'], 'linux', env['linux_build_id'], env['arch']) env['lkmc_vmlinux'] = join(env['linux_build_dir'], 'vmlinux') @@ -577,6 +581,11 @@ Valid emulators: {} else: env['vmlinux'] = env['lkmc_vmlinux'] env['linux_image'] = env['lkmc_linux_image'] + if env['emulator']== 'gem5': + env['userland_quit_cmd'] = '/gem5_exit.sh' + else: + env['userland_quit_cmd'] = '/poweroff.out' + env['quit_init'] = 'init={}'.format(env['userland_quit_cmd']) # Kernel modules. env['kernel_modules_build_dir'] = join(env['kernel_modules_build_base_dir'], env['arch']) @@ -1043,7 +1052,19 @@ class Test: return ' '.join(out) class TestCliFunction(LkmcCliFunction): + ''' + Represents a CLI command that runs tests. + + Automates test reporting boilerplate for those commands. + ''' + def __init__(self, *args, **kwargs): + defaults = { + 'print_time': False, + } + if 'defaults' in kwargs: + defaults.update(kwargs['defaults']) + kwargs['defaults'] = defaults super().__init__(*args, **kwargs) self.tests = [] self.add_argument( @@ -1054,13 +1075,25 @@ Stop running at the first failed test. ''' ) - def run_test(self, run_obj, run_args, extra_params): - test_id_string = self.test_setup(extra_params) + def run_test(self, run_obj, run_args, test_id=None): + ''' + This is a setup / run / teardown setup for simple tests that just do a single run. + + More complex tests might need to run the steps separately, e.g. gdb tests + must run multiple commands: one for the run and one GDB. + + :param run_obj: callable object + :param run_args: arguments to be passed to the runnable object + :param test_id: test identifier, to be added in addition to of arch and emulator ids + ''' + test_id_string = self.test_setup(test_id) exit_status = run_obj(**run_args) self.test_teardown(run_obj, exit_status, test_id_string) - def test_setup(self, extra_params): - test_id_string = '{} {} {}'.format(self.env['emulator'], self.env['arch'], extra_params) + def test_setup(self, test_id): + test_id_string = '{} {}'.format(self.env['emulator'], self.env['arch']) + if test_id is not None: + test_id_string += ' {}'.format(test_id) self.log_info('test_id {}'.format(test_id_string), flush=True) return test_id_string diff --git a/copy-overlay b/copy-overlay index 130d46d..53bae55 100755 --- a/copy-overlay +++ b/copy-overlay @@ -13,7 +13,6 @@ class Main(common.BuildCliFunction): description='''\ https://github.com/cirosantilli/linux-kernel-module-cheat#rootfs_overlay ''') - def build(self): # TODO: print rsync equivalent, move into shell_helpers. distutils.dir_util.copy_tree( diff --git a/rootfs_overlay/gem5_exit.sh b/rootfs_overlay/gem5_exit.sh new file mode 100755 index 0000000..4ff5c6a --- /dev/null +++ b/rootfs_overlay/gem5_exit.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# To be able to do init=/gem5_exit.sh, since kernel CLI argument passing is too messy: +# https://github.com/cirosantilli/linux-kernel-module-cheat#init-arguments +m5 exit diff --git a/run b/run index f3f4d29..0371131 100755 --- a/run +++ b/run @@ -472,10 +472,20 @@ Run QEMU with VNC instead of the default SDL. Connect to it with: .format(virtfs_dir=virtfs_dir, virtfs_tag=virtfs_tag), LF, ]) + if self.env['machine2'] is not None: + # Multiple -machine options can also be given comma separated in one -machine. + # We use multiple because the machine is used as an identifier on baremetal tests + # build paths, so better keep them clean. + machine2 = ['-machine', self.env['machine2'], LF] + else: + machine2 = [] cmd.extend( [ qemu_executable, LF, '-machine', self.env['machine'], LF, + ] + + machine2 + + [ '-device', 'rtl8139,netdev=net0', LF, '-gdb', 'tcp::{}'.format(self.env['gdb_port']), LF, '-kernel', self.env['image'], LF, diff --git a/test b/test index a14db1f..a26bc1d 100755 --- a/test +++ b/test @@ -10,7 +10,7 @@ while [ $# -gt 0 ]; do esac done ./test-boot --size "$test_size" -./test-modules --all-archs --all-emulators +./test-userland --all-archs --all-emulators ./test-gdb --all-archs --all-emulators ./test-baremetal --all-archs --all-emulators ./test-user-mode --all-archs --all-emulators diff --git a/test-baremetal b/test-baremetal index 8d6869e..ad5a467 100755 --- a/test-baremetal +++ b/test-baremetal @@ -8,9 +8,6 @@ import common class Main(common.TestCliFunction): def __init__(self): super().__init__( - defaults={ - 'print_time': False, - }, supported_archs=common.consts['crosstool_ng_supported_archs'], ) self.add_argument( diff --git a/test-boot b/test-boot index 89b4b96..cc179f4 100755 --- a/test-boot +++ b/test-boot @@ -7,9 +7,6 @@ from shell_helpers import LF class Main(common.TestCliFunction): def __init__(self): super().__init__( - defaults={ - 'print_time': False, - }, description='''\ Run Linux kernel boot tests and benchmarks. ''' @@ -55,10 +52,7 @@ Size of the tests to run. Scale: self.run = self.import_path_main('run') self.common_args = self.get_common_args() self.common_args['ctrl_c_host'] = True - if self.env['emulator'] == 'gem5': - self.common_args['eval'] = 'm5 exit' - elif self.env['emulator'] == 'qemu': - self.common_args['eval'] = '/poweroff.out' + self.common_args['kernel_cli'] = self.env['quit_init'] if (self.env['emulator'] == 'qemu' or (self.env['emulator'] == 'gem5' and self.env['size'] >= 2)): self._bench() diff --git a/test-gdb b/test-gdb index 0362cca..36861f6 100755 --- a/test-gdb +++ b/test-gdb @@ -7,11 +7,7 @@ import common class Main(common.TestCliFunction): def __init__(self): - super().__init__( - defaults={ - 'print_time': False, - }, - ) + super().__init__() self.add_argument( 'tests', nargs='*', diff --git a/test-user-mode b/test-user-mode index 6150035..fc3c70c 100755 --- a/test-user-mode +++ b/test-user-mode @@ -8,9 +8,6 @@ import common class Main(common.TestCliFunction): def __init__(self): super().__init__( - defaults={ - 'print_time': False, - }, description='''\ https://github.com/cirosantilli/linux-kernel-module-cheat#user-mode-tests ''' diff --git a/test-userland b/test-userland deleted file mode 100755 index bb6c66c..0000000 --- a/test-userland +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys - -import common - -class Main(common.LkmcCliFunction): - def __init__(self): - super().__init__( - defaults={ - 'print_time': False, - }, - description='''\ -Boot Linux and run all non-interactive userland tests, including those -for kernel modules. - -Detect failure based on the magic terminal failure string. -''' - ) - - def timed_main(self): - run = self.import_path_main('run') - run_args = self.get_common_args() - run_args['eval_after'] = '/test_all.sh;/poweroff.out' - run(**run_args) - -if __name__ == '__main__': - Main().cli() diff --git a/test-userland-full-system b/test-userland-full-system new file mode 100755 index 0000000..9178d2d --- /dev/null +++ b/test-userland-full-system @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 + +import os +import sys + +import common + +class Main(common.TestCliFunction): + def __init__(self): + super().__init__( + description='''\ +https://github.com/cirosantilli/linux-kernel-module-cheat#test-userland-in-full-system +''' + ) + def timed_main(self): + run = self.import_path_main('run') + run_args = self.get_common_args() + run_args['eval_after'] = '/test_all.sh;{};'.format(self.env['userland_quit_cmd']) + self.run_test(run, run_args) + +if __name__ == '__main__': + Main().cli() diff --git a/trace-boot b/trace-boot index ae29648..cd80517 100755 --- a/trace-boot +++ b/trace-boot @@ -15,18 +15,13 @@ More information at: https://github.com/cirosantilli/linux-kernel-module-cheat#t def timed_main(self): args = self.get_common_args() run = self.import_path_main('run') + self.common_args['kernel_cli'] = self.env['quit_init'] if self.env['emulator'] == 'gem5': - args.update({ - 'eval': 'm5 exit', - 'trace': 'Exec,-ExecSymbol,-ExecMicro', - }) + args['trace'] = 'Exec,-ExecSymbol,-ExecMicro' run.main(**args) elif self.env['emulator'] == 'qemu': run_args = args.copy() - run_args.update({ - 'kernel_cli': 'init=/poweroff.out', - 'trace': 'exec_tb', - }) + args['trace'] = 'exec_tb' run.main(**run_args) qemu_trace2txt = self.import_path_main('qemu-trace2txt') qemu_trace2txt.main(**args)