diff --git a/example_properties.py b/example_properties.py deleted file mode 100644 index eaa99f1..0000000 --- a/example_properties.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env python3 - -import os - -class ExecutableProperties: - ''' - Encodes properties of userland and baremetal examples. - For directories, it applies to all files under the directory. - Used to determine how to build and test the examples. - ''' - def __init__( - self, - exit_status=0, - interactive=False, - more_than_1s=False, - receives_signal=False, - requires_kernel_modules=False, - ): - ''' - :param receives_signal: the test receives a signal. We skip those tests for now, - on userland because we are lazy to figure out the exact semantics - of how Python + QEMU + gem5 determine the exit status of signals. - ''' - self.exit_status = exit_status - self.interactive = interactive - self.more_than_1s = more_than_1s - self.receives_signal = receives_signal - self.requires_kernel_modules = requires_kernel_modules - - def should_be_tested(self): - return \ - not self.interactive and \ - not self.more_than_1s and \ - not self.receives_signal - -executable_properties = { - 'arch/x86_64/c/ring0.c': ExecutableProperties(receives_signal=True), - 'c/assert_fail.c': ExecutableProperties(exit_status=1), - 'c/false.c': ExecutableProperties(exit_status=1), - 'c/infinite_loop.c': ExecutableProperties(more_than_1s=True), - 'kernel_modules': ExecutableProperties(requires_kernel_modules=True), - 'posix/count.c': ExecutableProperties(more_than_1s=True), - 'posix/sleep_forever.c': ExecutableProperties(more_than_1s=True), - 'posix/virt_to_phys_test.c': ExecutableProperties(more_than_1s=True), -} - -def get(test_path): - test_path_components = test_path.split(os.sep) - if test_path in executable_properties: - return executable_properties[test_path] - else: - return ExecutableProperties() diff --git a/path_properties.py b/path_properties.py new file mode 100644 index 0000000..cfcccf6 --- /dev/null +++ b/path_properties.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 + +import os + +class PathProperties: + ''' + Encodes properties of userland and baremetal paths. + For directories, it applies to all files under the directory. + Used to determine how to build and test the examples. + ''' + def __init__( + self, + **kwargs + ): + self.properties = { + 'allowed_archs': None, + 'exit_status': 0, + 'interactive': False, + 'more_than_1s': False, + # The path does not generate an executable in itself, e.g. + # it only generates intermediate object files. + 'no_executable': False, + # the test receives a signal. We skip those tests for now, + # on userland because we are lazy to figure out the exact semantics + # of how Python + QEMU + gem5 determine the exit status of signals. + 'receives_signal': False, + 'requires_kernel_modules': False, + } + for key in kwargs: + if not key in self.properties: + raise ValueError('Unknown key: {}'.format(key)) + self.properties.update(kwargs) + + def __getitem__(self, key): + return self.properties[key] + + def update(self, other): + return self.properties.update(other.properties) + + def should_be_tested(self, arch): + return \ + not self['interactive'] and \ + not self['more_than_1s'] and \ + not self['no_executable'] and \ + not self['receives_signal'] and \ + not self['requires_kernel_modules'] and \ + ( + self['allowed_archs'] is None or + arch in self['allowed_archs'] + ) + +class PrefixTree: + def __init__(self, children=None, value=None): + if children == None: + children = {} + self.children = children + self.value = value + +path_properties_tree = PrefixTree({ + 'arch': PrefixTree({ + 'x86_64': PrefixTree( + { + 'c': PrefixTree({ + 'ring0.c': PrefixTree(value=PathProperties(receives_signal=True)) + }) + }, + PathProperties(allowed_archs={'x86_64'}), + ), + 'arm': PrefixTree(value=PathProperties(allowed_archs={'arm'})), + 'aarch64': PrefixTree(value=PathProperties(allowed_archs={'aarch64'})), + 'empty.S': PrefixTree(value=PathProperties(no_executable=True)), + 'fail.S': PrefixTree(value=PathProperties(no_executable=True)), + 'main.c': PrefixTree(value=PathProperties(no_executable=True)), + }), + 'c': PrefixTree({ + 'assert_fail.c': PrefixTree(value=PathProperties(exit_status=1)), + 'false.c': PrefixTree(value=PathProperties(exit_status=1)), + 'infinite_loop.c': PrefixTree(value=PathProperties(more_than_1s=True)), + }), + 'kernel_modules': PrefixTree(value=PathProperties(requires_kernel_modules=True)), + 'linux': PrefixTree(value=PathProperties(requires_kernel_modules=True)), + 'posix': PrefixTree({ + 'count.c': PrefixTree(value=PathProperties(more_than_1s=True)), + 'sleep_forever.c': PrefixTree(value=PathProperties(more_than_1s=True)), + 'virt_to_phys_test.c': PrefixTree(value=PathProperties(more_than_1s=True)), + }) +}) + +def get(test_path): + cur_node = path_properties_tree + path_properties = PathProperties() + for path_component in test_path.split(os.sep): + if path_component in cur_node.children: + cur_node = cur_node.children[path_component] + if cur_node.value is not None: + path_properties.update(cur_node.value) + else: + break + return path_properties diff --git a/run b/run index ca980b8..1a69392 100755 --- a/run +++ b/run @@ -29,11 +29,15 @@ https://superuser.com/questions/1373226/how-to-redirect-qemu-serial-output-to-bo ''' ) self.add_argument( - '-c', '--cpus', default=1, type=int, + '-c', + '--cpus', + default=1, + type=int, help='Number of guest CPUs to emulate. Default: %(default)s' ) self.add_argument( - '--ctrl-c-host', default=False, + '--ctrl-c-host', + default=False, help='''\ Ctrl +C kills the QEMU simulator instead of being passed to the guest. ''' @@ -48,7 +52,8 @@ For --emulator native, this debugs the target program. ''' ) self.add_argument( - '--debug-vm-args', default='', + '--debug-vm-args', + default='', help='Pass arguments to GDB. Implies --debug-vm.' ) self.add_argument( @@ -59,7 +64,8 @@ which is what you usually want. ''' ) self.add_argument( - '-E', '--eval', + '-E', + '--eval', help='''\ Replace the normal init with a minimal init that just evals the given string. See: https://github.com/cirosantilli/linux-kernel-module-cheat#replace-init @@ -68,7 +74,8 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#lkmc_home ''' ) self.add_argument( - '-F', '--eval-after', + '-F', + '--eval-after', help='''\ Pass a base64 encoded command line parameter that gets evalled at the end of the normal init. @@ -76,7 +83,9 @@ See: https://github.com/cirosantilli/linux-kernel-module-cheat#init-busybox ''' ) self.add_argument( - '-G', '--gem5-exe-args', default='', + '-G', + '--gem5-exe-args', + default='', help='''\ Pass extra options to the gem5 executable. Do not confuse with the arguments passed to config scripts, @@ -93,11 +102,13 @@ gem.op5 --debug-flags=Exec fs.py --cpu-type=HPI --caches help='Which gem5 script to use' ) self.add_argument( - '--gem5-readfile', default='', + '--gem5-readfile', + default='', help='Set the contents of m5 readfile to this string.' ) self.add_argument( - '--gem5-restore', type=int, + '--gem5-restore', + type=int, help='''\ Restore the nth most recently taken gem5 checkpoint according to directory timestamps. diff --git a/test-user-mode b/test-user-mode index 77bf7c3..c6d12d7 100755 --- a/test-user-mode +++ b/test-user-mode @@ -4,7 +4,7 @@ import os import sys import common -import example_properties +import path_properties from thread_pool import ThreadPool class Main(common.TestCliFunction): @@ -50,15 +50,15 @@ If given, run only the given tests. Otherwise, run all tests. dirpath_relative_root = path_abs[rootdir_abs_len + 1:] for in_filename in in_filenames: path_relative_root = os.path.join(dirpath_relative_root, in_filename) - test = example_properties.get(path_relative_root) - if test.should_be_tested(): + my_path_properties = path_properties.get(path_relative_root) + if my_path_properties.should_be_tested(self.env['arch']): cur_run_args = run_args.copy() cur_run_args.update({ 'background': True, 'userland': path_relative_root, }) error = thread_pool.submit({ - 'expected_exit_status': test.exit_status, + 'expected_exit_status': my_path_properties['exit_status'], 'run_args': cur_run_args, 'run_obj': self.import_path_main('run'), 'test_id': path_relative_root,