mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-27 12:04:27 +01:00
userland: path properties getting nice!
This commit is contained in:
@@ -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
99
path_properties.py
Normal 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
27
run
@@ -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.
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user