diff --git a/lisa/_assets/__init__.py b/lisa/_assets/__init__.py index 69deff6da3447d201dc1cc4efead1cb39380ff6c..9e999ae7b222875e18add3f7ca5418ffefc4a78e 100644 --- a/lisa/_assets/__init__.py +++ b/lisa/_assets/__init__.py @@ -21,19 +21,7 @@ import platform import shutil from pathlib import Path -def _get_abi(): - machine = platform.machine() - return dict( - x86_64='x86_64', - aarch64='arm64', - arm='arm', - )[machine] - -HOST_ABI = _get_abi() -""" -ABI of the machine that imported that module. -""" -del _get_abi +from lisa.utils import LISA_HOST_ABI ASSETS_PATH = os.path.dirname(__file__) """ @@ -58,17 +46,17 @@ def _get_abi_bin(): ABI_BINARIES = _get_abi_bin() del _get_abi_bin -HOST_BINARIES = ABI_BINARIES[HOST_ABI] +HOST_BINARIES = ABI_BINARIES[LISA_HOST_ABI] def _make_path(abi=None): - abi = abi or HOST_ABI + abi = abi or LISA_HOST_ABI compos = [ os.path.join(ASSETS_PATH, 'binaries', abi), os.path.join(ASSETS_PATH, 'scripts'), ] - if abi == HOST_ABI: + if abi == LISA_HOST_ABI: path = os.environ['PATH'] use_system = bool(int(os.environ.get('LISA_USE_SYSTEM_BIN', 0))) if use_system: @@ -78,7 +66,7 @@ def _make_path(abi=None): return ':'.join(compos) -HOST_PATH = _make_path(HOST_ABI) +HOST_PATH = _make_path(LISA_HOST_ABI) """ Value to be used as the ``PATH`` env var on the host. """ diff --git a/lisa/_assets/binaries/arm64/pahole b/lisa/_assets/binaries/arm64/pahole index 5f4f98b36320222ced829d82283688a011a7c330..209e0721e190710aa6e664601876c588694180aa 100755 Binary files a/lisa/_assets/binaries/arm64/pahole and b/lisa/_assets/binaries/arm64/pahole differ diff --git a/lisa/_assets/binaries/armeabi/pahole b/lisa/_assets/binaries/armeabi/pahole index 5e67d2a03f8f414840eed05da342f487800e6ff1..1355aba9a021fa65f3a9753e6db9ee952edbbcdd 100755 Binary files a/lisa/_assets/binaries/armeabi/pahole and b/lisa/_assets/binaries/armeabi/pahole differ diff --git a/lisa/_assets/binaries/x86_64/pahole b/lisa/_assets/binaries/x86_64/pahole index a46c5b585c27ddfa141a9f5b9a68f34a0add28fa..774a0adb4fff80bf85ce216a6a237471d3de3167 100755 Binary files a/lisa/_assets/binaries/x86_64/pahole and b/lisa/_assets/binaries/x86_64/pahole differ diff --git a/lisa/_kmod.py b/lisa/_kmod.py index 05e62c10b0a647685b778b0e89153882dae78678..91762467ba4301bfdc8305f39e0cdd017ab47efa 100644 --- a/lisa/_kmod.py +++ b/lisa/_kmod.py @@ -173,8 +173,34 @@ def _subprocess_log(*args, env=None, extra_env=None, **kwargs): return subprocess_log(*args, **kwargs, env=env) +def _kbuild_make_cmd(path, targets, cc, make_vars): + make_vars = make_vars or {} + + formatted_vars = [ + f'{name}={val}' + for name, val in sorted(make_vars.items()) + if ( + val is not None + # For some reason Kbuild does not appreciate CC=gcc, even though + # it's happy with CC=clang + and (name, val) != ('CC', 'gcc') + ) + ] + + nr_cpus = int(os.cpu_count() * 1.5) + + cmd = ['make', f'-j{nr_cpus}', '-C', path, '--', *formatted_vars, *targets] + + var_cc = make_vars.get('CC', cc) + if var_cc != cc: + pretty_cmd = ' '.join(map(quote, map(str, cmd))) + raise ValueError(f'The kernel tree was prepared using CC={cc} so the make command cannot be ran with CC={var_cc}: {pretty_cmd}') + + return cmd + + @destroyablecontextmanager -def _make_chroot(make_vars, bind_paths=None, alpine_version='3.17.3', overlay_backend=None): +def _make_chroot(cc, make_vars, bind_paths=None, alpine_version='3.18.0', overlay_backend=None): """ Create a chroot folder ready to be used to build a kernel. """ @@ -246,59 +272,54 @@ def _make_chroot(make_vars, bind_paths=None, alpine_version='3.17.3', overlay_ba 'bison', 'flex', 'python3', - - # TODO: As of october 2021 for some reason, the kernel still needs GCC - # to build some tools even when compiling with clang - 'gcc', ] make_vars = make_vars or {} - try: - cc = make_vars['CC'] - except KeyError: - cc = 'gcc' - - if cc == 'clang': + is_clang = cc.startswith('clang') + if is_clang: packages.extend([ 'lld', 'llvm', ]) packages.append(cc) - devlib_arch = make_vars.get('ARCH', LISA_HOST_ABI) + target_arch = make_vars.get('ARCH', LISA_HOST_ABI) use_qemu = ( - devlib_arch != LISA_HOST_ABI and # Since clang binaries support cross compilation without issues, # there is no need to use QEMU that will slow everything down. - make_vars.get('CC') != 'clang' + (not is_clang) and + target_arch != LISA_HOST_ABI ) - qemu_arch = { - 'arm64': 'aarch64', - 'armeabi': 'arm', - 'armv7': 'arm', - }.get(devlib_arch, devlib_arch) - binfmt_path = Path('/proc/sys/fs/binfmt_misc/', f'qemu-{qemu_arch}') - if use_qemu and not binfmt_path.exists(): - raise ValueError(f'Alpine chroot is setup for {qemu_arch} architecture but QEMU userspace emulation is not installed on the host (missing {binfmt_path})') + chroot_arch = target_arch if use_qemu else LISA_HOST_ABI + qemu_msg = ' using QEMU userspace emulation' if use_qemu else '' + logger.debug(f'Using Alpine v{alpine_version} chroot with architecture {chroot_arch}{qemu_msg}.') + + # Check that QEMU userspace emulation is setup if we need it if use_qemu: - chroot_arch = devlib_arch - else: - chroot_arch = LISA_HOST_ABI + qemu_arch = { + 'arm64': 'aarch64', + 'armeabi': 'arm', + 'armv7': 'arm', + }.get(chroot_arch, chroot_arch) + binfmt_path = Path('/proc/sys/fs/binfmt_misc/', f'qemu-{qemu_arch}') + if not binfmt_path.exists(): + raise ValueError(f'Alpine chroot is setup for {qemu_arch} architecture but QEMU userspace emulation is not installed on the host (missing {binfmt_path})') - alpine_arch = { - 'arm64': 'aarch64', - 'armeabi': 'armv7', - }.get(chroot_arch, chroot_arch) # Add LISA static binaries inside the chroot bind_paths = { **dict(bind_paths or {}), - str((Path(ASSETS_PATH) / 'binaries' / devlib_arch).resolve()): '/usr/local/bin/' + str((Path(ASSETS_PATH) / 'binaries' / chroot_arch).resolve()): '/usr/local/bin/' } + alpine_arch = { + 'arm64': 'aarch64', + 'armeabi': 'armv7', + }.get(chroot_arch, chroot_arch) + dir_cache = DirCache( category='alpine_chroot', populate=populate, @@ -641,6 +662,10 @@ class KernelTree(Loggable, SerializeViaConstructor): prepared kernel tree. :type path_cm: collections.abc.Callable + :param cc: Compiler used to prepare the kernel tree. Can be e.g. ``"gcc"``, + ``"clang"``, ``"clang-14"`` etc. + :type cc: str or None + :param make_vars: Variables passed on ``make`` command line when preparing the kernel tree. :type make_vars: dict(str, str) @@ -675,7 +700,7 @@ class KernelTree(Loggable, SerializeViaConstructor): # On top of that, the kernel does not handle clang < 10.0.1 _MIN_CLANG_VERSION = 11 - def __init__(self, path_cm, make_vars, build_env=None, overlay_backend=None): + def __init__(self, path_cm, cc, make_vars, build_env=None, overlay_backend=None): self._make_path_cm = path_cm self.build_env = self._resolve_build_env(build_env) self.make_vars = make_vars or {} @@ -683,6 +708,7 @@ class KernelTree(Loggable, SerializeViaConstructor): self._path_cm = None self.path = None self.checksum = None + self.cc = cc @staticmethod def _resolve_build_env(build_env): @@ -815,19 +841,17 @@ class KernelTree(Loggable, SerializeViaConstructor): @classmethod - def _prepare_tree(cls, path, make_vars, build_env, apply_overlays, overlay_backend): - path = Path(path) + def _prepare_tree(cls, path, cc, make_vars, build_env, apply_overlays, overlay_backend): logger = cls.get_logger() - _make_vars = [ - f'{name}={val}' - for name, val in sorted((make_vars or {}).items()) - if val is not None - ] - nr_cpus = int(os.cpu_count() * 1.5) - + path = Path(path) def make(*targets): - return ['make', f'-j{nr_cpus}', '-C', path, '--', *_make_vars, *targets] + return _kbuild_make_cmd( + path=path, + targets=targets, + cc=cc, + make_vars=make_vars, + ) cmds = [ # On non-host build env, we need to clean first, as binaries compiled @@ -880,7 +904,7 @@ class KernelTree(Loggable, SerializeViaConstructor): if build_env == 'alpine': @contextlib.contextmanager def cmd_cm(cmds): - with _make_chroot(bind_paths=bind_paths, make_vars=make_vars, overlay_backend=overlay_backend) as chroot: + with _make_chroot(cc=cc, bind_paths=bind_paths, make_vars=make_vars, overlay_backend=overlay_backend) as chroot: yield [ _make_chroot_cmd(chroot, cmd) if cmd else None for cmd in cmds @@ -940,7 +964,7 @@ class KernelTree(Loggable, SerializeViaConstructor): make_vars, cc = cls._resolve_toolchain(abi, make_vars, build_env) if build_env == 'alpine': - if cc == 'clang': + if cc.startswith('clang'): make_vars['LLVM'] = '1' else: # Disable CROSS_COMPILE as we are going to build in a "native" @@ -952,6 +976,10 @@ class KernelTree(Loggable, SerializeViaConstructor): if 'KBUILD_MODPOST_WARN' not in make_vars: make_vars['KBUILD_MODPOST_WARN'] = '1' + # Ensure the make vars contain the chosen compiler explicitly. It will + # then be re-filtered right before invoking make to remove CC=gcc as it + # can confuse KBuild. + make_vars['CC'] = cc return (make_vars, cc) @classmethod @@ -1031,8 +1059,9 @@ class KernelTree(Loggable, SerializeViaConstructor): llvm = make_vars['LLVM'] version = llvm if llvm.startswith('-') else '' if cc == 'clang' and version: + cc = cc + version commands = { - cc: test_cmd(cc + version), + cc: test_cmd(cc), } # Only run the check on host build env, as other build envs are @@ -1068,15 +1097,11 @@ class KernelTree(Loggable, SerializeViaConstructor): if toolchain: detected['CROSS_COMPILE'] = toolchain - # For some reason Kbuild does not appreciate CC=gcc, even though - # it's happy with CC=clang - if cc != 'gcc': - detected['CC'] = cc - make_vars = { **detected, **make_vars, } + return (make_vars, cc) @classmethod @@ -1309,6 +1334,7 @@ class KernelTree(Loggable, SerializeViaConstructor): return cls( path_cm=functools.partial(try_loaders, loaders), + cc=cc, make_vars=make_vars, build_env=build_env, overlay_backend=overlay_backend, @@ -1381,6 +1407,7 @@ class KernelTree(Loggable, SerializeViaConstructor): def prepare_overlay(path): cls._prepare_tree( path, + cc=cc, make_vars=make_vars, build_env=build_env, apply_overlays=functools.partial(apply_overlays, path), @@ -1411,6 +1438,7 @@ class KernelTree(Loggable, SerializeViaConstructor): str(tree_key), str(build_env), str(overlay_backend), + str(cc), ] + [ # We need to take checksum the make variables # as well, as it can influence the kernel tree @@ -1495,6 +1523,7 @@ class KernelTree(Loggable, SerializeViaConstructor): cm = chain_cm(overlay_cm, tree_cm) return cls( path_cm=cm, + cc=cc, make_vars=make_vars, build_env=build_env, ) @@ -1620,7 +1649,11 @@ class KmodSrc(Loggable): better to set them when creating the ``kernel_tree``. :type make_vars: dict(str, object) or None """ - make_vars = dict(make_vars or {}) + make_vars = { + **kernel_tree.make_vars, + **(make_vars or {}), + } + cc = kernel_tree.cc overlay_backend = kernel_tree.overlay_backend tree_path = Path(kernel_tree.path) # "inherit" the build env from the KernelTree as we must use the same @@ -1644,22 +1677,22 @@ class KmodSrc(Loggable): f.write(content) def make_cmd(tree_path, mod_path, make_vars): - make_vars = make_vars.copy() - make_vars.update( - M=mod_path, - LISA_KMOD_NAME=self.mod_name, - KERNEL_SRC=tree_path, - MODULE_SRC=mod_path, - MODULE_OBJ=mod_path, + make_vars = { + **make_vars, + **dict( + M=mod_path, + LISA_KMOD_NAME=self.mod_name, + KERNEL_SRC=tree_path, + MODULE_SRC=mod_path, + MODULE_OBJ=mod_path, + ) + } + return _kbuild_make_cmd( + cc=cc, + path=tree_path, + targets=['modules'], + make_vars=make_vars, ) - make_vars = [ - f'{name}={value}' - for name, value in sorted(make_vars.items()) - if value is not None - ] - - nr_cpus = int(os.cpu_count() * 1.5) - cmd = ['make', f'-j{nr_cpus}', '-C', tree_path, *make_vars, 'modules'] return cmd def find_mod_file(path): @@ -1675,7 +1708,7 @@ class KmodSrc(Loggable): if build_env == 'alpine': @contextlib.contextmanager def cmd_cm(): - with _make_chroot(bind_paths=bind_paths, make_vars=make_vars, overlay_backend=overlay_backend) as chroot: + with _make_chroot(cc=cc, bind_paths=bind_paths, make_vars=make_vars, overlay_backend=overlay_backend) as chroot: # Do not use a CM here to avoid choking on permission # issues. Since the chroot itself will be entirely # removed it's not a problem. @@ -1840,7 +1873,6 @@ class DynamicKmod(Loggable): def get_bin(kernel_tree): return src.compile( kernel_tree=kernel_tree, - make_vars=kernel_tree.make_vars, ) def lookup_cache(kernel_tree, key, enter_cm=False): diff --git a/lisa/utils.py b/lisa/utils.py index e371149f1cf7dd9a864f1d46b0f2127d788bbe18..25b3d5b1c24ce69a92ac8bc5a43a4ad3d7c28a2a 100644 --- a/lisa/utils.py +++ b/lisa/utils.py @@ -112,7 +112,18 @@ Base folder used for caching files. RESULT_DIR = 'results' LATEST_LINK = 'results_latest' -LISA_HOST_ABI = platform.machine().lower().replace('aarch64', 'arm64') +def _get_abi(): + machine = platform.machine().lower() + # Match devlib arch names + return dict( + aarch64='arm64', + ).get(machine, machine) + +LISA_HOST_ABI = _get_abi() +""" +ABI of the machine that imported that module. +""" +del _get_abi TASK_COMM_MAX_LEN = 16 - 1 diff --git a/tools/recipes/pahole.manifest.yaml b/tools/recipes/pahole.manifest.yaml index 4a12a717903237023b73302ac507a221ff7b417e..4b5007f839f523abc5d4279f6f818f80441f1706 100644 --- a/tools/recipes/pahole.manifest.yaml +++ b/tools/recipes/pahole.manifest.yaml @@ -22,12 +22,6 @@ rebase-conf: base: master tip: anon_struct - - - name: fix_btf_ptr_size - remote: github - base: master - tip: fix_btf_ptr_size - remotes: github: url: https://github.com/douglas-raillard-arm/pahole.git diff --git a/tools/recipes/pahole.recipe b/tools/recipes/pahole.recipe index 80b9fa045ff10d78d70e6286cb4178ba109e7713..c1b92ceecc4cc96ddd5702e3c1233c50e401abe1 100644 --- a/tools/recipes/pahole.recipe +++ b/tools/recipes/pahole.recipe @@ -1,7 +1,7 @@ #! /bin/bash -ALPINE_VERSION=v3.14 -ALPINE_BUILD_DEPENDENCIES=(bash gcc git make cmake musl-dev zlib-static bzip2-static libelf-static libbpf-dev musl-obstack-dev argp-standalone linux-headers) +ALPINE_VERSION=v3.18 +ALPINE_BUILD_DEPENDENCIES=(bash gcc git make cmake musl-dev zlib-static bzip2-static libelf-static libbpf-dev musl-obstack-dev argp-standalone linux-headers xz-static zstd-static) BROKEN_CROSS_COMPILATION=1