create userland tests

Fix some more tabs.

Parse the "Simulated exit code not 0!" string in gem5 and exit with the proper status
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-01-22 00:00:00 +00:00
parent 780e9ceeb4
commit f2e73bac83
18 changed files with 183 additions and 67 deletions

View File

@@ -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 <<m5-fail>>:
....
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 ==== User mode vs full system benchmark
Let's see if user mode runs considerably faster than full system or not. Let's see if user mode runs considerably faster than full system or not.
@@ -3125,6 +3151,18 @@ Result on <<p51>> at bad30f513c46c1b0995d3a10c0d9bc2a33dc4fa0:
* QEMU user: 45 seconds * QEMU user: 45 seconds
* QEMU full system: 223 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: <<gem5-syscall-emulation-mode>>. TODO automate this better.
== Kernel module utilities == Kernel module utilities
=== insmod === insmod
@@ -7390,7 +7428,7 @@ Looks like a more raw alternative to libdrm:
.... ....
./build-buildroot --config 'BR2_PACKABE_LIBDRI2=y' ./build-buildroot --config 'BR2_PACKABE_LIBDRI2=y'
wget \ 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 \ https://raw.githubusercontent.com/robclark/libdri2/master/test/dri2test.c \
; ;
./build-userland ./build-userland
@@ -9551,7 +9589,9 @@ Simulated exit code not 0! Exit code is 1
and exits with status 0. 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: `m5 fail` is just a superset of `m5 exit`, which is just:

View File

@@ -1,13 +1,13 @@
#include <common.h> #include <common.h>
int main(void) { int main(void) {
int i, j, k; int i, j, k;
i = 1; i = 1;
/* test-gdb-op1 */ /* test-gdb-op1 */
j = 2; j = 2;
/* test-gdb-op2 */ /* test-gdb-op2 */
k = i + j; k = i + j;
/* test-gdb-result */ /* test-gdb-result */
if (k != 3) if (k != 3)
common_assert_fail(); common_assert_fail();
} }

View File

@@ -4,8 +4,8 @@
#include <inttypes.h> #include <inttypes.h>
int main(void) { int main(void) {
register uint64_t x0 __asm__ ("x0"); register uint64_t x0 __asm__ ("x0");
__asm__ ("mrs x0, CurrentEL;" : : : "%x0"); __asm__ ("mrs x0, CurrentEL;" : : : "%x0");
printf("%" PRIu64 "\n", x0 >> 2); printf("%" PRIu64 "\n", x0 >> 2);
return 0; return 0;
} }

View File

@@ -4,8 +4,8 @@
#include <inttypes.h> #include <inttypes.h>
int main(void) { int main(void) {
register uint32_t r0 __asm__ ("r0"); register uint32_t r0 __asm__ ("r0");
__asm__ ("mrs r0, CPSR" : : : "%r0"); __asm__ ("mrs r0, CPSR" : : : "%r0");
printf("%" PRIu32 "\n", r0 & 0x1F); printf("%" PRIu32 "\n", r0 & 0x1F);
return 0; return 0;
} }

View File

@@ -2,6 +2,6 @@
#include <stdlib.h> #include <stdlib.h>
int main(void) { int main(void) {
exit(0); exit(0);
} }

View File

@@ -1,6 +1,6 @@
#include <common.h> #include <common.h>
int main(void) { int main(void) {
common_assert_fail(); common_assert_fail();
} }

View File

@@ -2,5 +2,5 @@
#include <stdlib.h> #include <stdlib.h>
int main(void) { int main(void) {
exit(1); exit(1);
} }

View File

@@ -1,6 +1,6 @@
#include <stdio.h> #include <stdio.h>
int main(void) { int main(void) {
puts("hello"); puts("hello");
return 0; return 0;
} }

View File

@@ -64,24 +64,24 @@ int _write(int file, char *ptr, int len) {
void _exit(int status) { void _exit(int status) {
#if defined(GEM5) #if defined(GEM5)
#if defined(__arm__) #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__) #elif defined(__aarch64__)
__asm__ __volatile__ ("mov x0, #0; .inst 0XFF000110 | (0x21 << 16);"); __asm__ __volatile__ ("mov x0, #0; .inst 0XFF000110 | (0x21 << 16);");
#endif #endif
#else #else
#if defined(__arm__) #if defined(__arm__)
__asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456"); __asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456");
#elif defined(__aarch64__) #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__ ( __asm__ __volatile__ (
"mov x1, #0x26\n" \ "mov x1, #0x26\n" \
"movk x1, #2, lsl #16\n" \ "movk x1, #2, lsl #16\n" \
"str x1, [sp,#0]\n" \ "str x1, [sp,#0]\n" \
"mov x0, #0\n" \ "mov x0, #0\n" \
"str x0, [sp,#8]\n" \ "str x0, [sp,#8]\n" \
"mov x1, sp\n" \ "mov x1, sp\n" \
"mov w0, #0x18\n" \ "mov w0, #0x18\n" \
"hlt 0xf000\n" "hlt 0xf000\n"
); );
#endif #endif
#endif #endif

View File

