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
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 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
=== 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:

View File

@@ -1,13 +1,13 @@
#include <common.h>
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();
}

View File

@@ -4,8 +4,8 @@
#include <inttypes.h>
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;
}

View File

@@ -4,8 +4,8 @@
#include <inttypes.h>
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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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_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'],
)

View File

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

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

View File

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

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;
}