userland: path properties getting nice!

This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-05-05 00:00:00 +00:00
parent 8509f17a84
commit ca2dbcd25d
4 changed files with 122 additions and 64 deletions

View File

@@ -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()

99
path_properties.py Normal file
View File

@@ -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

27
run
View File

@@ -29,11 +29,15 @@ https://superuser.com/questions/1373226/how-to-redirect-qemu-serial-output-to-bo
''' '''
) )
self.add_argument( 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' help='Number of guest CPUs to emulate. Default: %(default)s'
) )
self.add_argument( self.add_argument(
'--ctrl-c-host', default=False, '--ctrl-c-host',
default=False,
help='''\ help='''\
Ctrl +C kills the QEMU simulator instead of being passed to the guest. 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( self.add_argument(
'--debug-vm-args', default='', '--debug-vm-args',
default='',
help='Pass arguments to GDB. Implies --debug-vm.' help='Pass arguments to GDB. Implies --debug-vm.'
) )
self.add_argument( self.add_argument(
@@ -59,7 +64,8 @@ which is what you usually want.
''' '''
) )
self.add_argument( self.add_argument(
'-E', '--eval', '-E',
'--eval',
help='''\ help='''\
Replace the normal init with a minimal init that just evals the given string. 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 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( self.add_argument(
'-F', '--eval-after', '-F',
'--eval-after',
help='''\ help='''\
Pass a base64 encoded command line parameter that gets evalled at the end of Pass a base64 encoded command line parameter that gets evalled at the end of
the normal init. the normal init.
@@ -76,7 +83,9 @@ See: https://github.com/cirosantilli/linux-kernel-module-cheat#init-busybox
''' '''
) )
self.add_argument( self.add_argument(
'-G', '--gem5-exe-args', default='', '-G',
'--gem5-exe-args',
default='',
help='''\ help='''\
Pass extra options to the gem5 executable. Pass extra options to the gem5 executable.
Do not confuse with the arguments passed to config scripts, 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' help='Which gem5 script to use'
) )
self.add_argument( self.add_argument(
'--gem5-readfile', default='', '--gem5-readfile',
default='',
help='Set the contents of m5 readfile to this string.' help='Set the contents of m5 readfile to this string.'
) )
self.add_argument( self.add_argument(
'--gem5-restore', type=int, '--gem5-restore',
type=int,
help='''\ help='''\
Restore the nth most recently taken gem5 checkpoint according to directory Restore the nth most recently taken gem5 checkpoint according to directory
timestamps. timestamps.

View File

@@ -4,7 +4,7 @@ import os
import sys import sys
import common import common
import example_properties import path_properties
from thread_pool import ThreadPool from thread_pool import ThreadPool
class Main(common.TestCliFunction): 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:] dirpath_relative_root = path_abs[rootdir_abs_len + 1:]
for in_filename in in_filenames: for in_filename in in_filenames:
path_relative_root = os.path.join(dirpath_relative_root, in_filename) path_relative_root = os.path.join(dirpath_relative_root, in_filename)
test = example_properties.get(path_relative_root) my_path_properties = path_properties.get(path_relative_root)
if test.should_be_tested(): if my_path_properties.should_be_tested(self.env['arch']):
cur_run_args = run_args.copy() cur_run_args = run_args.copy()
cur_run_args.update({ cur_run_args.update({
'background': True, 'background': True,
'userland': path_relative_root, 'userland': path_relative_root,
}) })
error = thread_pool.submit({ error = thread_pool.submit({
'expected_exit_status': test.exit_status, 'expected_exit_status': my_path_properties['exit_status'],
'run_args': cur_run_args, 'run_args': cur_run_args,
'run_obj': self.import_path_main('run'), 'run_obj': self.import_path_main('run'),
'test_id': path_relative_root, 'test_id': path_relative_root,