@@ -61,7 +61,7 @@ has the OpenBLAS libraries and headers installed.
'make', LF, 'make', LF,
'-j', str(self.env['nproc']), LF, '-j', str(self.env['nproc']), LF,
'ARCH={}'.format(self.env['arch']), 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, 'COMMON_DIR={}'.format(self.env['root_dir']), LF,
'CC={}'.format(cc), LF, 'CC={}'.format(cc), LF,
'CXX={}'.format(cxx), LF, 'CXX={}'.format(cxx), LF,
@@ -73,7 +73,7 @@ has the OpenBLAS libraries and headers installed.
shlex.split(self.env['make_args']) + 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']]) 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']], extra_paths=[self.env['ccache_dir']],
) )
self.sh.copy_dir_if_update_non_recursive( self.sh.copy_dir_if_update_non_recursive(

View File

@@ -3,6 +3,7 @@
import argparse import argparse
import imp import imp
import os import os
import sys
class _Argument: class _Argument:
def __init__( def __init__(
@@ -193,6 +194,12 @@ class CliFunction:
args = parser.parse_args(args=cli_args) args = parser.parse_args(args=cli_args)
return self(**vars(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 @staticmethod
def get_key(*args, **kwargs): def get_key(*args, **kwargs):
return _Argument.get_key(*args, **kwargs) return _Argument.get_key(*args, **kwargs)

View File

@@ -49,7 +49,7 @@ consts['packages_dir'] = os.path.join(consts['root_dir'], 'buildroot_packages')
consts['kernel_modules_subdir'] = 'kernel_modules' consts['kernel_modules_subdir'] = 'kernel_modules'
consts['kernel_modules_src_dir'] = os.path.join(consts['root_dir'], consts['kernel_modules_subdir']) consts['kernel_modules_src_dir'] = os.path.join(consts['root_dir'], consts['kernel_modules_subdir'])
consts['userland_subdir'] = 'userland' 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['userland_build_ext'] = '.out'
consts['include_subdir'] = 'include' consts['include_subdir'] = 'include'
consts['include_src_dir'] = os.path.join(consts['root_dir'], consts['include_subdir']) 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): def resolve_userland(self, path):
return self.resolve_executable( return self.resolve_executable(
path, path,
self.env['userland_src_dir'], self.env['userland_source_dir'],
self.env['userland_build_dir'], self.env['userland_build_dir'],
self.env['userland_build_ext'], self.env['userland_build_ext'],
) )

View File

@@ -1,17 +1,23 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import sys
import common import common
from shell_helpers import LF from shell_helpers import LF
parser = self.get_argparse( class Main(common.LkmcCliFunction):
default_args={'gem5':True}, def __init__(self):
argparse_args={'description':'Connect a terminal to a running gem5 instance'} super().__init__(
) defaults={
args = self.setup(parser) 'gem5': True,
sys.exit(self.sh.run_cmd([ },
kwargs['gem5_m5term'], LF, description='Connect a terminal to a running gem5 instance',
'localhost', LF, )
str(kwargs['gem5_telnet_port']), LF, 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()

33
run
View File

@@ -343,7 +343,7 @@ Run QEMU with VNC instead of the default SDL. Connect to it with:
if self.env['userland'] is not None: if self.env['userland'] is not None:
cmd.extend([ cmd.extend([
self.env['gem5_se_file'], LF, self.env['gem5_se_file'], LF,
'-c', self.resolve_userland(self.env['userland']), LF, '--cmd', self.resolve_userland(self.env['userland']), LF,
]) ])
else: else:
if self.env['gem5_script'] == 'fs': 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']) tmux_args += " --baremetal '{}'".format(self.env['baremetal'])
if self.env['userland']: if self.env['userland']:
tmux_args += " --userland '{}'".format(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([ subprocess.Popen([
os.path.join(self.env['root_dir'], 'tmu'), os.path.join(self.env['root_dir'], 'tmu'),
"sleep 2;{} {}".format(tmux_cmd, tmux_args) "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_msg = b'Kernel panic - not syncing'
panic_re = re.compile(panic_msg) panic_re = re.compile(panic_msg)
error_string_found = False error_string_found = False
exit_status = 0
if out_file is not None and not self.env['dry_run']: if out_file is not None and not self.env['dry_run']:
with open(self.env['termout_file'], 'br') as logfile: with open(self.env['termout_file'], 'br') as logfile:
for line in logfile: for line in logfile:
if panic_re.search(line): if panic_re.search(line):
error_string_found = True exit_status = 1
if os.path.exists(self.env['guest_terminal_file']): last_line = line.rstrip()
with open(self.env['guest_terminal_file'], 'br') as logfile: match = re.search(b'Simulated exit code not 0! Exit code is (\d+)', last_line)
lines = logfile.readlines() if match:
if lines: exit_status = int(match.group(1))
last_line = lines[-1] if not self.env['userland']:
if last_line.rstrip() == self.env['magic_fail_string']: if os.path.exists(self.env['guest_terminal_file']):
error_string_found = True with open(self.env['guest_terminal_file'], 'br') as logfile:
if error_string_found: if logfile.readlines()[-1].rstrip() == self.env['magic_fail_string']:
self.log_error('simulation error detected by parsing logs') exit_status = 1
return 1 if exit_status != 0:
return 0 self.log_error('simulation error detected by parsing logs')
return exit_status
if __name__ == '__main__': if __name__ == '__main__':
Main().cli() Main().cli_exit()

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import functools
import threading import threading
import os import os
@@ -29,13 +28,15 @@ class Main(common.LkmcCliFunction):
arch=arch, arch=arch,
background=True, background=True,
baremetal=test_script_noext, baremetal=test_script_noext,
print_time=False,
emulator=emulator, emulator=emulator,
wait_gdb=True wait_gdb=True
)) ))
run_thread.start() run_thread.start()
run_gdb( 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() run_thread.join()

47
test-userland Executable file
View File

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

View File

@@ -1 +0,0 @@
name: USERLAND

13
userland/false.c Normal file
View File

@@ -0,0 +1,13 @@
/* Test that emulators forward the exit status properly. */
#include <stdlib.h>
int main(int argc, char **argv) {
int ret;
if (argc == 1) {
ret = 1;
} else {
ret = strtoull(argv[1], NULL, 0);
}
return ret;
}