From 59f96b192a653f4980f19478320a4cb29533b794 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: Fri, 18 Oct 2019 00:00:01 +0000 Subject: [PATCH] dhrystone: create nicer custom build, baremetal almost working --- .gitmodules | 3 ++ README.adoc | 52 +++++++++++++++++++++++++++++++-- baremetal/lib/syscalls.c | 21 ++++++++++---- build-baremetal | 32 ++++++++------------- build-dhrystone | 62 ++++++++++++++++++++++++++++++++++++++++ common.py | 25 +++++++++++++++- shell_helpers.py | 18 ++++++++---- submodules/dhrystone | 1 + 8 files changed, 180 insertions(+), 34 deletions(-) create mode 100755 build-dhrystone create mode 160000 submodules/dhrystone diff --git a/.gitmodules b/.gitmodules index 951732e..d118666 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,6 +14,9 @@ path = submodules/crosstool-ng # url = https://github.com/crosstool-ng/crosstool-ng url = https://github.com/cirosantilli/crosstool-ng +[submodule "submodules/dhrystone"] + path = submodules/dhrystone + url = https://github.com/cirosantilli/dhrystone [submodule "submodules/gcc"] path = submodules/gcc # url = git://gcc.gnu.org/git/gcc.git diff --git a/README.adoc b/README.adoc index 5673ad1..d70f404 100644 --- a/README.adoc +++ b/README.adoc @@ -3957,7 +3957,9 @@ Source: link:userland/c/getchar.c[] Let's see if user mode runs considerably faster than full system or not. -First we build Dhrystone manually statically since dynamic linking is broken in gem5 as explained at: xref:gem5-syscall-emulation-mode[xrefstyle=full]. +First we build <> manually statically since dynamic linking is broken in gem5 as explained at: xref:gem5-syscall-emulation-mode[xrefstyle=full]. + +TODO: move this section to our new custom dhrystone setup: xref:dhrystone[xrefstyle=full]. gem5 user mode: @@ -10454,7 +10456,7 @@ gem5 however has tended towards intensive code generation in order to support al OK, this is why we used gem5 in the first place, performance measurements! -Let's see how many cycles https://en.wikipedia.org/wiki/Dhrystone[Dhrystone], which Buildroot provides, takes for a few different input parameters. +Let's see how many cycles <>, which Buildroot provides, takes for a few different input parameters. We will do that for various input parameters on full system by taking a checkpoint after the boot finishes a fast atomic CPU boot, and then we will restore in a more detailed mode and run the benchmark: @@ -10937,6 +10939,52 @@ Buildroot built-in libraries, mostly under Libraries > Other: There are not yet enabled, but it should be easy to so, see: xref:add-new-buildroot-packages[xrefstyle=full] +===== Dhrystone + +https://en.wikipedia.org/wiki/Dhrystone + +Created in the 80's, it is not a representative measure of performance in modern computers anymore. It has mostly been replaced by https://en.wikipedia.org/wiki/SPECint[SPEC], which is... closed source! Unbelievable. + +<> has a `dhrystone` package, but because it is so interesting to us, we decided to also build it ourselves, which allows things like static and baremetal compilation more easily. + +Build and run on QEMU <>: + +.... +git submodule update --init submodules/dhrystone +./build-dhrystone --mode userland +./run --userland "$(./getvar userland_build_dir)/submodules/dhrystone/dhrystone" +.... + +Build and run on gem5 use mode: + +.... +./build-dhrystone --mode userland --static --force-rebuild +./run --emulator gem5 --userland "$(./getvar userland_build_dir)/submodules/dhrystone/dhrystone" +.... + +TODO automate run more nicely. + +Build for <> execution and run it in baremetal QEMU: + +.... +./build-dhrystone --arch aarch64 --mode baremetal +./run --arch aarch64 --baremetal "$(./getvar baremetal_build_dir)/submodules/dhrystone/dhrystone" +.... + +TODO: fix the build, just need to factor out all run arguments from link:build-baremetal[] into link:common.py[] and it should just work, no missing syscalls. + +If you really want the Buildroot package for some reason, build it with: + +.... +./build-buildroot --config 'BR2_PACKAGE_DHRYSTONE=y' +.... + +and run inside the guest from `PATH` with: + +.... +dhrystone +.... + ===== BST vs heap vs hashmap The following benchmark setup works both: diff --git a/baremetal/lib/syscalls.c b/baremetal/lib/syscalls.c index af4c464..66268d2 100644 --- a/baremetal/lib/syscalls.c +++ b/baremetal/lib/syscalls.c @@ -1,15 +1,21 @@ #include #include #include +#include #include +#define UART_DR(baseaddr) (*(unsigned int *)(baseaddr)) +#define UART_FR(baseaddr) (*(((unsigned int *)(baseaddr))+6)) + enum { UART_FR_RXFE = 0x10, }; -#define UART_DR(baseaddr) (*(unsigned int *)(baseaddr)) -#define UART_FR(baseaddr) (*(((unsigned int *)(baseaddr))+6)) +extern char heap_low; +extern char heap_top; + +char *heap_end = 0; void lkmc_baremetal_on_exit_callback(int status, void *arg) { (void)arg; @@ -73,10 +79,15 @@ int _read(int file, char *ptr, int len) { return todo; } -char *heap_end = 0; +/* Dummy implementation that just increments an integer. */ +_CLOCK_T_ _times_r (struct _reent *r, struct tms *ptms) { + static long long unsigned t = 0; + (void)r; + (void)ptms; + return t++; +} + caddr_t _sbrk(int incr) { - extern char heap_low; - extern char heap_top; char *prev_heap_end; if (heap_end == 0) { heap_end = &heap_low; diff --git a/build-baremetal b/build-baremetal index b9fdeac..4158b2a 100755 --- a/build-baremetal +++ b/build-baremetal @@ -36,23 +36,6 @@ Build the baremetal examples with crosstool-NG. self.env['baremetal_build_lib_dir'], self.env['common_basename_noext'] + self.env['obj_ext'] ) - syscalls_basename_noext = 'syscalls' - syscalls_src = os.path.join( - self.env['baremetal_source_lib_dir'], - syscalls_basename_noext + self.env['c_ext'] - ) - syscalls_obj = os.path.join( - self.env['baremetal_build_lib_dir'], - syscalls_basename_noext + self.env['obj_ext'] - ) - syscalls_asm_src = os.path.join( - self.env['baremetal_source_lib_dir'], - syscalls_basename_noext + '_asm' + self.env['asm_ext'] - ) - syscalls_asm_obj = os.path.join( - self.env['baremetal_build_lib_dir'], - syscalls_basename_noext + '_asm' + self.env['obj_ext'] - ) cc_flags = [ '-I', self.env['root_dir'], LF, '-O{}'.format(self.env['optimization_level']), LF, @@ -101,8 +84,14 @@ Build the baremetal examples with crosstool-NG. for in_path, out_path in [ (bootloader_src, extra_obj_baremetal_bootloader), (self.env['common_c'], extra_obj_lkmc_common), - (syscalls_src, syscalls_obj), - (syscalls_asm_src, syscalls_asm_obj), + ( + self.env['baremetal_syscalls_src'], + self.env['baremetal_syscalls_obj'] + ), + ( + self.env['baremetal_syscalls_asm_src'], + self.env['baremetal_syscalls_asm_obj'] + ), ]: self._build_one( in_path=in_path, @@ -133,7 +122,10 @@ Build the baremetal examples with crosstool-NG. self.env['baremetal_link_script'], self.env['common_h'] ], - 'extra_objs': [syscalls_obj, syscalls_asm_obj], + 'extra_objs': [ + self.env['baremetal_syscalls_obj'], + self.env['baremetal_syscalls_asm_obj'] + ], 'extra_objs_baremetal_bootloader': [extra_obj_baremetal_bootloader], 'extra_objs_lkmc_common': [extra_obj_lkmc_common], 'in_path': in_path, diff --git a/build-dhrystone b/build-dhrystone new file mode 100755 index 0000000..7dbd404 --- /dev/null +++ b/build-dhrystone @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import os +import shutil + +import common +import shlex +from shell_helpers import LF + +class Main(common.BuildCliFunction): + def __init__(self): + super().__init__( + description='''\ +https://cirosantilli.com/linux-kernel-module-cheat#dhrystone +''' + ) + self._add_argument('--ccflags') + self._add_argument('--force-rebuild') + self._add_argument('--optimization-level') + + def setup(self, env): + self.root_relpath = os.path.join('submodules', 'dhrystone') + + def build(self): + build_dir = self.get_build_dir() + cflags = ['-O{}'.format(self.env['optimization_level'])] + extra_flags = [] + if self.env['static']: + cflags.extend(['-static']) + if self.env['force_rebuild']: + extra_flags.extend(['-B', LF]) + if self.env['mode'] == 'baremetal': + extra_objs = [ + self.env['baremetal_syscalls_obj'], + self.env['baremetal_syscalls_asm_obj'] + ] + else: + extra_objs = [] + ret = self.sh.run_cmd( + [ + 'make', LF, + '-j', str(self.env['nproc']), LF, + '-C', os.path.join(self.env['submodules_dir'], 'dhrystone'), LF, + 'CC={}'.format(self.env['gcc_path']), LF, + 'CFLAGS={}'.format(' '.join(cflags)), LF, + 'EXTRA_OBJS={}'.format(' '.join(extra_objs)), LF, + 'OUT_DIR={}'.format(build_dir), LF, + ] + + extra_flags + ) + if ret == 0 and self.env['mode'] == 'userland': + self.sh.copy_file_if_update( + os.path.join(build_dir, 'dhrystone'), + os.path.join(self.env['out_rootfs_overlay_lkmc_dir'], self.root_relpath), + ) + return ret + + def get_build_dir(self): + return os.path.join(self.env['build_dir'], self.root_relpath) + +if __name__ == '__main__': + Main().cli() diff --git a/common.py b/common.py index 8c0aee3..7a8608d 100644 --- a/common.py +++ b/common.py @@ -947,6 +947,23 @@ Incompatible archs are skipped. env['simulator_name'] = 'qemu' env['baremetal_build_dir'] = join(env['out_dir'], 'baremetal', env['arch'], env['simulator_name'], env['machine']) env['baremetal_build_lib_dir'] = join(env['baremetal_build_dir'], env['baremetal_lib_basename']) + env['baremetal_syscalls_basename_noext'] = 'syscalls' + env['baremetal_syscalls_src'] = os.path.join( + env['baremetal_source_lib_dir'], + env['baremetal_syscalls_basename_noext'] + self.env['c_ext'] + ) + env['baremetal_syscalls_obj'] = os.path.join( + self.env['baremetal_build_lib_dir'], + env['baremetal_syscalls_basename_noext'] + self.env['obj_ext'] + ) + env['baremetal_syscalls_asm_src'] = os.path.join( + self.env['baremetal_source_lib_dir'], + env['baremetal_syscalls_basename_noext'] + '_asm' + self.env['asm_ext'] + ) + env['baremetal_syscalls_asm_obj'] = os.path.join( + self.env['baremetal_build_lib_dir'], + env['baremetal_syscalls_basename_noext'] + '_asm' + self.env['obj_ext'] + ) # Userland / baremetal common source. env['common_basename_noext'] = env['repo_short_id'] @@ -958,6 +975,10 @@ Incompatible archs are skipped. env['root_dir'], env['common_basename_noext'] + env['header_ext'] ) + if env['mode'] == 'baremetal': + env['build_dir'] = env['baremetal_build_dir'] + elif env['mode'] == 'userland': + env['build_dir'] = env['userland_build_dir'] # Docker env['docker_build_dir'] = join(env['out_dir'], 'docker', env['arch']) @@ -1022,8 +1043,10 @@ lunch aosp_{}-eng '''.format(self.env['android_arch']) # Toolchain. + if env['baremetal'] and not env['_args_given']['mode']: + env['mode'] = 'baremetal' if not env['_args_given']['gcc_which']: - if env['baremetal']: + if env['mode'] == 'baremetal': env['gcc_which'] = 'crosstool-ng' if env['gcc_which'] == 'buildroot': env['toolchain_prefix'] = os.path.join( diff --git a/shell_helpers.py b/shell_helpers.py index 55ed3b7..2217dc3 100644 --- a/shell_helpers.py +++ b/shell_helpers.py @@ -162,6 +162,15 @@ class ShellHelpers: ending = last_newline + ';' return newline_separator.join(out) + ending + def copy_file_if_update(self, src, dest): + if os.path.isdir(dest): + dest = os.path.join(dest, os.path.basename(src)) + if ( + not os.path.exists(dest) or \ + os.path.getmtime(src) > os.path.getmtime(dest) + ): + self.cp(src, dest) + def copy_dir_if_update_non_recursive( self, srcdir, @@ -178,12 +187,9 @@ class ShellHelpers: src = os.path.join(srcdir, basename) if os.path.isfile(src) or os.path.islink(src): noext, ext = os.path.splitext(basename) - dest = os.path.join(destdir, basename) - if ( - (filter_ext is None or ext == filter_ext) and - (not os.path.exists(dest) or os.path.getmtime(src) > os.path.getmtime(dest)) - ): - self.cp(src, dest) + if (filter_ext is None or ext == filter_ext): + dest = os.path.join(destdir, basename) + self.copy_file_if_update(src, dest) def copy_dir_if_update( self, diff --git a/submodules/dhrystone b/submodules/dhrystone new file mode 160000 index 0000000..fb5e012 --- /dev/null +++ b/submodules/dhrystone @@ -0,0 +1 @@ +Subproject commit fb5e01298a16e793672316067d8034d608e84c13