diff --git a/ipynb/energy/EnergyModel_ClusterEnergy.ipynb b/ipynb/energy/EnergyModel_ClusterEnergy.ipynb index bece7fe95aac167cd1f71dae6fe4ff7a2042358b..ec05faa9a73df00d1e50a98726192bf244e211f8 100644 --- a/ipynb/energy/EnergyModel_ClusterEnergy.ipynb +++ b/ipynb/energy/EnergyModel_ClusterEnergy.ipynb @@ -178,7 +178,7 @@ "source": [ "# Initialize a test environment using:\n", "# the provided target configuration (my_conf)\n", - "te = TestEnv(target_conf=my_conf, force_new=True)\n", + "te = TestEnv(target_conf=my_conf)\n", "target = te.target" ] }, diff --git a/ipynb/energy/EnergyModel_SystemEnergy.ipynb b/ipynb/energy/EnergyModel_SystemEnergy.ipynb index c813f097ba13c8eea41211973da00317ef3b7ea8..65b76e92d1fd08d1ebaf57ca9e50389dd6216ba8 100644 --- a/ipynb/energy/EnergyModel_SystemEnergy.ipynb +++ b/ipynb/energy/EnergyModel_SystemEnergy.ipynb @@ -207,7 +207,7 @@ "source": [ "# Initialize a test environment using:\n", "# the provided target configuration (my_conf)\n", - "te = TestEnv(target_conf=my_conf, force_new=True)\n", + "te = TestEnv(target_conf=my_conf)\n", "target = te.target" ] }, diff --git a/ipynb/examples/devlib/cgroups_example.ipynb b/ipynb/examples/devlib/cgroups_example.ipynb index 1e1dbb43fefc3d636fa1e10dec1939940e691817..fd2f5ecb91aed6daf1648577a2d3b16f85e3f46d 100644 --- a/ipynb/examples/devlib/cgroups_example.ipynb +++ b/ipynb/examples/devlib/cgroups_example.ipynb @@ -202,7 +202,7 @@ " }\n", "}\n", "\n", - "te = TestEnv(my_conf, force_new=True)\n", + "te = TestEnv(my_conf)\n", "target = te.target\n", "\n", "# Report target connection\n", diff --git a/ipynb/examples/energy_meter/EnergyMeter_ACME.ipynb b/ipynb/examples/energy_meter/EnergyMeter_ACME.ipynb index 01291a2b6be8d3fef458b0a11008684d9dfd165d..ea4f9d12338a1d9061e107f170252aa54d4079ee 100644 --- a/ipynb/examples/energy_meter/EnergyMeter_ACME.ipynb +++ b/ipynb/examples/energy_meter/EnergyMeter_ACME.ipynb @@ -159,7 +159,7 @@ ], "source": [ "# Initialize a test environment using:\n", - "te = TestEnv(my_conf, wipe=False, force_new=True)\n", + "te = TestEnv(my_conf, wipe=False)\n", "target = te.target" ] }, diff --git a/ipynb/examples/energy_meter/EnergyMeter_AEP.ipynb b/ipynb/examples/energy_meter/EnergyMeter_AEP.ipynb index f71839e5640eeabe2031e75d30a777dc60fa4139..b3fb909e06e0f4f99c7e8049401540a2f5e43403 100644 --- a/ipynb/examples/energy_meter/EnergyMeter_AEP.ipynb +++ b/ipynb/examples/energy_meter/EnergyMeter_AEP.ipynb @@ -158,7 +158,7 @@ ], "source": [ "# Initialize a test environment using:\n", - "te = TestEnv(my_conf, wipe=False, force_new=True)\n", + "te = TestEnv(my_conf, wipe=False)\n", "target = te.target" ] }, diff --git a/ipynb/examples/energy_meter/EnergyMeter_HWMON.ipynb b/ipynb/examples/energy_meter/EnergyMeter_HWMON.ipynb index 2357662fbb6f61e0a4364d8ae8422123286020b6..c99048631cef7c488ac05aa9db8aab5c0c8b09d5 100644 --- a/ipynb/examples/energy_meter/EnergyMeter_HWMON.ipynb +++ b/ipynb/examples/energy_meter/EnergyMeter_HWMON.ipynb @@ -158,7 +158,7 @@ ], "source": [ "# Initialize a test environment using:\n", - "te = TestEnv(my_conf, wipe=False, force_new=True)\n", + "te = TestEnv(my_conf, wipe=False)\n", "target = te.target" ] }, diff --git a/ipynb/examples/energy_meter/EnergyMeter_Monsoon.ipynb b/ipynb/examples/energy_meter/EnergyMeter_Monsoon.ipynb index db7568323ca95b2f9e051f7ba1d8e6b741a323f5..b265f9fb36e5731a88d0b7075946cf6c246b3f66 100644 --- a/ipynb/examples/energy_meter/EnergyMeter_Monsoon.ipynb +++ b/ipynb/examples/energy_meter/EnergyMeter_Monsoon.ipynb @@ -191,7 +191,7 @@ ], "source": [ "# Initialize a test environment using:\n", - "te = TestEnv(my_conf, wipe=False, force_new=True)\n", + "te = TestEnv(my_conf, wipe=False)\n", "target = te.target" ] }, diff --git a/ipynb/examples/trace_analysis/TraceAnalysis_FunctionsProfiling.ipynb b/ipynb/examples/trace_analysis/TraceAnalysis_FunctionsProfiling.ipynb index ba6a0721e9c7a10a5f608bf0fe3ce3728ff2ad10..179dbcc14660a3bbf297c8b6280767e9f11006ea 100644 --- a/ipynb/examples/trace_analysis/TraceAnalysis_FunctionsProfiling.ipynb +++ b/ipynb/examples/trace_analysis/TraceAnalysis_FunctionsProfiling.ipynb @@ -169,7 +169,7 @@ ], "source": [ "# Initialize a test environment using:\n", - "te = TestEnv(my_conf, wipe=False, force_new=True)\n", + "te = TestEnv(my_conf, wipe=False)\n", "target = te.target" ] }, diff --git a/ipynb/examples/trace_analysis/TraceAnalysis_IdleStates.ipynb b/ipynb/examples/trace_analysis/TraceAnalysis_IdleStates.ipynb index 3659e6f769babb45fc0bd1e5fba468114e8d8080..6130c215bd01296d568e053ae6ec8e1759ab175d 100644 --- a/ipynb/examples/trace_analysis/TraceAnalysis_IdleStates.ipynb +++ b/ipynb/examples/trace_analysis/TraceAnalysis_IdleStates.ipynb @@ -191,7 +191,7 @@ ], "source": [ "# Initialize a test environment\n", - "te = TestEnv(my_conf, wipe=False, force_new=True)\n", + "te = TestEnv(my_conf, wipe=False)\n", "target = te.target" ] }, diff --git a/ipynb/examples/trace_analysis/TraceAnalysis_TasksLatencies.ipynb b/ipynb/examples/trace_analysis/TraceAnalysis_TasksLatencies.ipynb index 27023db7eb77a59ce02f32471058105035241177..d2987c9e32db3add4fb5417c94eb5c60a76817c7 100644 --- a/ipynb/examples/trace_analysis/TraceAnalysis_TasksLatencies.ipynb +++ b/ipynb/examples/trace_analysis/TraceAnalysis_TasksLatencies.ipynb @@ -197,7 +197,7 @@ ], "source": [ "# Initialize a test environment using:\n", - "te = TestEnv(my_conf, wipe=False, force_new=True)\n", + "te = TestEnv(my_conf, wipe=False)\n", "target = te.target" ] }, diff --git a/ipynb/releases/ReleaseNotes_v16.10.ipynb b/ipynb/releases/ReleaseNotes_v16.10.ipynb index 3e3ece946a74229a762bc915f37a38066d00b755..9f0e2a003de82198ff03c54e0ab5a92033b5053b 100644 --- a/ipynb/releases/ReleaseNotes_v16.10.ipynb +++ b/ipynb/releases/ReleaseNotes_v16.10.ipynb @@ -255,7 +255,7 @@ "source": [ "from env import TestEnv\n", "\n", - "te = TestEnv(my_conf, force_new=True)\n", + "te = TestEnv(my_conf)\n", "target = te.target" ] }, @@ -532,7 +532,7 @@ "source": [ "from env import TestEnv\n", "\n", - "te = TestEnv(my_conf, force_new=True)" + "te = TestEnv(my_conf)" ] }, { @@ -766,7 +766,7 @@ "source": [ "from env import TestEnv\n", "\n", - "te = TestEnv(my_conf, force_new=True)\n", + "te = TestEnv(my_conf)\n", "target = te.target" ] }, @@ -1662,7 +1662,7 @@ "source": [ "from env import TestEnv\n", "\n", - "te = TestEnv(my_conf, force_new=True)\n", + "te = TestEnv(my_conf)\n", "target = te.target" ] }, diff --git a/ipynb/releases/ReleaseNotes_v16.12.ipynb b/ipynb/releases/ReleaseNotes_v16.12.ipynb index b51b300853de120d81bbef9eca65f168ab732539..791644806b9f9c8b2cec4e579b3fc3b6c6a5b71e 100644 --- a/ipynb/releases/ReleaseNotes_v16.12.ipynb +++ b/ipynb/releases/ReleaseNotes_v16.12.ipynb @@ -493,7 +493,7 @@ " 'platform' : 'android',\n", " 'board' : 'pixel',\n", " 'ANDROID_HOME' : '/home/patbel01/Code/lisa/tools/android-sdk-linux/'\n", - " }, force_new=True)\n", + " })\n", "target = te.target" ] }, diff --git a/libs/utils/env.py b/libs/utils/env.py index 438bebe62df846d78947313ac975e2327b6fb41c..b00b5a6d256a64002ebb768a3a945d9b2fc16b0e 100644 --- a/libs/utils/env.py +++ b/libs/utils/env.py @@ -39,35 +39,40 @@ from platforms.juno_r0_energy import juno_r0_energy from platforms.hikey_energy import hikey_energy from platforms.pixel_energy import pixel_energy +# This should be an absolute path pointing to the root of LISA repository. +BASEPATH = os.path.abspath(os.path.join( + os.path.dirname(os.path.realpath(__file__)), '..', '..' +)) + USERNAME_DEFAULT = 'root' PASSWORD_DEFAULT = '' FTRACE_EVENTS_DEFAULT = ['sched:*'] FTRACE_BUFSIZE_DEFAULT = 10240 -OUT_PREFIX = 'results' +DEFAULT_RESULTS_BASE = os.path.abspath(os.path.join(BASEPATH, 'results')) LATEST_LINK = 'results_latest' -basepath = os.path.dirname(os.path.realpath(__file__)) -basepath = basepath.replace('/libs/utils', '') - -class ShareState(object): - __shared_state = {} - - def __init__(self): - self.__dict__ = self.__shared_state -class TestEnv(ShareState): +class TestEnv(object): """ Represents the environment configuring LISA, the target, and the test setup + If LISA_TARGET_SINGLETON environment variable is defined to 1, only the + first TestEnv instance will create a target and the following ones will + reuse the same target object. That avoids reinitializing the target when + starting tests using lisa-test command. + The test environment is defined by: - a target configuration (target_conf) defining which HW platform we want to use to run the experiments - a test configuration (test_conf) defining which SW setups we need on that HW target - - a folder to collect the experiments results, which can be specified using - the target_conf::results_dir option, or using LISA_RESULTS_DIR environment - variable and is by default wiped from all the previous contents + - a folder to collect the experiments results. If a relative path is given + through the test_conf, it will be interpreted relatively to the results + base path. This base path can be set using the LISA_RESULTS_BASE + environment variable or via target_conf::results_base key. Relative + base paths are interpreted relatively to the default base path. + By default, the results directory is wiped from all the previous contents (if wipe=True) :param target_conf: @@ -110,7 +115,10 @@ class TestEnv(ShareState): target. LISA does *not* manage this TFTP server, it must be provided externally. Optional. **results_dir** - location of results of the experiments. + location of results of the experiments. If the path is relative, + it will be created under the results base. If it is an absolute + path, it is used as-is. If no results_dir is provided, a + timestamp-based folder is created under the results base. **ftrace** Ftrace configuration merged with test-specific configuration. Currently, only additional events through "events" key is supported. @@ -150,11 +158,6 @@ class TestEnv(ShareState): :param wipe: set true to cleanup all previous content from the output folder :type wipe: bool - - :param force_new: Create a new TestEnv object even if there is one available - for this session. By default, TestEnv only creates one - object per session, use this to override this behaviour. - :type force_new: bool """ critical_tasks = { @@ -184,24 +187,43 @@ class TestEnv(ShareState): freeze when using freeeze_userspace. """ - _initialized = False + # target class attribute is used as a shared storage between instances + target = None - def __init__(self, target_conf=None, test_conf=None, wipe=True, - force_new=False): - super(TestEnv, self).__init__() + __singleton = (None, None) - if self._initialized and not force_new: - return + @classmethod + def create_once(cls, *args, **kwargs): + """ + Create a new TestEnv instance the first time it is called, and then + always returns it. + + :param args: Parameters passed to the constructor. + :param kwargs: Parameters passed to the constructor. + + """ + + params = (tuple(args), tuple(kwargs.items())) + if cls.__singleton is None: + cls.__singleton = params, cls(*args, **kwargs) + + # We only try to check if the parameters are the same. This check is + # not perfect but should catch some common mistakes. It will not catch + # a change to a JSON file's content for example. + orig_params, singleton = cls.__singleton + if orig_params != params: + raise ValueError( + 'Original instance created using different parameters') + + return singleton + + def __init__(self, target_conf=None, test_conf=None, wipe=True): + super(TestEnv, self).__init__() self.conf = {} self.test_conf = {} - self.target = None self.ftrace = None self.workdir = None - self.__installed_tools = set() - self.__modules = [] - self.__connection_settings = None - self._calib = None # Keep track of target IP and MAC address self.ip = None @@ -227,7 +249,7 @@ class TestEnv(ShareState): self._log = logging.getLogger('TestEnv') # Compute base installation path - self._log.info('Using base path: %s', basepath) + self._log.info('Using base path: %s', BASEPATH) # Setup target configuration if isinstance(target_conf, dict): @@ -263,7 +285,7 @@ class TestEnv(ShareState): # Initialize binary tools to deploy test_conf_tools = self.test_conf.get('tools', []) target_conf_tools = self.conf.get('tools', []) - self.__tools = list(set(test_conf_tools + target_conf_tools)) + self._tools = list(set(test_conf_tools + target_conf_tools)) # Initialize ftrace events # test configuration override target one @@ -277,28 +299,46 @@ class TestEnv(ShareState): ) self.conf['ftrace'] = ftrace if ftrace['events']: - self.__tools.append('trace-cmd') + self._tools.append('trace-cmd') # Initialize features if '__features__' not in self.conf: self.conf['__features__'] = [] - # Initialize local results folder. - # The test configuration overrides the target's one and the environment - # variable overrides everything else. - self.res_dir = ( - os.getenv('LISA_RESULTS_DIR') or - self.conf.get('results_dir') + # Base directory under which results directories are stored. The + # default location can be overriden by defining LISA_RESULTS_BASE + # environment variable and from target config. + res_base = ( + os.getenv('LISA_RESULTS_BASE') or + self.conf.get('results_base') or + DEFAULT_RESULTS_BASE ) - # Default result dir based on the current time - if not self.res_dir: - self.res_dir = datetime.now().strftime( - os.path.join(basepath, OUT_PREFIX, '%Y%m%d_%H%M%S') + if not os.path.isabs(res_base): + res_base = os.path.join(DEFAULT_RESULTS_BASE, res_base) + + # Initialize local results folder. + res_dir = ( + # Setting it from the target config is sloppy but it avoids a + # compatibility break with previous behavior. That should work as + # long as only one TestEnv is created, for example in a notebook. + self.conf.get('results_dir') or + # This should be set to the tested class name so that the results + # folder can be linked easily to the class that created the + # TestEnv. + self.test_conf.get('results_dir') or + # If no results dir is given by the test configuration, we default + # to a timestamp-based name. This is mostly useful when a TestEnv + # is created from a notebook. Automated tests are expected to set + # results_dir to . + datetime.now().strftime('%Y%m%d_%H%M%S') ) - # Relative paths are interpreted as relative to a fixed root. - if not os.path.isabs(self.res_dir): - self.res_dir = os.path.join(basepath, OUT_PREFIX, self.res_dir) + # A TestEnv instantiated in a notebook may specify an absolute path + # which must be honored. + if os.path.isabs(res_dir): + self.res_dir = res_dir + else: + self.res_dir = os.path.join(res_base, res_dir) if wipe and os.path.exists(self.res_dir): self._log.warning('Wipe previous contents of the results folder:') @@ -307,7 +347,7 @@ class TestEnv(ShareState): if not os.path.exists(self.res_dir): os.makedirs(self.res_dir) - res_lnk = os.path.join(basepath, LATEST_LINK) + res_lnk = os.path.join(BASEPATH, LATEST_LINK) if os.path.islink(res_lnk): os.remove(res_lnk) os.symlink(self.res_dir, res_lnk) @@ -315,20 +355,35 @@ class TestEnv(ShareState): self._init() # Initialize FTrace events collection - self._init_ftrace(True) + self._init_ftrace() # Initialize RT-App calibration values - self.calibration() + # If the target is already existing and calibrated, we skip it + self.calibration(force=False) # Initialize energy probe instrument - self._init_energy(True) + self._init_energy() self._log.info('Set results folder to:') self._log.info(' %s', self.res_dir) self._log.info('Experiment results available also in:') self._log.info(' %s', res_lnk) - self._initialized = True + @property + def _target_state(self): + """ + Monkey patch devlib Target to store some LISA-specific data. + + That allows storing all target-specific state in the target object that + can then be shared across multiple TestEnv instances. _lisa_state + member is unlikely to be used by devlib code so this hack should be + harmless. + """ + try: + return self.target._lisa_state + except AttributeError: + self.target._lisa_state = dict() + return self.target._lisa_state def loadTargetConfig(self, filepath=None): """ @@ -344,14 +399,14 @@ class TestEnv(ShareState): filepath = filepath or 'target.config' # Loading default target configuration - conf_file = os.path.join(basepath, filepath) + conf_file = os.path.join(BASEPATH, filepath) self._log.info('Loading target configuration [%s]...', conf_file) conf = JsonConf(conf_file) conf.load() return conf.json - def _init(self, force = False): + def _init(self, force=True): # Initialize target self._init_target(force) @@ -387,30 +442,26 @@ class TestEnv(ShareState): self._init_platform() - def _init_target(self, force = False): - - if not force and self.target is not None: - return self.target - - self.__connection_settings = {} + def _init_target(self, force=True): + connection_settings = {} # Configure username if 'username' in self.conf: - self.__connection_settings['username'] = self.conf['username'] + connection_settings['username'] = self.conf['username'] else: - self.__connection_settings['username'] = USERNAME_DEFAULT + connection_settings['username'] = USERNAME_DEFAULT # Configure password or SSH keyfile if 'keyfile' in self.conf: - self.__connection_settings['keyfile'] = self.conf['keyfile'] + connection_settings['keyfile'] = self.conf['keyfile'] elif 'password' in self.conf: - self.__connection_settings['password'] = self.conf['password'] + connection_settings['password'] = self.conf['password'] else: - self.__connection_settings['password'] = PASSWORD_DEFAULT + connection_settings['password'] = PASSWORD_DEFAULT # Configure port if 'port' in self.conf: - self.__connection_settings['port'] = self.conf['port'] + connection_settings['port'] = self.conf['port'] # Configure the host IP/MAC address if 'host' in self.conf: @@ -419,7 +470,7 @@ class TestEnv(ShareState): (self.mac, self.ip) = self.resolv_host(self.conf['host']) else: self.ip = self.conf['host'] - self.__connection_settings['host'] = self.ip + connection_settings['host'] = self.ip except KeyError: raise ValueError('Config error: missing [host] parameter') @@ -458,7 +509,7 @@ class TestEnv(ShareState): # Setup board default if not specified by configuration self.nrg_model = None platform = None - self.__modules = ['cpufreq', 'cpuidle'] + modules = ['cpufreq', 'cpuidle'] if 'board' not in self.conf: self.conf['board'] = 'UNKNOWN' @@ -467,12 +518,12 @@ class TestEnv(ShareState): # Initialize TC2 board if board_name == 'TC2': platform = devlib.platform.arm.TC2() - self.__modules = ['bl', 'hwmon', 'cpufreq'] + modules = ['bl', 'hwmon', 'cpufreq'] # Initialize JUNO board elif board_name in ('JUNO', 'JUNO2'): platform = devlib.platform.arm.Juno() - self.__modules = ['bl', 'hwmon', 'cpufreq'] + modules = ['bl', 'hwmon', 'cpufreq'] if board_name == 'JUNO': self.nrg_model = juno_r0_energy @@ -480,28 +531,28 @@ class TestEnv(ShareState): # Initialize OAK board elif board_name == 'OAK': platform = Platform(model='MT8173') - self.__modules = ['bl', 'cpufreq'] + modules = ['bl', 'cpufreq'] # Initialized HiKey board elif board_name == 'HIKEY': self.nrg_model = hikey_energy - self.__modules = [ "cpufreq", "cpuidle" ] + modules = [ "cpufreq", "cpuidle" ] platform = Platform(model='hikey') # Initialize HiKey960 board elif board_name == 'HIKEY960': - self.__modules = ['bl', 'cpufreq', 'cpuidle'] + modules = ['bl', 'cpufreq', 'cpuidle'] platform = Platform(model='hikey960') # Initialize Pixel phone elif board_name == 'PIXEL': self.nrg_model = pixel_energy - self.__modules = ['bl', 'cpufreq'] + modules = ['bl', 'cpufreq'] platform = Platform(model='pixel') # Initialize gem5 platform elif board_name == 'GEM5': - self.__modules=['cpufreq'] + modules=['cpufreq'] platform = self._init_target_gem5() elif board_name != 'UNKNOWN': @@ -516,13 +567,13 @@ class TestEnv(ShareState): big_core=board.get('big_core', None) ) if 'modules' in board: - self.__modules = board['modules'] + modules = board['modules'] ######################################################################## # Modules configuration ######################################################################## - modules = set(self.__modules) + modules = set(modules) # Refine modules list based on target.conf modules.update(self.conf.get('modules', [])) @@ -533,8 +584,8 @@ class TestEnv(ShareState): self.test_conf.get('exclude_modules', [])) modules.difference_update(remove_modules) - self.__modules = list(modules) - self._log.info('Devlib modules to load: %s', self.__modules) + modules = list(modules) + self._log.info('Devlib modules to load: %s', modules) ######################################################################## # Devlib target setup (based on target.config::platform) @@ -542,7 +593,7 @@ class TestEnv(ShareState): # If the target is Android, we need just (eventually) the device if platform_type.lower() == 'android': - self.__connection_settings = None + connection_settings = None device = 'DEFAULT' # Workaround for ARM-software/devlib#225 @@ -551,70 +602,79 @@ class TestEnv(ShareState): if 'device' in self.conf: device = self.conf['device'] - self.__connection_settings = {'device' : device} + connection_settings = {'device' : device} elif 'host' in self.conf: host = self.conf['host'] port = '5555' if 'port' in self.conf: port = str(self.conf['port']) device = '{}:{}'.format(host, port) - self.__connection_settings = {'device' : device} + connection_settings = {'device' : device} self._log.info('Connecting Android target [%s]', device) else: self._log.info('Connecting %s target:', platform_type) - for key in self.__connection_settings: + for key in connection_settings: self._log.info('%10s : %s', key, - self.__connection_settings[key]) + connection_settings[key]) self._log.info('Connection settings:') - self._log.info(' %s', self.__connection_settings) - - if platform_type.lower() == 'linux': - self._log.debug('Setup LINUX target...') - if "host" not in self.__connection_settings: - raise ValueError('Missing "host" param in Linux target conf') - - self.target = devlib.LinuxTarget( - platform = platform, - connection_settings = self.__connection_settings, - working_directory = self.workdir, - load_default_modules = False, - modules = self.__modules) - elif platform_type.lower() == 'android': - self._log.debug('Setup ANDROID target...') - self.target = devlib.AndroidTarget( - platform = platform, - connection_settings = self.__connection_settings, - working_directory = self.workdir, - load_default_modules = False, - modules = self.__modules) - elif platform_type.lower() == 'host': - self._log.debug('Setup HOST target...') - self.target = devlib.LocalLinuxTarget( - platform = platform, - working_directory = '/tmp/devlib-target', - executables_directory = '/tmp/devlib-target/bin', - load_default_modules = False, - modules = self.__modules, - connection_settings = {'unrooted': True}) - else: - raise ValueError('Config error: not supported [platform] type {}'\ - .format(platform_type)) + self._log.info(' %s', connection_settings) + + # If a target does not already exist or if we want to recreate the + # target every time + if self.target is None or not int(os.getenv('LISA_TARGET_SINGLETON',0)): + if platform_type.lower() == 'linux': + self._log.debug('Setup LINUX target...') + if "host" not in connection_settings: + raise ValueError('Missing "host" param in Linux target conf') + + self.target = devlib.LinuxTarget( + platform = platform, + connection_settings = connection_settings, + working_directory = self.workdir, + load_default_modules = False, + modules = modules) + elif platform_type.lower() == 'android': + self._log.debug('Setup ANDROID target...') + self.target = devlib.AndroidTarget( + platform = platform, + connection_settings = connection_settings, + working_directory = self.workdir, + load_default_modules = False, + modules = modules) + elif platform_type.lower() == 'host': + self._log.debug('Setup HOST target...') + self.target = devlib.LocalLinuxTarget( + platform = platform, + working_directory = '/tmp/devlib-target', + executables_directory = '/tmp/devlib-target/bin', + load_default_modules = False, + modules = modules, + connection_settings = {'unrooted': True}) + else: + raise ValueError('Config error: not supported [platform] type {}'\ + .format(platform_type)) + + self._log.debug('Checking target connection...') + self._log.debug('Target info:') + self._log.debug(' ABI: %s', self.target.abi) + self._log.debug(' CPUs: %s', self.target.cpuinfo) + self._log.debug(' Clusters: %s', self.target.core_clusters) - self._log.debug('Checking target connection...') - self._log.debug('Target info:') - self._log.debug(' ABI: %s', self.target.abi) - self._log.debug(' CPUs: %s', self.target.cpuinfo) - self._log.debug(' Clusters: %s', self.target.core_clusters) + self._log.info('Initializing target workdir:') + self._log.info(' %s', self.target.working_directory) - self._log.info('Initializing target workdir:') - self._log.info(' %s', self.target.working_directory) + self.target.setup() + self._target_state['installed_tools'] = set() - self.target.setup() - self.install_tools(self.__tools) + # Store the target in the class attribute so they will be available + # if target reinint is skipped + type(self).target = self.target + + self.install_tools(self._tools) # Verify that all the required modules have been initialized - for module in self.__modules: + for module in modules: self._log.debug('Check for module [%s]...', module) if not hasattr(self.target, module): self._log.warning('Unable to initialize [%s] module', module) @@ -624,11 +684,20 @@ class TestEnv(ShareState): 'update your kernel or test configurations'.format(module)) if not self.nrg_model: - try: - self._log.info('Attempting to read energy model from target') - self.nrg_model = EnergyModel.from_target(self.target) - except (TargetError, RuntimeError, ValueError) as e: - self._log.error("Couldn't read target energy model: %s", e) + # If the target already has an energy model attached, we just use + # that to avoid spending time re-fetching it. + target_em = self._target_state.get('nrg_model') + if target_em: + self._log.info('Using energy model already fetched from target') + self.nrg_model = target_em + else: + try: + self._log.info('Attempting to read energy model from target') + self.nrg_model = EnergyModel.from_target(self.target) + except (TargetError, RuntimeError, ValueError) as e: + self._log.error("Couldn't read target energy model: %s", e) + + self._target_state['nrg_model'] = self.nrg_model def _init_target_gem5(self): system = self.conf['gem5']['system'] @@ -695,25 +764,25 @@ class TestEnv(ShareState): tools.update(['taskset', 'trace-cmd', 'perf', 'cgroup_run_into.sh']) # Remove duplicates and already-instaled tools - tools.difference_update(self.__installed_tools) + tools.difference_update(self._target_state['installed_tools']) tools_to_install = [] for tool in tools: - binary = '{}/tools/scripts/{}'.format(basepath, tool) + binary = '{}/tools/scripts/{}'.format(BASEPATH, tool) if not os.path.isfile(binary): binary = '{}/tools/{}/{}'\ - .format(basepath, self.target.abi, tool) + .format(BASEPATH, self.target.abi, tool) tools_to_install.append(binary) for tool_to_install in tools_to_install: self.target.install(tool_to_install) - self.__installed_tools.update(tools) + self._target_state['installed_tools'].update(tools) def ftrace_conf(self, conf): self._init_ftrace(True, conf) - def _init_ftrace(self, force=False, conf=None): + def _init_ftrace(self, force=True, conf=None): if not force and self.ftrace is not None: return self.ftrace @@ -750,7 +819,7 @@ class TestEnv(ShareState): return self.ftrace - def _init_energy(self, force): + def _init_energy(self, force=True): # Initialize energy probe to board default self.emeter = EnergyMeter.getInstance(self.target, self.conf, force, @@ -795,7 +864,7 @@ class TestEnv(ShareState): self.platform['cpus_count'] = len(self.target.core_clusters) def _load_em(self, board): - em_path = os.path.join(basepath, + em_path = os.path.join(BASEPATH, 'libs/utils/platforms', board.lower() + '.json') self._log.debug('Trying to load default EM from %s', em_path) if not os.path.exists(em_path): @@ -809,7 +878,7 @@ class TestEnv(ShareState): return board.json['nrg_model'] def _load_board(self, board): - board_path = os.path.join(basepath, + board_path = os.path.join(BASEPATH, 'libs/utils/platforms', board.lower() + '.json') self._log.debug('Trying to load board descriptor from %s', board_path) if not os.path.exists(board_path): @@ -884,10 +953,11 @@ class TestEnv(ShareState): force=False and we have not installed rt-app. """ - if not force and self._calib: - return self._calib + if not force and self._target_state.get('calib'): + return self._target_state['calib'] - required = force or 'rt-app' in self.__installed_tools + + required = force or 'rt-app' in self._target_state['installed_tools'] if not required: self._log.debug('No RT-App workloads, skipping calibration') @@ -895,19 +965,21 @@ class TestEnv(ShareState): if not force and 'rtapp-calib' in self.conf: self._log.info('Using configuration provided RTApp calibration') - self._calib = { + calib = { int(key): int(value) for key, value in self.conf['rtapp-calib'].items() } else: self._log.info('Calibrating RTApp...') - self._calib = RTA.calibrate(self.target) + calib = RTA.calibrate(self.target) self._log.info('Using RT-App calibration values:') self._log.info(' %s', - "{" + ", ".join('"%r": %r' % (key, self._calib[key]) - for key in sorted(self._calib)) + "}") - return self._calib + "{" + ", ".join('"%r": %r' % (key, calib[key]) + for key in sorted(calib)) + "}") + + self._target_state['calib'] = calib + return calib def resolv_host(self, host=None): """ diff --git a/libs/utils/test.py b/libs/utils/test.py index d3c30928bee49b37ff98046e9af68c8df18ffc17..7e741ee034757f5708b447fa51b176b9d72e260e 100644 --- a/libs/utils/test.py +++ b/libs/utils/test.py @@ -15,6 +15,7 @@ # limitations under the License. # +import copy import os import unittest import logging @@ -63,7 +64,15 @@ class LisaTest(unittest.TestCase): def _getTestConf(cls): if cls.test_conf is None: raise NotImplementedError("Override `test_conf` attribute") - return cls.test_conf + # Shallow copy of the test conf so we can update its results_dir + # without modifying it for all classes that may share the same test_conf + # object (through their base class for example). + test_conf = copy.copy(cls.test_conf) + # Using a qualified name avoids clashes if the same class name is used + # by different modules. + test_name = cls.__module__ + '.' + cls.__name__ + test_conf.setdefault('results_dir', test_name) + return test_conf @classmethod def _getExperimentsConf(cls, test_env): diff --git a/src/shell/lisa_shell b/src/shell/lisa_shell index bc6a4f528ebbc3a37e1592d4e6d5e99e543fa021..69238cf1296b950a09f0013052bbb3e2f466c294 100755 --- a/src/shell/lisa_shell +++ b/src/shell/lisa_shell @@ -296,8 +296,7 @@ cat <