test-gdb: can now run in either userland or baremetal modes

Selection with --mode userland (default because has x86_64) or --mode baremetal.

This is the first userland tool where this choice is done on the command line,
which led to a refactor of supported_archs and is_baremetal and is_userland
into a single self.env['mode'].
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-05-29 00:00:00 +00:00
parent b6126a5268
commit 6994dc21af
21 changed files with 106 additions and 55 deletions

View File

@@ -15293,13 +15293,21 @@ Most userland programs that don't rely on kernel modules can also be tested in u
===== GDB tests ===== GDB tests
We have some link:https://github.com/pexpect/pexpect[pexpect] automated tests for the baremetal programs! We have some link:https://github.com/pexpect/pexpect[pexpect] automated tests for GDB for both userland and baremetal programs!
Run the userland tests:
.... ....
./build --all-archs test-gdb && \ ./build --all-archs test-gdb && \
./test-gdb --all-archs --all-emulators ./test-gdb --all-archs --all-emulators
.... ....
Run the baremetal tests instead:
....
./test-gdb --all-archs --all-emulators --mode baremetal
....
Sources: Sources:
* link:test-gdb[] * link:test-gdb[]

2
build
View File

@@ -318,8 +318,8 @@ so looping over all of them would waste time.
]), ]),
'test-gdb': _Component(dependencies=[ 'test-gdb': _Component(dependencies=[
'all-baremetal', 'all-baremetal',
'userland',
], ],
supported_archs=common.consts['crosstool_ng_supported_archs'],
), ),
'test-user-mode': _Component(dependencies=[ 'test-user-mode': _Component(dependencies=[
'test-user-mode-qemu', 'test-user-mode-qemu',

View File

@@ -9,13 +9,12 @@ class Main(common.BuildCliFunction):
def __init__(self): def __init__(self):
super().__init__( super().__init__(
defaults={ defaults={
'gcc_which':'crosstool-ng', 'gcc_which': 'crosstool-ng',
'mode': 'baremetal',
}, },
description='''\ description='''\
Build the baremetal examples with crosstool-NG. Build the baremetal examples with crosstool-NG.
''', ''',
is_baremetal=True,
supported_archs=common.consts['crosstool_ng_supported_archs']
) )
self._add_argument('--ccflags') self._add_argument('--ccflags')
self._add_argument('--force-rebuild') self._add_argument('--force-rebuild')

View File

@@ -8,10 +8,12 @@ from shell_helpers import LF
class Main(common.BuildCliFunction): class Main(common.BuildCliFunction):
def __init__(self): def __init__(self):
super().__init__( super().__init__(
defaults={
'mode': 'baremetal',
},
description='''\ description='''\
Build crosstool-NG with Newlib for bare metal compilation Build crosstool-NG with Newlib for bare metal compilation
''', ''',
supported_archs=common.consts['crosstool_ng_supported_archs']
) )
def build(self): def build(self):

View File

@@ -15,7 +15,11 @@ class Main(common.BuildCliFunction):
kwargs['description'] = '''\ kwargs['description'] = '''\
Build our compiled userland examples. Build our compiled userland examples.
''' '''
super().__init__(*args, is_userland=True, **kwargs) if not 'defaults' in kwargs:
kwargs['defaults'] = {}
if not 'mode' in kwargs['defaults']:
kwargs['defaults']['mode'] = 'userland'
super().__init__(*args, **kwargs)
self._add_argument('--ccflags') self._add_argument('--ccflags')
self._add_argument('--force-rebuild') self._add_argument('--force-rebuild')
self._add_argument('--optimization-level') self._add_argument('--optimization-level')

View File

@@ -159,17 +159,11 @@ class LkmcCliFunction(cli_function.CliFunction):
It would be beautiful to do this evaluation in a lazy way, e.g. with functions + It would be beautiful to do this evaluation in a lazy way, e.g. with functions +
cache decorators: cache decorators:
https://stackoverflow.com/questions/815110/is-there-a-decorator-to-simply-cache-function-return-values https://stackoverflow.com/questions/815110/is-there-a-decorator-to-simply-cache-function-return-values
:param is_userland: on ./run, we detect if userland based on --userland. However, ./test-user-mode
does not take --userland, and that causes problems.
''' '''
def __init__( def __init__(
self, self,
*args, *args,
is_baremetal=False,
is_userland=False,
defaults=None, defaults=None,
supported_archs=None,
**kwargs **kwargs
): ):
''' '''
@@ -180,13 +174,10 @@ class LkmcCliFunction(cli_function.CliFunction):
kwargs['extra_config_params'] = os.path.basename(inspect.getfile(self.__class__)) kwargs['extra_config_params'] = os.path.basename(inspect.getfile(self.__class__))
if defaults is None: if defaults is None:
defaults = {} defaults = {}
self.is_baremetal = is_baremetal
self.is_userland = is_userland
self._defaults = defaults self._defaults = defaults
self._is_common = True self._is_common = True
self._common_args = set() self._common_args = set()
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.supported_archs = supported_archs
self.print_lock = threading.Lock() self.print_lock = threading.Lock()
# Args for all scripts. # Args for all scripts.
@@ -252,6 +243,16 @@ Which toolchain binaries to use:
- crosstool-ng: the ones we built with ./build-crosstool-ng. For baremetal, links to newlib. - crosstool-ng: the ones we built with ./build-crosstool-ng. For baremetal, links to newlib.
- host: the host distro pre-packaged userland ones. For userland, links to glibc. - host: the host distro pre-packaged userland ones. For userland, links to glibc.
- host-baremetal: the host distro pre-packaged bare one. For baremetal, links to newlib. - host-baremetal: the host distro pre-packaged bare one. For baremetal, links to newlib.
'''
)
self.add_argument(
'--mode',
choices=('userland', 'baremetal'),
default=None,
help='''Differentiate between userland and baremetal for scripts that can do both.
./run differentiates between them based on the --userland and --baremetal options,
however those options take arguments, Certain scripts can be run on either user or baremetal mode.
If given, this differentiates between them.
''' '''
) )
self.add_argument( self.add_argument(
@@ -1161,8 +1162,11 @@ lunch aosp_{}-eng
_json = {} _json = {}
return _json return _json
def is_arch_supported(self, arch): def is_arch_supported(self, arch, mode):
return self.supported_archs is None or arch in self.supported_archs return not (
mode == 'baremetal' and
not arch in consts['crosstool_ng_supported_archs']
)
def log_error(self, msg): def log_error(self, msg):
with self.print_lock: with self.print_lock:
@@ -1224,12 +1228,12 @@ lunch aosp_{}-eng
continue continue
else: else:
raise Exception('native emulator only supported in if target arch == host arch') raise Exception('native emulator only supported in if target arch == host arch')
if env['userland'] is None and not self.is_userland: if env['userland'] is None and not env['mode'] == 'userland':
if real_all_emulators: if real_all_emulators:
continue continue
else: else:
raise Exception('native emulator only supported in user mode') raise Exception('native emulator only supported in user mode')
if self.is_arch_supported(arch): if self.is_arch_supported(arch, env['mode']):
if not env['dry_run']: if not env['dry_run']:
start_time = time.time() start_time = time.time()
env['arch'] = arch env['arch'] = arch
@@ -1526,16 +1530,14 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#gem5-debug-build
if my_path_properties.should_be_built( if my_path_properties.should_be_built(
self.env, self.env,
link, link,
is_baremetal=self.is_baremetal,
is_userland=self.is_userland
): ):
if extra_objs is None: if extra_objs is None:
extra_objs= [] extra_objs= []
if link: if link:
if self.is_baremetal or my_path_properties['extra_objs_lkmc_common']: if self.env['mode'] == 'baremetal' or my_path_properties['extra_objs_lkmc_common']:
extra_objs.extend(extra_objs_lkmc_common) extra_objs.extend(extra_objs_lkmc_common)
if ( if (
self.is_baremetal and self.env['mode'] == 'baremetal' and
not my_path_properties['extra_objs_disable_baremetal_bootloader'] not my_path_properties['extra_objs_disable_baremetal_bootloader']
): ):
extra_objs.extend(extra_objs_baremetal_bootloader) extra_objs.extend(extra_objs_baremetal_bootloader)
@@ -1756,7 +1758,7 @@ class TestCliFunction(LkmcCliFunction):
:param test_id: test identifier, to be added in addition to of arch and emulator ids :param test_id: test identifier, to be added in addition to of arch and emulator ids
:param thread_id: which thread the test is running under :param thread_id: which thread the test is running under
''' '''
if run_obj.is_arch_supported(run_args['archs'][0]): if run_obj.is_arch_supported(run_args['archs'][0], run_args.get('mode', None)):
cur_run_args = { cur_run_args = {
'run_id': thread_id, 'run_id': thread_id,
} }

View File

@@ -114,8 +114,6 @@ class PathProperties:
self, self,
env, env,
link=False, link=False,
is_baremetal=False,
is_userland=False,
): ):
if len(self.path_components) > 1 and \ if len(self.path_components) > 1 and \
self.path_components[1] == 'libs' and \ self.path_components[1] == 'libs' and \
@@ -129,23 +127,21 @@ class PathProperties:
env['arch'] in self['allowed_archs'] env['arch'] in self['allowed_archs']
) and \ ) and \
( (
(is_userland and self['userland'] ) or (env['mode'] == 'userland' and self['userland'] ) or
(is_baremetal and self['baremetal']) (env['mode'] == 'baremetal' and self['baremetal'])
) and \ ) and \
not ( not (
link and link and
self['no_executable'] self['no_executable']
) )
def should_be_tested(self, env, is_baremetal=False, is_userland=False): def should_be_tested(self, env):
return ( return (
self.should_be_built( self.should_be_built(
env, env,
is_baremetal=is_baremetal,
is_userland=is_userland
) and ) and
not ( not (
is_baremetal and ( env['mode'] == 'baremetal' and (
self['arm_aarch32'] or self['arm_aarch32'] or
self['signal_generated_by_os'] self['signal_generated_by_os']
) )

View File

@@ -11,8 +11,9 @@ import thread_pool
class Main(common.TestCliFunction): class Main(common.TestCliFunction):
def __init__(self): def __init__(self):
super().__init__( super().__init__(
is_baremetal=True, defaults={
supported_archs=common.consts['crosstool_ng_supported_archs'], 'mode': 'baremetal'
},
) )
self.add_argument( self.add_argument(
'tests', 'tests',
@@ -49,7 +50,7 @@ If given, run only the given tests. Otherwise, run all tests.
if os.path.splitext(in_filename)[1] in self.env['baremetal_build_in_exts']: if os.path.splitext(in_filename)[1] in self.env['baremetal_build_in_exts']:
path_relative_root = os.path.join(dirpath_relative_root, in_filename) path_relative_root = os.path.join(dirpath_relative_root, in_filename)
my_path_properties = path_properties.get(path_relative_root) my_path_properties = path_properties.get(path_relative_root)
if my_path_properties.should_be_tested(self.env, is_baremetal=True): if my_path_properties.should_be_tested(self.env):
cur_run_args = run_args.copy() cur_run_args = run_args.copy()
cur_run_args.update({ cur_run_args.update({
'baremetal': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()), 'baremetal': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()),

View File

@@ -13,11 +13,9 @@ class Main(common.TestCliFunction):
description='''\ description='''\
https://github.com/cirosantilli/linux-kernel-module-cheat#test-gdb https://github.com/cirosantilli/linux-kernel-module-cheat#test-gdb
''', ''',
supported_archs=common.consts['crosstool_ng_supported_archs'], defaults={
) 'mode': 'userland',
self.add_argument( }
'--userland-mode',
default=False
) )
self.add_argument( self.add_argument(
'tests', 'tests',
@@ -38,15 +36,9 @@ found by searching for the Python test files.
) )
def timed_main(self): def timed_main(self):
if self.env['userland_mode']: if self.env['mode'] == 'userland':
source_key = 'userland'
is_baremetal = False
is_userland = True
exts = self.env['build_in_exts'] exts = self.env['build_in_exts']
else: elif self.env['mode'] == 'baremetal':
source_key = 'baremetal'
is_baremetal = True
is_userland = False
exts = self.env['baremetal_build_in_exts'] exts = self.env['baremetal_build_in_exts']
rootdir_abs_len = len(self.env['root_dir']) rootdir_abs_len = len(self.env['root_dir'])
for test in self.env['tests']: for test in self.env['tests']:
@@ -61,17 +53,18 @@ found by searching for the Python test files.
my_path_properties = path_properties.get(path_relative_root) my_path_properties = path_properties.get(path_relative_root)
if my_path_properties.should_be_tested( if my_path_properties.should_be_tested(
self.env, self.env,
is_baremetal=is_baremetal,
is_userland=is_userland
): ):
run = lkmc.import_path.import_path_main('run') run = lkmc.import_path.import_path_main('run')
run_gdb = lkmc.import_path.import_path_main('run-gdb') run_gdb = lkmc.import_path.import_path_main('run-gdb')
common_args = self.get_common_args() common_args = self.get_common_args()
common_args[source_key] = path_relative_root common_args[self.env['mode']] = path_relative_root
run_args = common_args.copy() run_args = common_args.copy()
run_args['gdb_wait'] = True run_args['gdb_wait'] = True
run_args.update(self.base_run_args) run_args.update(self.base_run_args)
test_id_string = self.test_setup(run_args, path_relative_root) test_id_string = self.test_setup(
run_args,
'{} {}'.format(self.env['mode'], path_relative_root)
)
run_thread = threading.Thread(target=lambda: run(**run_args)) run_thread = threading.Thread(target=lambda: run(**run_args))
run_thread.start() run_thread.start()
gdb_args = common_args.copy() gdb_args = common_args.copy()

View File

@@ -17,7 +17,9 @@ TODO: expose all userland relevant ./run args here as well somehow.
''' '''
if not 'defaults' in kwargs: if not 'defaults' in kwargs:
kwargs['defaults'] = {} kwargs['defaults'] = {}
super().__init__(*args, is_userland=True, **kwargs) if not 'mode' in kwargs['defaults']:
kwargs['defaults']['mode'] = 'userland'
super().__init__(*args, **kwargs)
self.add_argument( self.add_argument(
'tests', 'tests',
nargs='*', nargs='*',
@@ -53,7 +55,7 @@ If given, run only the given tests. Otherwise, run all tests.
if os.path.splitext(in_filename)[1] in self.env['build_in_exts']: if os.path.splitext(in_filename)[1] in self.env['build_in_exts']:
path_relative_root = os.path.join(dirpath_relative_root, in_filename) path_relative_root = os.path.join(dirpath_relative_root, in_filename)
my_path_properties = path_properties.get(path_relative_root) my_path_properties = path_properties.get(path_relative_root)
if my_path_properties.should_be_tested(self.env, is_userland=True): if my_path_properties.should_be_tested(self.env):
cur_run_args = run_args.copy() cur_run_args = run_args.copy()
cur_run_args.update({ cur_run_args.update({
'userland': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()), 'userland': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()),

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1 @@
../test

View File

@@ -0,0 +1 @@
https://github.com/cirosantilli/linux-kernel-module-cheat#gdb-tests

View File

@@ -0,0 +1 @@
../build

View File

@@ -0,0 +1,11 @@
#include <lkmc.h>
LKMC_PROLOGUE
/* 1 + 2 == 3 */
mov $1, %rax
/* test-gdb-op1 */
mov $2, %rbx
add %rax, %rbx
/* test-gdb-result */
LKMC_ASSERT_EQ(%rbx, $3)
LKMC_EPILOGUE

View File

@@ -0,0 +1,7 @@
def test(self):
self.sendline('tbreak main')
self.sendline('continue')
self.continue_to('op1')
assert self.get_int('$rax') == 1
self.continue_to('result')
assert self.get_int('$rbx') == 3

View File

@@ -0,0 +1,10 @@
/* Test that we can set registers from GDB. */
#include <lkmc.h>
LKMC_PROLOGUE
mov $1, %rax
/* test-gdb-rax */
mov $2, %rbx
/* test-gdb-rbx */
LKMC_EPILOGUE

View File

@@ -0,0 +1,9 @@
def test(self):
self.sendline('tbreak main')
self.sendline('continue')
self.continue_to('rax')
self.sendline('set $rax = 3')
self.continue_to('rbx')
assert self.get_int('$rax') == 3
assert self.get_int('$rbx') == 2

View File

@@ -0,0 +1 @@
../test