diff --git a/README.adoc b/README.adoc index 60c5b5f..adea126 100644 --- a/README.adoc +++ b/README.adoc @@ -15293,13 +15293,21 @@ Most userland programs that don't rely on kernel modules can also be tested in u ===== 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 && \ ./test-gdb --all-archs --all-emulators .... +Run the baremetal tests instead: + +.... +./test-gdb --all-archs --all-emulators --mode baremetal +.... + Sources: * link:test-gdb[] diff --git a/build b/build index 9954590..fd80036 100755 --- a/build +++ b/build @@ -318,8 +318,8 @@ so looping over all of them would waste time. ]), 'test-gdb': _Component(dependencies=[ 'all-baremetal', + 'userland', ], - supported_archs=common.consts['crosstool_ng_supported_archs'], ), 'test-user-mode': _Component(dependencies=[ 'test-user-mode-qemu', diff --git a/build-baremetal b/build-baremetal index 1f926f5..ec9d78a 100755 --- a/build-baremetal +++ b/build-baremetal @@ -9,13 +9,12 @@ class Main(common.BuildCliFunction): def __init__(self): super().__init__( defaults={ - 'gcc_which':'crosstool-ng', + 'gcc_which': 'crosstool-ng', + 'mode': 'baremetal', }, description='''\ 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('--force-rebuild') diff --git a/build-crosstool-ng b/build-crosstool-ng index 72e42df..329261f 100755 --- a/build-crosstool-ng +++ b/build-crosstool-ng @@ -8,10 +8,12 @@ from shell_helpers import LF class Main(common.BuildCliFunction): def __init__(self): super().__init__( + defaults={ + 'mode': 'baremetal', + }, description='''\ Build crosstool-NG with Newlib for bare metal compilation ''', - supported_archs=common.consts['crosstool_ng_supported_archs'] ) def build(self): diff --git a/build-userland b/build-userland index a389fa6..637963f 100755 --- a/build-userland +++ b/build-userland @@ -15,7 +15,11 @@ class Main(common.BuildCliFunction): kwargs['description'] = '''\ 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('--force-rebuild') self._add_argument('--optimization-level') diff --git a/common.py b/common.py index 290c309..dacd1ca 100644 --- a/common.py +++ b/common.py @@ -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 + cache decorators: 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__( self, *args, - is_baremetal=False, - is_userland=False, defaults=None, - supported_archs=None, **kwargs ): ''' @@ -180,13 +174,10 @@ class LkmcCliFunction(cli_function.CliFunction): kwargs['extra_config_params'] = os.path.basename(inspect.getfile(self.__class__)) if defaults is None: defaults = {} - self.is_baremetal = is_baremetal - self.is_userland = is_userland self._defaults = defaults self._is_common = True self._common_args = set() super().__init__(*args, **kwargs) - self.supported_archs = supported_archs self.print_lock = threading.Lock() # 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. - 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. +''' + ) + 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( @@ -1161,8 +1162,11 @@ lunch aosp_{}-eng _json = {} return _json - def is_arch_supported(self, arch): - return self.supported_archs is None or arch in self.supported_archs + def is_arch_supported(self, arch, mode): + return not ( + mode == 'baremetal' and + not arch in consts['crosstool_ng_supported_archs'] + ) def log_error(self, msg): with self.print_lock: @@ -1224,12 +1228,12 @@ lunch aosp_{}-eng continue else: 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: continue else: 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']: start_time = time.time() 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( self.env, link, - is_baremetal=self.is_baremetal, - is_userland=self.is_userland ): if extra_objs is None: extra_objs= [] 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) if ( - self.is_baremetal and + self.env['mode'] == 'baremetal' and not my_path_properties['extra_objs_disable_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 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 = { 'run_id': thread_id, } diff --git a/path_properties.py b/path_properties.py index 3375158..dcf1ab9 100644 --- a/path_properties.py +++ b/path_properties.py @@ -114,8 +114,6 @@ class PathProperties: self, env, link=False, - is_baremetal=False, - is_userland=False, ): if len(self.path_components) > 1 and \ self.path_components[1] == 'libs' and \ @@ -129,23 +127,21 @@ class PathProperties: env['arch'] in self['allowed_archs'] ) and \ ( - (is_userland and self['userland'] ) or - (is_baremetal and self['baremetal']) + (env['mode'] == 'userland' and self['userland'] ) or + (env['mode'] == 'baremetal' and self['baremetal']) ) and \ not ( link and self['no_executable'] ) - def should_be_tested(self, env, is_baremetal=False, is_userland=False): + def should_be_tested(self, env): return ( self.should_be_built( env, - is_baremetal=is_baremetal, - is_userland=is_userland ) and not ( - is_baremetal and ( + env['mode'] == 'baremetal' and ( self['arm_aarch32'] or self['signal_generated_by_os'] ) diff --git a/test-baremetal b/test-baremetal index 24f5dbe..7f3aa39 100755 --- a/test-baremetal +++ b/test-baremetal @@ -11,8 +11,9 @@ import thread_pool class Main(common.TestCliFunction): def __init__(self): super().__init__( - is_baremetal=True, - supported_archs=common.consts['crosstool_ng_supported_archs'], + defaults={ + 'mode': 'baremetal' + }, ) self.add_argument( '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']: path_relative_root = os.path.join(dirpath_relative_root, in_filename) 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.update({ 'baremetal': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()), diff --git a/test-gdb b/test-gdb index e2d39d5..c29529a 100755 --- a/test-gdb +++ b/test-gdb @@ -13,11 +13,9 @@ class Main(common.TestCliFunction): description='''\ https://github.com/cirosantilli/linux-kernel-module-cheat#test-gdb ''', - supported_archs=common.consts['crosstool_ng_supported_archs'], - ) - self.add_argument( - '--userland-mode', - default=False + defaults={ + 'mode': 'userland', + } ) self.add_argument( 'tests', @@ -38,15 +36,9 @@ found by searching for the Python test files. ) def timed_main(self): - if self.env['userland_mode']: - source_key = 'userland' - is_baremetal = False - is_userland = True + if self.env['mode'] == 'userland': exts = self.env['build_in_exts'] - else: - source_key = 'baremetal' - is_baremetal = True - is_userland = False + elif self.env['mode'] == 'baremetal': exts = self.env['baremetal_build_in_exts'] rootdir_abs_len = len(self.env['root_dir']) 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) if my_path_properties.should_be_tested( self.env, - is_baremetal=is_baremetal, - is_userland=is_userland ): run = lkmc.import_path.import_path_main('run') run_gdb = lkmc.import_path.import_path_main('run-gdb') 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['gdb_wait'] = True 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.start() gdb_args = common_args.copy() diff --git a/test-user-mode b/test-user-mode index bbef608..5491e23 100755 --- a/test-user-mode +++ b/test-user-mode @@ -17,7 +17,9 @@ TODO: expose all userland relevant ./run args here as well somehow. ''' if not 'defaults' in kwargs: 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( 'tests', 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']: path_relative_root = os.path.join(dirpath_relative_root, in_filename) 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.update({ 'userland': os.path.relpath(os.path.join(path_abs, in_filename), os.getcwd()), diff --git a/userland/arch/aarch64/gdb_tests/build b/userland/arch/aarch64/gdb_tests/build new file mode 120000 index 0000000..ab18017 --- /dev/null +++ b/userland/arch/aarch64/gdb_tests/build @@ -0,0 +1 @@ +../build \ No newline at end of file diff --git a/userland/arch/aarch64/gdb_tests/test b/userland/arch/aarch64/gdb_tests/test new file mode 120000 index 0000000..419df4f --- /dev/null +++ b/userland/arch/aarch64/gdb_tests/test @@ -0,0 +1 @@ +../test \ No newline at end of file diff --git a/userland/arch/arm/gdb_tests/build b/userland/arch/arm/gdb_tests/build new file mode 120000 index 0000000..ab18017 --- /dev/null +++ b/userland/arch/arm/gdb_tests/build @@ -0,0 +1 @@ +../build \ No newline at end of file diff --git a/userland/arch/arm/gdb_tests/test b/userland/arch/arm/gdb_tests/test new file mode 120000 index 0000000..419df4f --- /dev/null +++ b/userland/arch/arm/gdb_tests/test @@ -0,0 +1 @@ +../test \ No newline at end of file diff --git a/userland/arch/x86_64/gdb_tests/README.adoc b/userland/arch/x86_64/gdb_tests/README.adoc new file mode 100644 index 0000000..1bcfcd0 --- /dev/null +++ b/userland/arch/x86_64/gdb_tests/README.adoc @@ -0,0 +1 @@ +https://github.com/cirosantilli/linux-kernel-module-cheat#gdb-tests diff --git a/userland/arch/x86_64/gdb_tests/build b/userland/arch/x86_64/gdb_tests/build new file mode 120000 index 0000000..ab18017 --- /dev/null +++ b/userland/arch/x86_64/gdb_tests/build @@ -0,0 +1 @@ +../build \ No newline at end of file diff --git a/userland/arch/x86_64/gdb_tests/integer_registers.S b/userland/arch/x86_64/gdb_tests/integer_registers.S new file mode 100644 index 0000000..53d5491 --- /dev/null +++ b/userland/arch/x86_64/gdb_tests/integer_registers.S @@ -0,0 +1,11 @@ +#include + +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 diff --git a/userland/arch/x86_64/gdb_tests/integer_registers.py b/userland/arch/x86_64/gdb_tests/integer_registers.py new file mode 100644 index 0000000..df05a42 --- /dev/null +++ b/userland/arch/x86_64/gdb_tests/integer_registers.py @@ -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 diff --git a/userland/arch/x86_64/gdb_tests/set_registers.S b/userland/arch/x86_64/gdb_tests/set_registers.S new file mode 100644 index 0000000..a590171 --- /dev/null +++ b/userland/arch/x86_64/gdb_tests/set_registers.S @@ -0,0 +1,10 @@ +/* Test that we can set registers from GDB. */ + +#include + +LKMC_PROLOGUE + mov $1, %rax + /* test-gdb-rax */ + mov $2, %rbx + /* test-gdb-rbx */ +LKMC_EPILOGUE diff --git a/userland/arch/x86_64/gdb_tests/set_registers.py b/userland/arch/x86_64/gdb_tests/set_registers.py new file mode 100644 index 0000000..430481b --- /dev/null +++ b/userland/arch/x86_64/gdb_tests/set_registers.py @@ -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 diff --git a/userland/arch/x86_64/gdb_tests/test b/userland/arch/x86_64/gdb_tests/test new file mode 120000 index 0000000..419df4f --- /dev/null +++ b/userland/arch/x86_64/gdb_tests/test @@ -0,0 +1 @@ +../test \ No newline at end of file