diff --git a/lisa/assets/__init__.py b/lisa/assets/__init__.py index 58988a6bab9b1460a1af4ae3d8d547c9979cdcad..eff1f20109c8c9cec6e99bde4aad78450fb87fec 100644 --- a/lisa/assets/__init__.py +++ b/lisa/assets/__init__.py @@ -1 +1,62 @@ -# Provide an empty __init__.py so that lisa.assets.__file__ can point to it +#! /USSR/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# +# Copyright (C) 2019, ARM Limited and contributors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import os +import stat +import platform + +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 + +ASSETS_PATH = os.path.dirname(__file__) +""" +Path in which all assets the ``lisa`` package relies on are located in. +""" + +def _get_abi_bin(): + def list_binaries(path): + return { + entry.name: os.path.abspath(entry.path) + for entry in os.scandir(path) + if entry.stat().st_mode & stat.S_IXUSR + } + + bin_path = os.path.join(ASSETS_PATH, 'binaries') + return { + entry.name: list_binaries(entry.path) + for entry in os.scandir(bin_path) + if entry.is_dir() + } + +ABI_BINARIES = _get_abi_bin() +del _get_abi_bin + +HOST_BINARIES = ABI_BINARIES[HOST_ABI] + + +# vim :set tabstop=4 shiftwidth=4 textwidth=80 expandtab diff --git a/lisa/conf.py b/lisa/conf.py index 0969c880972b39af8149c192ddd390e8ac6ea866..564f2d397baf0ac2034f3b7ec181877754113f6b 100644 --- a/lisa/conf.py +++ b/lisa/conf.py @@ -28,6 +28,7 @@ import re import contextlib import pprint import os +import io import lisa @@ -686,6 +687,17 @@ class MultiSrcConfABC(Serializable, abc.ABC, metaclass=MultiSrcConfMeta): """ return self._to_path(self.as_yaml_map, path, fmt='yaml') + def to_yaml_map_str(self, **kwargs): + """ + Return the content of the file that would be create by + :meth:`to_yaml_map` in a string. + + :Variable keyword arguments: Forwarded to :meth:`to_yaml_map` + """ + content = io.StringIO() + self.to_yaml_map(content, **kwargs) + return content.getvalue() + # Only used with Python >= 3.6, but since that is just a sanity check it # should be okay @classmethod diff --git a/lisa/tests/base.py b/lisa/tests/base.py index 646eeea26e40f37b6c3f3b07221352d38b486521..7f3d6bdb6d34d384b75600f825d2912df019ef05 100644 --- a/lisa/tests/base.py +++ b/lisa/tests/base.py @@ -27,6 +27,7 @@ import inspect import copy import contextlib import itertools +import subprocess from datetime import datetime from collections import OrderedDict, ChainMap @@ -54,6 +55,7 @@ from lisa.conf import ( SimpleMultiSrcConf, KeyDesc, TopLevelKeyDesc, StrList, ) +from lisa.assets import HOST_BINARIES def _nested_formatter(multiline): @@ -408,9 +410,8 @@ class TestBundleMeta(abc.ABCMeta): stub. The annotation is then removed from ``_from_target`` so that it is not picked up by exekall. - The signature of ``from_target`` is the result of merging - ``super().from_target`` parameters with the ones defined in - ``_from_target``. + The signature of ``from_target`` is the result of merging the original + ``cls.from_target`` parameters with the ones defined in ``_from_target``. """ @staticmethod def test_method(func): @@ -462,10 +463,11 @@ class TestBundleMeta(abc.ABCMeta): f = metacls.test_method(f) setattr(new_cls, name, f) - # If that class defines _from_target but not from_target, we create a - # stub from_target and move the annotations of _from_target to - # from_target - if '_from_target' in dct and 'from_target' not in dct: + # If that class defines _from_target, stub from_target and move the + # annotations of _from_target to from_target. If from_target was + # already defined on that class, it's wrapped by the stub, otherwise + # super().from_target is used. + if '_from_target' in dct: assert isinstance(dct['_from_target'], classmethod) _from_target = new_cls._from_target @@ -519,10 +521,20 @@ class TestBundleMeta(abc.ABCMeta): return_annotation=sig2.return_annotation ) + if 'from_target' in dct: + # Bind the classmethod object to the class + orig_from_target = dct['from_target'] + def get_orig_from_target(cls): + return orig_from_target.__get__(cls, cls) + else: + def get_orig_from_target(cls): + return super(new_cls, cls).from_target + # Make a stub that we can freely update @functools.wraps(_from_target.__func__) def from_target(cls, *args, **kwargs): - return super(new_cls, cls).from_target(*args, **kwargs) + from_target = get_orig_from_target(cls) + return from_target(*args, **kwargs) # Hide the fact that we wrapped the function, so exekall does not # get confused @@ -1463,6 +1475,39 @@ class RTATestBundle(FtraceTestBundle, DmesgTestBundle): """ return cls.run_rtapp(*args, **kwargs) + @classmethod + def from_target(cls, target: Target, *, res_dir: ArtifactPath = None, **kwargs): + test_bundle = super().from_target(target=target, res_dir=res_dir, **kwargs) + + # Crop the trace around the area we are interested in to reduce its size + trace = test_bundle.get_trace( + events=test_bundle.trace_window.used_events.get_all_events(), + # Keep absolute timestamps that we can give to trace-cmd + normalize_time=False, + ) + window = test_bundle.trace_window(trace) + margin = (0.5, 0.1) + window = ( + max(window[0] - margin[0], 0), + window[1] + margin[1], + ) + trace_path = trace.trace_path + split_path = '{}.split'.format(trace_path) + subprocess.check_call( + [ + HOST_BINARIES['trace-cmd'], 'split', + '-i', trace_path, + '-o', split_path, + *map(str, window), + ], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + actual_split_path = '{}.1'.format(split_path) + os.rename(actual_split_path, trace_path) + + return test_bundle + @classmethod def _from_target(cls, target: Target, *, res_dir: ArtifactPath, ftrace_coll: FtraceCollector = None) -> 'RTATestBundle': """ diff --git a/lisa/trace.py b/lisa/trace.py index a1adeb8733c228550ac6366a7cc7128077aeb90e..c8a8776a1346b0fa28c976cc61decd5659e03a53 100644 --- a/lisa/trace.py +++ b/lisa/trace.py @@ -33,7 +33,7 @@ import shlex import contextlib import tempfile from functools import reduce, wraps -from collections.abc import Sequence +from collections.abc import Iterable from collections import namedtuple from operator import itemgetter @@ -451,10 +451,10 @@ class Trace(Loggable, TraceBase): events = sorted(proxy_cls.get_all_events()) elif isinstance(events, str): events = [events] - elif isinstance(events, Sequence): + elif isinstance(events, Iterable): events = list(events) else: - raise ValueError('Events must be a string or a sequence of strings') + raise ValueError('Events must be a string or an iterable of strings') # Register devlib fake cpu_frequency events if 'cpu_frequency' in events: @@ -1334,7 +1334,7 @@ class AssociativeTraceEventChecker(TraceEventCheckerBase): Build an instance of the class, converting ``str`` to ``TraceEventChecker``. - :param events: Sequence of events + :param events: Iterable of events :type events: list(str or TraceEventCheckerBase) """ def make_event(e): diff --git a/lisa/utils.py b/lisa/utils.py index 8c4eb0b89a30aaed6ed6f97ff1fe61a7cec39c60..ab305ea12e0f4ea34c8d6fa25474dcddae25468c 100644 --- a/lisa/utils.py +++ b/lisa/utils.py @@ -59,6 +59,7 @@ except ImportError: import lisa import lisa.assets from lisa.version import version_tuple, parse_version, format_version +from lisa.assets import ASSETS_PATH # Do not infer the value using __file__, since it will break later on when @@ -69,11 +70,6 @@ LISA_HOME = os.getenv('LISA_HOME') The detected location of your LISA installation """ -ASSETS_PATH = os.path.dirname(lisa.assets.__file__) -""" -Path in which all assets the ``lisa`` package relies on are located in. -""" - RESULT_DIR = 'results' LATEST_LINK = 'results_latest' diff --git a/lisa/wlgen/rta.py b/lisa/wlgen/rta.py index 2fbd3ded7a43f7b1903e74ae8180a8033119eaed..66ce6d5a24f542f19b74969628b1a3a2995a9c16 100644 --- a/lisa/wlgen/rta.py +++ b/lisa/wlgen/rta.py @@ -480,7 +480,16 @@ class RTA(Workload): pload[cpu] = int(pload_match.group(1)) logger.debug('>>> CPU{}: {}'.format(cpu, pload[cpu])) - logger.info('Target RT-App calibration: {}'.format(pload)) + # Avoid circular import issue + from lisa.platforms.platinfo import PlatformInfo + snippet_plat_info = PlatformInfo({ + 'rtapp': { + 'calib': pload, + }, + }) + logger.info('Platform info rt-app calibration configuration:\n{}'.format( + snippet_plat_info.to_yaml_map_str() + )) plat_info = target.plat_info diff --git a/tools/lisa-conf-cat b/tools/lisa-conf-cat index 449fea32272c04944af81280c26dfbfa893269fb..d6ec4e838d8ffb54b7bed6ce28f4438060f8626a 100755 --- a/tools/lisa-conf-cat +++ b/tools/lisa-conf-cat @@ -24,9 +24,8 @@ def main(): fragments = {} for conf_cls, conf in sorted(conf_map.items(), key=key): - content = io.StringIO() - conf.to_yaml_map(content) - fragments[conf_cls.__qualname__] = content.getvalue() + content = conf.to_yaml_map_str() + fragments[conf_cls.__qualname__] = content print('\n'.join( '\n{}'.format(content)