From f2e73bac831a1c6f3bca6af5cc602719dd052b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Tue, 22 Jan 2019 00:00:00 +0000 Subject: [PATCH] create userland tests Fix some more tabs. Parse the "Simulated exit code not 0!" string in gem5 and exit with the proper status --- README.adoc | 44 +++++++++++++++++++++++++-- baremetal/add.c | 12 ++++---- baremetal/arch/aarch64/el.c | 8 ++--- baremetal/arch/arm/el.c | 8 ++--- baremetal/exit.c | 2 +- baremetal/interactive/assert_fail.c | 2 +- baremetal/interactive/exit1.c | 2 +- baremetal/interactive/hello.c | 4 +-- baremetal/lib/syscalls.c | 22 +++++++------- build-userland | 4 +-- cli_function.py | 7 +++++ common.py | 4 +-- gem5-shell | 30 ++++++++++-------- run | 33 +++++++++++--------- test-gdb | 7 +++-- test-userland | 47 +++++++++++++++++++++++++++++ userland/external.desc | 1 - userland/false.c | 13 ++++++++ 18 files changed, 183 insertions(+), 67 deletions(-) create mode 100755 test-userland delete mode 100644 userland/external.desc create mode 100644 userland/false.c diff --git a/README.adoc b/README.adoc index 83c8ef4..c27f30c 100644 --- a/README.adoc +++ b/README.adoc @@ -3065,6 +3065,32 @@ Step debug also works: ; .... +===== gem5 syscall emulation exit status + +As of gem5 7fa4c946386e7207ad5859e8ade0bbfc14000d91, the crappy `se.py` script does not forward the exit status of syscall emulation mode, you can test it with: + +.... +./run --dry-run --gem5 --userland false --userland-build-id static +.... + +Source: link:userland/false[]. + +Then manually run the generated gem5 CLI, and do: + +.... +echo $? +.... + +and the output is always `0`. + +Instead, it just outputs a message to stdout just like for <>: + +.... +Simulated exit code not 0! Exit code is 1 +.... + +which we parse in link:run[] and then exit with the correct result ourselves... + ==== User mode vs full system benchmark Let's see if user mode runs considerably faster than full system or not. @@ -3125,6 +3151,18 @@ Result on <> at bad30f513c46c1b0995d3a10c0d9bc2a33dc4fa0: * QEMU user: 45 seconds * QEMU full system: 223 seconds +=== User mode testing + +Automatically run non-interactive userland tests that don't depend on nay kernel modules: + +.... +./test-userland +.... + +Source: link:test-userland[] + +The gem5 tests require building statically with build id `static`, see also: <>. TODO automate this better. + == Kernel module utilities === insmod @@ -7390,7 +7428,7 @@ Looks like a more raw alternative to libdrm: .... ./build-buildroot --config 'BR2_PACKABE_LIBDRI2=y' wget \ - -O "$(./getvar userland_src_dir)/dri2test.c" \ + -O "$(./getvar userland_source_dir)/dri2test.c" \ https://raw.githubusercontent.com/robclark/libdri2/master/test/dri2test.c \ ; ./build-userland @@ -9551,7 +9589,9 @@ Simulated exit code not 0! Exit code is 1 and exits with status 0. -TODO: it used to exit non 0, be like that, but it actually got changed to just print the message. Why? https://gem5-review.googlesource.com/c/public/gem5/+/4880 +We then parse that string ourselves in link:run[] and exit with the correct status... + +TODO: it used to be like that, but it actually got changed to just print the message. Why? https://gem5-review.googlesource.com/c/public/gem5/+/4880 `m5 fail` is just a superset of `m5 exit`, which is just: diff --git a/baremetal/add.c b/baremetal/add.c index 76567b4..3ec6671 100644 --- a/baremetal/add.c +++ b/baremetal/add.c @@ -1,13 +1,13 @@ #include int main(void) { - int i, j, k; - i = 1; + int i, j, k; + i = 1; /* test-gdb-op1 */ - j = 2; + j = 2; /* test-gdb-op2 */ - k = i + j; + k = i + j; /* test-gdb-result */ - if (k != 3) - common_assert_fail(); + if (k != 3) + common_assert_fail(); } diff --git a/baremetal/arch/aarch64/el.c b/baremetal/arch/aarch64/el.c index 851c362..a39c66c 100644 --- a/baremetal/arch/aarch64/el.c +++ b/baremetal/arch/aarch64/el.c @@ -4,8 +4,8 @@ #include int main(void) { - register uint64_t x0 __asm__ ("x0"); - __asm__ ("mrs x0, CurrentEL;" : : : "%x0"); - printf("%" PRIu64 "\n", x0 >> 2); - return 0; + register uint64_t x0 __asm__ ("x0"); + __asm__ ("mrs x0, CurrentEL;" : : : "%x0"); + printf("%" PRIu64 "\n", x0 >> 2); + return 0; } diff --git a/baremetal/arch/arm/el.c b/baremetal/arch/arm/el.c index 5944b58..1e8aa88 100644 --- a/baremetal/arch/arm/el.c +++ b/baremetal/arch/arm/el.c @@ -4,8 +4,8 @@ #include int main(void) { - register uint32_t r0 __asm__ ("r0"); - __asm__ ("mrs r0, CPSR" : : : "%r0"); - printf("%" PRIu32 "\n", r0 & 0x1F); - return 0; + register uint32_t r0 __asm__ ("r0"); + __asm__ ("mrs r0, CPSR" : : : "%r0"); + printf("%" PRIu32 "\n", r0 & 0x1F); + return 0; } diff --git a/baremetal/exit.c b/baremetal/exit.c index de81ab6..7260f78 100644 --- a/baremetal/exit.c +++ b/baremetal/exit.c @@ -2,6 +2,6 @@ #include int main(void) { - exit(0); + exit(0); } diff --git a/baremetal/interactive/assert_fail.c b/baremetal/interactive/assert_fail.c index d4ea62c..419a3c0 100644 --- a/baremetal/interactive/assert_fail.c +++ b/baremetal/interactive/assert_fail.c @@ -1,6 +1,6 @@ #include int main(void) { - common_assert_fail(); + common_assert_fail(); } diff --git a/baremetal/interactive/exit1.c b/baremetal/interactive/exit1.c index 90f1d14..342579e 100644 --- a/baremetal/interactive/exit1.c +++ b/baremetal/interactive/exit1.c @@ -2,5 +2,5 @@ #include int main(void) { - exit(1); + exit(1); } diff --git a/baremetal/interactive/hello.c b/baremetal/interactive/hello.c index fe49acd..20d437d 100644 --- a/baremetal/interactive/hello.c +++ b/baremetal/interactive/hello.c @@ -1,6 +1,6 @@ #include int main(void) { - puts("hello"); - return 0; + puts("hello"); + return 0; } diff --git a/baremetal/lib/syscalls.c b/baremetal/lib/syscalls.c index d9fbd61..a05be15 100644 --- a/baremetal/lib/syscalls.c +++ b/baremetal/lib/syscalls.c @@ -64,24 +64,24 @@ int _write(int file, char *ptr, int len) { void _exit(int status) { #if defined(GEM5) #if defined(__arm__) - __asm__ __volatile__ ("mov r0, #0; mov r1, #0; .inst 0xEE000110 | (0x21 << 16);"); + __asm__ __volatile__ ("mov r0, #0; mov r1, #0; .inst 0xEE000110 | (0x21 << 16);"); #elif defined(__aarch64__) - __asm__ __volatile__ ("mov x0, #0; .inst 0XFF000110 | (0x21 << 16);"); + __asm__ __volatile__ ("mov x0, #0; .inst 0XFF000110 | (0x21 << 16);"); #endif #else #if defined(__arm__) __asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456"); #elif defined(__aarch64__) - /* TODO actually use the exit value here, just for fun. */ + /* TODO actually use the exit value here, just for fun. */ __asm__ __volatile__ ( - "mov x1, #0x26\n" \ - "movk x1, #2, lsl #16\n" \ - "str x1, [sp,#0]\n" \ - "mov x0, #0\n" \ - "str x0, [sp,#8]\n" \ - "mov x1, sp\n" \ - "mov w0, #0x18\n" \ - "hlt 0xf000\n" + "mov x1, #0x26\n" \ + "movk x1, #2, lsl #16\n" \ + "str x1, [sp,#0]\n" \ + "mov x0, #0\n" \ + "str x0, [sp,#8]\n" \ + "mov x1, sp\n" \ + "mov w0, #0x18\n" \ + "hlt 0xf000\n" ); #endif #endif diff --git a/build-userland b/build-userland index 02f7123..749e3e5 100755 --- a/build-userland +++ b/build-userland @@ -61,7 +61,7 @@ has the OpenBLAS libraries and headers installed. 'make', LF, '-j', str(self.env['nproc']), LF, 'ARCH={}'.format(self.env['arch']), LF, - 'CCFLAGS_SCRIPT={} {}'.format('-I', self.env['userland_src_dir']), LF, + 'CCFLAGS_SCRIPT={} {}'.format('-I', self.env['userland_source_dir']), LF, 'COMMON_DIR={}'.format(self.env['root_dir']), LF, 'CC={}'.format(cc), LF, 'CXX={}'.format(cxx), LF, @@ -73,7 +73,7 @@ has the OpenBLAS libraries and headers installed. shlex.split(self.env['make_args']) + self.sh.add_newlines([os.path.join(build_dir, os.path.splitext(os.path.split(target)[1])[0]) + self.env['userland_build_ext'] for target in self.env['targets']]) ), - cwd=self.env['userland_src_dir'], + cwd=self.env['userland_source_dir'], extra_paths=[self.env['ccache_dir']], ) self.sh.copy_dir_if_update_non_recursive( diff --git a/cli_function.py b/cli_function.py index ca90f24..eeca9b8 100755 --- a/cli_function.py +++ b/cli_function.py @@ -3,6 +3,7 @@ import argparse import imp import os +import sys class _Argument: def __init__( @@ -193,6 +194,12 @@ class CliFunction: args = parser.parse_args(args=cli_args) return self(**vars(args)) + def cli_exit(self, *args, **kwargs): + ''' + Same as cli, but also exit the program with int(cli(). + ''' + sys.exit(int(self.cli(*args, **kwargs))) + @staticmethod def get_key(*args, **kwargs): return _Argument.get_key(*args, **kwargs) diff --git a/common.py b/common.py index 93b7cbe..6433f0f 100644 --- a/common.py +++ b/common.py @@ -49,7 +49,7 @@ consts['packages_dir'] = os.path.join(consts['root_dir'], 'buildroot_packages') consts['kernel_modules_subdir'] = 'kernel_modules' consts['kernel_modules_src_dir'] = os.path.join(consts['root_dir'], consts['kernel_modules_subdir']) consts['userland_subdir'] = 'userland' -consts['userland_src_dir'] = os.path.join(consts['root_dir'], consts['userland_subdir']) +consts['userland_source_dir'] = os.path.join(consts['root_dir'], consts['userland_subdir']) consts['userland_build_ext'] = '.out' consts['include_subdir'] = 'include' consts['include_src_dir'] = os.path.join(consts['root_dir'], consts['include_subdir']) @@ -783,7 +783,7 @@ Use gem5 instead of QEMU. Shortcut for `--emulator gem5`. def resolve_userland(self, path): return self.resolve_executable( path, - self.env['userland_src_dir'], + self.env['userland_source_dir'], self.env['userland_build_dir'], self.env['userland_build_ext'], ) diff --git a/gem5-shell b/gem5-shell index 66eca3f..58977ed 100755 --- a/gem5-shell +++ b/gem5-shell @@ -1,17 +1,23 @@ #!/usr/bin/env python3 -import sys - import common from shell_helpers import LF -parser = self.get_argparse( - default_args={'gem5':True}, - argparse_args={'description':'Connect a terminal to a running gem5 instance'} -) -args = self.setup(parser) -sys.exit(self.sh.run_cmd([ - kwargs['gem5_m5term'], LF, - 'localhost', LF, - str(kwargs['gem5_telnet_port']), LF, -])) +class Main(common.LkmcCliFunction): + def __init__(self): + super().__init__( + defaults={ + 'gem5': True, + }, + description='Connect a terminal to a running gem5 instance', + ) + def timed_main(self): + return self.sh.run_cmd([ + self.env['gem5_m5term'], + 'localhost', + str(self.env['gem5_telnet_port']), + LF, + ]) + +if __name__ == '__main__': + Main().cli_exit() diff --git a/run b/run index 830ab0b..ceb16dc 100755 --- a/run +++ b/run @@ -343,7 +343,7 @@ Run QEMU with VNC instead of the default SDL. Connect to it with: if self.env['userland'] is not None: cmd.extend([ self.env['gem5_se_file'], LF, - '-c', self.resolve_userland(self.env['userland']), LF, + '--cmd', self.resolve_userland(self.env['userland']), LF, ]) else: if self.env['gem5_script'] == 'fs': @@ -578,7 +578,8 @@ Run QEMU with VNC instead of the default SDL. Connect to it with: tmux_args += " --baremetal '{}'".format(self.env['baremetal']) if self.env['userland']: tmux_args += " --userland '{}'".format(self.env['userland']) - tmux_args += ' {}'.format(self.env['tmux']) + if self.env['tmux_args'] is not None: + tmux_args += ' {}'.format(self.env['tmux_args']) subprocess.Popen([ os.path.join(self.env['root_dir'], 'tmu'), "sleep 2;{} {}".format(tmux_cmd, tmux_args) @@ -599,22 +600,24 @@ Run QEMU with VNC instead of the default SDL. Connect to it with: panic_msg = b'Kernel panic - not syncing' panic_re = re.compile(panic_msg) error_string_found = False + exit_status = 0 if out_file is not None and not self.env['dry_run']: with open(self.env['termout_file'], 'br') as logfile: for line in logfile: if panic_re.search(line): - error_string_found = True - if os.path.exists(self.env['guest_terminal_file']): - with open(self.env['guest_terminal_file'], 'br') as logfile: - lines = logfile.readlines() - if lines: - last_line = lines[-1] - if last_line.rstrip() == self.env['magic_fail_string']: - error_string_found = True - if error_string_found: - self.log_error('simulation error detected by parsing logs') - return 1 - return 0 + exit_status = 1 + last_line = line.rstrip() + match = re.search(b'Simulated exit code not 0! Exit code is (\d+)', last_line) + if match: + exit_status = int(match.group(1)) + if not self.env['userland']: + if os.path.exists(self.env['guest_terminal_file']): + with open(self.env['guest_terminal_file'], 'br') as logfile: + if logfile.readlines()[-1].rstrip() == self.env['magic_fail_string']: + exit_status = 1 + if exit_status != 0: + self.log_error('simulation error detected by parsing logs') + return exit_status if __name__ == '__main__': - Main().cli() + Main().cli_exit() diff --git a/test-gdb b/test-gdb index 3c94bf1..962d17e 100755 --- a/test-gdb +++ b/test-gdb @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import functools import threading import os @@ -29,13 +28,15 @@ class Main(common.LkmcCliFunction): arch=arch, background=True, baremetal=test_script_noext, - print_time=False, emulator=emulator, wait_gdb=True )) run_thread.start() run_gdb( - arch=arch, baremetal=test_script_noext, print_time=False, emulator=emulator, test=True + arch=arch, + baremetal=test_script_noext, + emulator=emulator, + test=True, ) run_thread.join() diff --git a/test-userland b/test-userland new file mode 100755 index 0000000..b4f5d2e --- /dev/null +++ b/test-userland @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +import os + +import common + +class Main(common.LkmcCliFunction): + def timed_main(self): + run = self.import_path('run').Main() + sources = [ + 'add.c', + 'hello.c', + 'hello_cpp.cpp', + 'print_argv.c', + ] + # for emulator in self.env['emulators']: + for emulator in ['gem5']: + if emulator == 'gem5': + extra_args = { + 'userland_build_id': 'static', + } + else: + extra_args = {} + for arch in self.env['all_archs']: + if arch == 'x86_64': + arch_sources = [ + 'asm_hello' + ] + elif arch == 'aarch64': + arch_sources = [ + 'asm_hello' + ] + else: + arch_sources = [] + arch_sources[:] = [os.path.join('arch', arch, arch_source) for arch_source in arch_sources] + for source in sources + arch_sources: + exit_status = run( + arch=arch, + userland=source, + emulator=emulator, + **extra_args, + ) + if exit_status != 0: + raise Exception('Test failed: {} {} {} {}'.format(emulator, arch, source, exit_status)) + +if __name__ == '__main__': + Main().cli() diff --git a/userland/external.desc b/userland/external.desc deleted file mode 100644 index 5c5ffab..0000000 --- a/userland/external.desc +++ /dev/null @@ -1 +0,0 @@ -name: USERLAND diff --git a/userland/false.c b/userland/false.c new file mode 100644 index 0000000..5425237 --- /dev/null +++ b/userland/false.c @@ -0,0 +1,13 @@ +/* Test that emulators forward the exit status properly. */ + +#include + +int main(int argc, char **argv) { + int ret; + if (argc == 1) { + ret = 1; + } else { + ret = strtoull(argv[1], NULL, 0); + } + return ret; +}