mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-10-30 06:34:13 +00:00 
			
		
		
		
	New target description + moving target stuff under "framework"
Changing the way target descriptions work from a static mapping to something that is dynamically generated and is extensible via plugins. Also moving core target implementation stuff under "framework".
This commit is contained in:
		
							
								
								
									
										0
									
								
								wa/framework/target/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								wa/framework/target/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										20
									
								
								wa/framework/target/config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								wa/framework/target/config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| from copy import copy | ||||
|  | ||||
| #Not going to be used for now. | ||||
|  | ||||
| class TargetConfig(dict): | ||||
|     """ | ||||
|     Represents a configuration for a target. | ||||
|  | ||||
|     """ | ||||
|     def __init__(self, config=None): | ||||
|         if isinstance(config, TargetConfig): | ||||
|             self.__dict__ = copy(config.__dict__) | ||||
|         elif hasattr(config, 'iteritems'): | ||||
|             for k, v in config.iteritems: | ||||
|                 self.set(k, v) | ||||
|         elif config: | ||||
|             raise ValueError(config) | ||||
|  | ||||
|     def set(self, name, value): | ||||
|         setattr(self, name, value) | ||||
							
								
								
									
										252
									
								
								wa/framework/target/descriptor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								wa/framework/target/descriptor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,252 @@ | ||||
| from collections import OrderedDict | ||||
| from copy import copy | ||||
|  | ||||
| from devlib import (LinuxTarget, AndroidTarget, LocalLinuxTarget, | ||||
|                     Platform, Juno, TC2, Gem5SimulationPlatform) | ||||
|  | ||||
| from wa.framework import pluginloader | ||||
| from wa.framework.exception import PluginLoaderError | ||||
| from wa.framework.plugin import Plugin, Parameter | ||||
| from wa.utils.types import list_of_strings, list_of_ints | ||||
|  | ||||
|  | ||||
| def get_target_descriptions(loader=pluginloader): | ||||
|     targets = {} | ||||
|     for cls in loader.list_target_descriptors(): | ||||
|         descriptor = cls() | ||||
|         for desc in descriptor.get_descriptions(): | ||||
|             if desc.name in targets: | ||||
|                 msg = 'Duplicate target "{}" returned by {} and {}' | ||||
|                 prev_dtor = targets[desc.name].source | ||||
|                 raise PluginLoaderError(msg.format(dsc.name, prev_dtor.name, | ||||
|                                                    descriptor.name)) | ||||
|             targets[desc.name] = desc | ||||
|     return targets.values() | ||||
|  | ||||
|  | ||||
| class TargetDescription(object): | ||||
|  | ||||
|     def __init__(self, name, source, description=None, target=None, platform=None,  | ||||
|                  conn=None, target_params=None, platform_params=None, | ||||
|                  conn_params=None): | ||||
|         self.name = name | ||||
|         self.source = source | ||||
|         self.description = description | ||||
|         self.target = target | ||||
|         self.platform = platform | ||||
|         self.connection = conn | ||||
|         self._set('target_params', target_params) | ||||
|         self._set('platform_params', platform_params) | ||||
|         self._set('conn_params', conn_params) | ||||
|  | ||||
|     def _set(self, attr, vals): | ||||
|         if vals is None: | ||||
|             vals = {} | ||||
|         elif isiterable(vals): | ||||
|             if not hasattr(vals, 'iteritems'): | ||||
|                 vals = {v.name: v for v in vals} | ||||
|         else: | ||||
|             msg = '{} must be iterable; got "{}"' | ||||
|             raise ValueError(msg.format(attr, vals)) | ||||
|         setattr(self, attr, vals) | ||||
|  | ||||
|  | ||||
| class TargetDescriptor(Plugin): | ||||
|  | ||||
|     kind = 'target_descriptor' | ||||
|  | ||||
|     def get_descriptions(self): | ||||
|         return [] | ||||
|  | ||||
|  | ||||
| COMMON_TARGET_PARAMS = [ | ||||
|     Parameter('working_directory', kind=str, | ||||
|               description=''' | ||||
|               On-target working directory that will be used by WA. This  | ||||
|               directory must be writable by the user WA logs in as without | ||||
|               the need for privilege elevation. | ||||
|               '''), | ||||
|     Parameter('executables_directory', kind=str, | ||||
|               description=''' | ||||
|               On-target directory where WA will install its executable | ||||
|               binaries.  This location must allow execution. This location does | ||||
|               *not* need to be writable by unprivileged users or rooted devices | ||||
|               (WA will install with elevated privileges as necessary). | ||||
|               '''), | ||||
|     Parameter('modules', kind=list_of_strings, | ||||
|               description=''' | ||||
|               A list of additional modules to be installed for the target. | ||||
|  | ||||
|               ``devlib`` implements functionality for particular subsystems as | ||||
|               modules.  A number of "default" modules (e.g. for cpufreq | ||||
|               subsystem) are loaded automatically, unless explicitly disabled. | ||||
|               If additional modules need to be loaded, they may be specified | ||||
|               using this parameter. | ||||
|  | ||||
|               Please see ``devlab`` documentation for information on the available | ||||
|               modules. | ||||
|               '''), | ||||
| ] | ||||
|  | ||||
| COMMON_PLATFORM_PARAMS = [ | ||||
|     Parameter('core_names', kind=list_of_strings, | ||||
|               description=''' | ||||
|               List of names of CPU cores in the order that they appear to the | ||||
|               kernel. If not specified, it will be inferred from the platform. | ||||
|               '''), | ||||
|     Parameter('core_clusters', kind=list_of_ints, | ||||
|               description=''' | ||||
|               Cluster mapping corresponding to the cores in ``core_names``. | ||||
|               Cluster indexing starts at ``0``.  If not specified, this will be | ||||
|               inferred from ``core_names`` -- consecutive cores with the same | ||||
|               name will be assumed to share a cluster. | ||||
|               '''), | ||||
|     Parameter('big_core', kind=str, | ||||
|               description=''' | ||||
|               The name of the big cores in a big.LITTLE system. If not | ||||
|               specified, this will be inferred, either from the name (if one of | ||||
|               the names in ``core_names`` matches known big cores), or by | ||||
|               assuming that the last cluster is big. | ||||
|               '''), | ||||
|     Parameter('model', kind=str, | ||||
|               description=''' | ||||
|               Hardware model of the platform. If not specified, an attempt will | ||||
|               be made to read it from target. | ||||
|               '''), | ||||
|     Parameter('modules', kind=list_of_strings, | ||||
|               description=''' | ||||
|               An additional list of modules to be loaded into the target. | ||||
|               '''), | ||||
| ] | ||||
|  | ||||
| VEXPRESS_PLATFORM_PARAMS = [ | ||||
|     Parameter('serial_port', kind=str, | ||||
|               description=''' | ||||
|               The serial device/port on the host for the initial connection to | ||||
|               the target (used for early boot, flashing, etc). | ||||
|               '''), | ||||
|     Parameter('baudrate', kind=int, | ||||
|               description=''' | ||||
|               Baud rate for the serial connection. | ||||
|               '''), | ||||
|     Parameter('vemsd_mount', kind=str, | ||||
|               description=''' | ||||
|               VExpress MicroSD card mount location. This is a MicroSD card in | ||||
|               the VExpress device that is mounted on the host via USB. The card | ||||
|               contains configuration files for the platform and firmware and | ||||
|               kernel images to be flashed. | ||||
|               '''), | ||||
|     Parameter('bootloader', kind=str, | ||||
|               allowed_values=['uefi', 'uefi-shell', 'u-boot', 'bootmon'], | ||||
|               description=''' | ||||
|               Selects the bootloader mechanism used by the board. Depending on | ||||
|               firmware version, a number of possible boot mechanisms may be use. | ||||
|  | ||||
|               Please see ``devlib`` documentation for descriptions. | ||||
|               '''), | ||||
|     Parameter('hard_reset_method', kind=str, | ||||
|               allowed_values=['dtr', 'reboottxt'], | ||||
|               description=''' | ||||
|               There are a couple of ways to reset VersatileExpress board if the | ||||
|               software running on the board becomes unresponsive. Both require | ||||
|               configuration to be enabled (please see ``devlib`` documentation). | ||||
|  | ||||
|               ``dtr``: toggle the DTR line on the serial connection | ||||
|               ``reboottxt``: create ``reboot.txt`` in the root of the VEMSD mount. | ||||
|  | ||||
|               '''), | ||||
| ] | ||||
|  | ||||
| GEM5_PLATFORM_PARAMS = [ | ||||
|     Parameter('host_output_dir', kind=str, mandatory=True, | ||||
|               description=''' | ||||
|               Path on the host where gem5 output (e.g. stats file) will be placed. | ||||
|               '''), | ||||
|     Parameter('gem5_bin', kind=str, mandatory=True, | ||||
|               description=''' | ||||
|               Path to the gem5 binary | ||||
|               '''), | ||||
|     Parameter('gem5_args', kind=str, mandatory=True, | ||||
|               description=''' | ||||
|               Arguments to be passed to the gem5 binary | ||||
|               '''), | ||||
|     Parameter('gem5_virtio', kind=str, mandatory=True, | ||||
|               description=''' | ||||
|               VirtIO device setup arguments to be passed to gem5. VirtIO is used | ||||
|               to transfer files between the simulation and the host. | ||||
|               '''), | ||||
| ] | ||||
|  | ||||
| # name --> (target_class, params_list, defaults) | ||||
| TARGETS = { | ||||
|     'linux': (LinuxTarget, COMMON_TARGET_PARAMS, None), | ||||
|     'android': (AndroidTarget, COMMON_TARGET_PARAMS + | ||||
|                 [Parameter('package_data_directory', kind=str, default='/data/data', | ||||
|                            description=''' | ||||
|                            Directory containing Android data | ||||
|                            '''), | ||||
|                 ], None), | ||||
|     'local': (LocalLinuxTarget, COMMON_TARGET_PARAMS, None), | ||||
| } | ||||
|  | ||||
| # name --> (platform_class, params_list, defaults) | ||||
| PLATFORMS = { | ||||
|     'generic': (Platform, COMMON_PLATFORM_PARAMS, None), | ||||
|     'juno': (Juno, COMMON_PLATFORM_PARAMS + VEXPRESS_PLATFORM_PARAMS, | ||||
|             { | ||||
|                  'vemsd_mount': '/media/JUNO', | ||||
|                  'baudrate': 115200, | ||||
|                  'bootloader': 'u-boot', | ||||
|                  'hard_reset_method': 'dtr', | ||||
|             }), | ||||
|     'tc2': (TC2, COMMON_PLATFORM_PARAMS + VEXPRESS_PLATFORM_PARAMS, | ||||
|             { | ||||
|                  'vemsd_mount': '/media/VEMSD', | ||||
|                  'baudrate': 38400, | ||||
|                  'bootloader': 'bootmon', | ||||
|                  'hard_reset_method': 'reboottxt', | ||||
|             }), | ||||
|     'gem5': (Gem5SimulationPlatform, GEM5_PLATFORM_PARAMS, None), | ||||
| } | ||||
|  | ||||
|  | ||||
| class DefaultTargetDescriptor(TargetDescriptor): | ||||
|  | ||||
|     name = 'devlib_targets' | ||||
|  | ||||
|     description = """ | ||||
|     The default target descriptor that provides descriptions in the form | ||||
|     <platform>_<target>. | ||||
|  | ||||
|     These map directly onto ``Target``\ s and ``Platform``\ s supplied by ``devlib``. | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     def get_descriptions(self): | ||||
|         result = [] | ||||
|         for target_name, target_tuple in TARGETS.iteritems(): | ||||
|             target, target_params = self._get_item(target_tuple) | ||||
|             for platform_name, platform_tuple in PLATFORMS.iteritems(): | ||||
|                 platform, platform_params = self._get_item(platform_tuple) | ||||
|  | ||||
|                 name = '{}_{}'.format(platform_name, target_name) | ||||
|                 td = TargetDescription(name, self) | ||||
|                 td.target = target | ||||
|                 td.platform = platform | ||||
|                 td.target_params = target_params | ||||
|                 td.platform_params = platform_params | ||||
|                 result.append(td) | ||||
|         return result | ||||
|  | ||||
|     def _get_item(self, item_tuple): | ||||
|         cls, params, defaults = item_tuple | ||||
|         if not defaults: | ||||
|             return cls, params | ||||
|  | ||||
|         param_map = OrderedDict((p.name, copy(p)) for p in params) | ||||
|         for name, value in defaults.iteritems(): | ||||
|             if name not in param_map: | ||||
|                 raise ValueError('Unexpected default "{}"'.format(name)) | ||||
|             param_map[name].default = value | ||||
|         return cls, param_map.values() | ||||
|  | ||||
							
								
								
									
										78
									
								
								wa/framework/target/info.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								wa/framework/target/info.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| from devlib import AndroidTarget | ||||
| from devlib.exception import TargetError | ||||
| from devlib.target import KernelConfig, KernelVersion, Cpuinfo | ||||
|  | ||||
|  | ||||
| class TargetInfo(object): | ||||
|  | ||||
|     @staticmethod | ||||
|     def from_pod(pod): | ||||
|         instance = TargetInfo() | ||||
|         instance.target = pod['target'] | ||||
|         instance.abi = pod['abi'] | ||||
|         instance.cpuinfo = Cpuinfo(pod['cpuinfo']) | ||||
|         instance.os = pod['os'] | ||||
|         instance.os_version = pod['os_version'] | ||||
|         instance.abi = pod['abi'] | ||||
|         instance.is_rooted = pod['is_rooted'] | ||||
|         instance.kernel_version = KernelVersion(pod['kernel_release'],  | ||||
|                                                 pod['kernel_version']) | ||||
|         instance.kernel_config = KernelConfig(pod['kernel_config']) | ||||
|  | ||||
|         if pod["target"] == "AndroidTarget": | ||||
|             instance.screen_resolution = pod['screen_resolution'] | ||||
|             instance.prop = pod['prop'] | ||||
|             instance.prop = pod['android_id'] | ||||
|  | ||||
|         return instance | ||||
|  | ||||
|     def __init__(self, target=None): | ||||
|         if target: | ||||
|             self.target = target.__class__.__name__ | ||||
|             self.cpuinfo = target.cpuinfo | ||||
|             self.os = target.os | ||||
|             self.os_version = target.os_version | ||||
|             self.abi = target.abi | ||||
|             self.is_rooted = target.is_rooted | ||||
|             self.kernel_version = target.kernel_version | ||||
|             self.kernel_config = target.config | ||||
|  | ||||
|             if isinstance(target, AndroidTarget): | ||||
|                 self.screen_resolution = target.screen_resolution | ||||
|                 self.prop = target.getprop() | ||||
|                 self.android_id = target.android_id | ||||
|  | ||||
|         else: | ||||
|             self.target = None | ||||
|             self.cpuinfo = None | ||||
|             self.os = None | ||||
|             self.os_version = None | ||||
|             self.abi = None | ||||
|             self.is_rooted = None | ||||
|             self.kernel_version = None | ||||
|             self.kernel_config = None | ||||
|  | ||||
|             if isinstance(target, AndroidTarget): | ||||
|                 self.screen_resolution = None | ||||
|                 self.prop = None | ||||
|                 self.android_id = None | ||||
|  | ||||
|     def to_pod(self): | ||||
|         pod = {} | ||||
|         pod['target'] = self.target | ||||
|         pod['abi'] = self.abi | ||||
|         pod['cpuinfo'] = self.cpuinfo.sections | ||||
|         pod['os'] = self.os | ||||
|         pod['os_version'] = self.os_version | ||||
|         pod['abi'] = self.abi | ||||
|         pod['is_rooted'] = self.is_rooted | ||||
|         pod['kernel_release'] = self.kernel_version.release | ||||
|         pod['kernel_version'] = self.kernel_version.version | ||||
|         pod['kernel_config'] = dict(self.kernel_config.iteritems()) | ||||
|  | ||||
|         if self.target == "AndroidTarget": | ||||
|             pod['screen_resolution'] = self.screen_resolution | ||||
|             pod['prop'] = self.prop | ||||
|             pod['android_id'] = self.android_id | ||||
|  | ||||
|         return pod | ||||
							
								
								
									
										383
									
								
								wa/framework/target/manager.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										383
									
								
								wa/framework/target/manager.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,383 @@ | ||||
| import logging | ||||
| import tempfile | ||||
| import threading | ||||
| import os | ||||
| import time | ||||
| import shutil | ||||
| import sys | ||||
|  | ||||
| from wa.framework import signal | ||||
| from wa.framework.exception import WorkerThreadError, ConfigError | ||||
| from wa.framework.plugin import Parameter | ||||
| from wa.framework.target.info import TargetInfo | ||||
| from wa.framework.target.runtime_config import (SysfileValuesRuntimeConfig, | ||||
|                                                 HotplugRuntimeConfig, | ||||
|                                                 CpufreqRuntimeConfig, | ||||
|                                                 CpuidleRuntimeConfig) | ||||
| from wa.utils.misc import isiterable | ||||
| from wa.utils.serializer import json | ||||
|  | ||||
|  | ||||
| from devlib import LocalLinuxTarget, LinuxTarget, AndroidTarget | ||||
| from devlib.utils.types import identifier | ||||
| # from wa.target.manager import AndroidTargetManager, LinuxTargetManager | ||||
|  | ||||
|  | ||||
| class TargetManager(object): | ||||
|  | ||||
|     name = 'target-manager' | ||||
|  | ||||
|     description = """ | ||||
|     Instanciated the required target and performs configuration and validation | ||||
|     of the device. | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     parameters = [ | ||||
|         Parameter('disconnect', kind=bool, default=False, | ||||
|                   description=""" | ||||
|                   Specifies whether the target should be disconnected from | ||||
|                   at the end of the run. | ||||
|                   """), | ||||
|     ] | ||||
|  | ||||
|     DEVICE_MAPPING = {'test' : {'platform_name':'generic', | ||||
|                                'target_name': 'android'}, | ||||
|                       'other':  {'platform_name':'test', | ||||
|                                 'target_name': 'linux'}, | ||||
|                       } | ||||
|  | ||||
|     runtime_config_cls = [ | ||||
|                             # order matters | ||||
|                             SysfileValuesRuntimeConfig, | ||||
|                             HotplugRuntimeConfig, | ||||
|                             CpufreqRuntimeConfig, | ||||
|                             CpuidleRuntimeConfig, | ||||
|                           ] | ||||
|  | ||||
|     def __init__(self, name, parameters): | ||||
|         self.name = name | ||||
|         self.target = None | ||||
|         self.assistant = None | ||||
|         self.target_name = None | ||||
|         self.platform_name = None | ||||
|         self.parameters = parameters | ||||
|         self.disconnect = parameters.get('disconnect') | ||||
|         self.info = TargetInfo() | ||||
|  | ||||
|         # Determine platform and target based on passed name | ||||
|         self._parse_name() | ||||
|         # Create target | ||||
|         self._get_target() | ||||
|         # Create an assistant to perform target specific configuration | ||||
|         self._get_assistant() | ||||
|  | ||||
|         ### HERE FOR TESTING, WILL BE CALLED EXTERNALLY ### | ||||
|         # Connect to device and retrieve details. | ||||
|         # self.initialize() | ||||
|         # self.add_parameters() | ||||
|         # self.validate_parameters() | ||||
|         # self.set_parameters() | ||||
|  | ||||
|     def initialize(self): | ||||
|         self.runtime_configs = [cls(self.target) for cls in self.runtime_config_cls] | ||||
|         # if self.parameters: | ||||
|         # self.logger.info('Connecting to the device') | ||||
|         with signal.wrap('TARGET_CONNECT'): | ||||
|             self.target.connect() | ||||
|             # self.info.load(self.target) | ||||
|             # info_file = os.path.join(self.context.info_directory, 'target.json') | ||||
|             # with open(info_file, 'w') as wfh: | ||||
|             #     json.dump(self.info.to_pod(), wfh) | ||||
|  | ||||
|     def finalize(self): | ||||
|         # self.logger.info('Disconnecting from the device') | ||||
|         if self.disconnect: | ||||
|             with signal.wrap('TARGET_DISCONNECT'): | ||||
|                 self.target.disconnect() | ||||
|  | ||||
|     def add_parameters(self, parameters=None): | ||||
|         if parameters: | ||||
|             self.parameters = parameters | ||||
|         if not self.parameters: | ||||
|             raise ConfigError('No Configuration Provided') | ||||
|  | ||||
|         for name in self.parameters.keys(): | ||||
|             for cfg in self.runtime_configs: | ||||
|                 # if name in cfg.supported_parameters: | ||||
|                 if any(parameter in name for parameter in cfg.supported_parameters): | ||||
|                     cfg.add(name, self.parameters.pop(name)) | ||||
|  | ||||
|     def validate_parameters(self): | ||||
|         for cfg in self.runtime_configs: | ||||
|             cfg.validate() | ||||
|  | ||||
|     def set_parameters(self): | ||||
|         for cfg in self.runtime_configs: | ||||
|             cfg.set() | ||||
|  | ||||
|     def clear_parameters(self): | ||||
|         for cfg in self.runtime_configs: | ||||
|             cfg.clear() | ||||
|  | ||||
|     def _parse_name(self): | ||||
|         # Try and get platform and target | ||||
|         self.name = identifier(self.name.replace('-', '_')) | ||||
|         if '_' in self.name: | ||||
|             self.platform_name, self.target_name = self.name.split('_', 1) | ||||
|         elif self.name in self.DEVICE_MAPPING: | ||||
|             self.platform_name = self.DEVICE_MAPPING[self.name]['platform_name'] | ||||
|             self.target_name = self.DEVICE_MAPPING[self.name]['target_name'] | ||||
|         else: | ||||
|             raise ConfigError('Unknown Device Specified {}'.format(self.name)) | ||||
|  | ||||
|     def _get_target(self): | ||||
|         # Create a corresponding target and target-assistant | ||||
|         if self.target_name == 'android': | ||||
|             self.target = AndroidTarget() | ||||
|         elif self.target_name == 'linux': | ||||
|             self.target = LinuxTarget()  # pylint: disable=redefined-variable-type | ||||
|         elif self.target_name == 'localLinux': | ||||
|             self.target = LocalLinuxTarget() | ||||
|         else: | ||||
|             raise ConfigError('Unknown Target Specified {}'.format(self.target_name)) | ||||
|  | ||||
|     def _get_assistant(self): | ||||
|         # Create a corresponding target and target-assistant to help with platformy stuff? | ||||
|         if self.target_name == 'android': | ||||
|             self.assistant = AndroidAssistant(self.target) | ||||
|         elif self.target_name in ['linux', 'localLinux']: | ||||
|             self.assistant = LinuxAssistant(self.target)  # pylint: disable=redefined-variable-type | ||||
|         else: | ||||
|             raise ConfigError('Unknown Target Specified {}'.format(self.target_name)) | ||||
|  | ||||
|     # def validate_runtime_parameters(self, parameters): | ||||
|     #     for  name, value in parameters.iteritems(): | ||||
|     #         self.add_parameter(name, value) | ||||
|     #     self.validate_parameters() | ||||
|  | ||||
|     # def set_runtime_parameters(self, parameters): | ||||
|     #     # self.clear() | ||||
|     #     for  name, value in parameters.iteritems(): | ||||
|     #         self.add_parameter(name, value) | ||||
|     #     self.set_parameters() | ||||
|  | ||||
|  | ||||
| class LinuxAssistant(object): | ||||
|  | ||||
|     name = 'linux-assistant' | ||||
|  | ||||
|     description = """ | ||||
|     Performs configuration, instrumentation, etc. during runs on Linux targets. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, target, **kwargs): | ||||
|         self.target = target | ||||
|     # parameters = [ | ||||
|  | ||||
|     #     Parameter('disconnect', kind=bool, default=False, | ||||
|     #               description=""" | ||||
|     #               Specifies whether the target should be disconnected from | ||||
|     #               at the end of the run. | ||||
|     #               """), | ||||
|     # ] | ||||
|  | ||||
|     # runtime_config_cls = [ | ||||
|     #     # order matters | ||||
|     #     SysfileValuesRuntimeConfig, | ||||
|     #     HotplugRuntimeConfig, | ||||
|     #     CpufreqRuntimeConfig, | ||||
|     #     CpuidleRuntimeConfig, | ||||
|     # ] | ||||
|  | ||||
|     # def __init__(self, target, context, **kwargs): | ||||
|     #     # super(LinuxTargetManager, self).__init__(target, context, **kwargs) | ||||
|     #     self.target = target | ||||
|     #     self.context = context | ||||
|     #     self.info = TargetInfo() | ||||
|     #     self.runtime_configs = [cls(target) for cls in self.runtime_config_cls] | ||||
|  | ||||
|     # def __init__(self): | ||||
|     #     # super(LinuxTargetManager, self).__init__(target, context, **kwargs) | ||||
|     #     self.target = target | ||||
|     #     self.info = TargetInfo() | ||||
|     #     self.parameters = parameters | ||||
|  | ||||
|         # self.info = TargetInfo() | ||||
|         # self.runtime_configs = [cls(target) for cls in self.runtime_config_cls] | ||||
|  | ||||
|     # def initialize(self): | ||||
|     #     # self.runtime_configs = [cls(self.target) for cls in self.runtime_config_cls] | ||||
|     #     # if self.parameters: | ||||
|     #     self.logger.info('Connecting to the device') | ||||
|     #     with signal.wrap('TARGET_CONNECT'): | ||||
|     #         self.target.connect() | ||||
|     #         self.info.load(self.target) | ||||
|     #         # info_file = os.path.join(self.context.info_directory, 'target.json') | ||||
|     #         # with open(info_file, 'w') as wfh: | ||||
|     #         #     json.dump(self.info.to_pod(), wfh) | ||||
|  | ||||
|     # def finalize(self, runner): | ||||
|     #     self.logger.info('Disconnecting from the device') | ||||
|     #     if self.disconnect: | ||||
|     #         with signal.wrap('TARGET_DISCONNECT'): | ||||
|     #             self.target.disconnect() | ||||
|  | ||||
|     # def _add_parameters(self): | ||||
|     #     for name, value in self.parameters.iteritems(): | ||||
|     #         self.add_parameter(name, value) | ||||
|  | ||||
|     # def validate_runtime_parameters(self, parameters): | ||||
|     #     self.clear() | ||||
|     #     for  name, value in parameters.iteritems(): | ||||
|     #         self.add_parameter(name, value) | ||||
|     #     self.validate_parameters() | ||||
|  | ||||
|     # def set_runtime_parameters(self, parameters): | ||||
|     #     self.clear() | ||||
|     #     for  name, value in parameters.iteritems(): | ||||
|     #         self.add_parameter(name, value) | ||||
|     #     self.set_parameters() | ||||
|  | ||||
|     # def clear_parameters(self): | ||||
|     #     for cfg in self.runtime_configs: | ||||
|     #         cfg.clear() | ||||
|  | ||||
|     # def add_parameter(self, name, value): | ||||
|     #     for cfg in self.runtime_configs: | ||||
|     #         if name in cfg.supported_parameters: | ||||
|     #             cfg.add(name, value) | ||||
|     #             return | ||||
|     #     raise ConfigError('Unexpected runtime parameter "{}".'.format(name)) | ||||
|  | ||||
|     # def validate_parameters(self): | ||||
|     #     for cfg in self.runtime_configs: | ||||
|     #         cfg.validate() | ||||
|  | ||||
|     # def set_parameters(self): | ||||
|     #     for cfg in self.runtime_configs: | ||||
|     #         cfg.set() | ||||
|  | ||||
|  | ||||
| class AndroidAssistant(LinuxAssistant): | ||||
|  | ||||
|     name = 'android-assistant' | ||||
|     description = """ | ||||
|     Extends ``LinuxTargetManager`` with Android-specific operations. | ||||
|     """ | ||||
|  | ||||
|     parameters = [ | ||||
|         Parameter('logcat_poll_period', kind=int, | ||||
|                   description=""" | ||||
|                   If specified, logcat will cached in a temporary file on the  | ||||
|                   host every ``logcat_poll_period`` seconds. This is useful for | ||||
|                   longer job executions, where on-device logcat buffer may not be  | ||||
|                   big enough to capture output for the entire execution. | ||||
|                   """), | ||||
|     ] | ||||
|  | ||||
|     def __init__(self, target, **kwargs): | ||||
|         super(AndroidAssistant, self).__init__(target) | ||||
|         self.logcat_poll_period = kwargs.get('logcat_poll_period', None) | ||||
|         if self.logcat_poll_period: | ||||
|             self.logcat_poller = LogcatPoller(target, self.logcat_poll_period) | ||||
|         else: | ||||
|             self.logcat_poller = None | ||||
|  | ||||
|     # def __init__(self, target, context, **kwargs): | ||||
|     #     super(AndroidAssistant, self).__init__(target, context, **kwargs) | ||||
|     #     self.logcat_poll_period = kwargs.get('logcat_poll_period', None) | ||||
|     #     if self.logcat_poll_period: | ||||
|     #         self.logcat_poller = LogcatPoller(target, self.logcat_poll_period) | ||||
|     #     else: | ||||
|     #         self.logcat_poller = None | ||||
|  | ||||
|     # def next_job(self, job): | ||||
|     #     super(AndroidAssistant, self).next_job(job) | ||||
|     #     if self.logcat_poller: | ||||
|     #         self.logcat_poller.start() | ||||
|  | ||||
|     # def job_done(self, job): | ||||
|     #     super(AndroidAssistant, self).job_done(job) | ||||
|     #     if self.logcat_poller: | ||||
|     #         self.logcat_poller.stop() | ||||
|     #     outfile = os.path.join(self.context.output_directory, 'logcat.log') | ||||
|     #     self.logger.debug('Dumping logcat to {}'.format(outfile)) | ||||
|     #     self.dump_logcat(outfile) | ||||
|     #     self.clear() | ||||
|  | ||||
|     def dump_logcat(self, outfile): | ||||
|         if self.logcat_poller: | ||||
|             self.logcat_poller.write_log(outfile) | ||||
|         else: | ||||
|             self.target.dump_logcat(outfile) | ||||
|  | ||||
|     def clear_logcat(self): | ||||
|         if self.logcat_poller: | ||||
|             self.logcat_poller.clear_buffer() | ||||
|  | ||||
|  | ||||
| class LogcatPoller(threading.Thread): | ||||
|  | ||||
|     def __init__(self, target, period=60, timeout=30): | ||||
|         super(LogcatPoller, self).__init__() | ||||
|         self.target = target | ||||
|         self.logger = logging.getLogger('logcat') | ||||
|         self.period = period | ||||
|         self.timeout = timeout | ||||
|         self.stop_signal = threading.Event() | ||||
|         self.lock = threading.Lock() | ||||
|         self.buffer_file = tempfile.mktemp() | ||||
|         self.last_poll = 0 | ||||
|         self.daemon = True | ||||
|         self.exc = None | ||||
|  | ||||
|     def start(self): | ||||
|         self.logger.debug('starting polling') | ||||
|         try: | ||||
|             while True: | ||||
|                 if self.stop_signal.is_set(): | ||||
|                     break | ||||
|                 with self.lock: | ||||
|                     current_time = time.time() | ||||
|                     if (current_time - self.last_poll) >= self.period: | ||||
|                         self.poll() | ||||
|                 time.sleep(0.5) | ||||
|         except Exception:  # pylint: disable=W0703 | ||||
|             self.exc = WorkerThreadError(self.name, sys.exc_info()) | ||||
|         self.logger.debug('polling stopped') | ||||
|  | ||||
|     def stop(self): | ||||
|         self.logger.debug('Stopping logcat polling') | ||||
|         self.stop_signal.set() | ||||
|         self.join(self.timeout) | ||||
|         if self.is_alive(): | ||||
|             self.logger.error('Could not join logcat poller thread.') | ||||
|         if self.exc: | ||||
|             raise self.exc  # pylint: disable=E0702 | ||||
|  | ||||
|     def clear_buffer(self): | ||||
|         self.logger.debug('clearing logcat buffer') | ||||
|         with self.lock: | ||||
|             self.target.clear_logcat() | ||||
|             with open(self.buffer_file, 'w') as _:  # NOQA | ||||
|                 pass | ||||
|  | ||||
|     def write_log(self, outfile): | ||||
|         with self.lock: | ||||
|             self.poll() | ||||
|             if os.path.isfile(self.buffer_file): | ||||
|                 shutil.copy(self.buffer_file, outfile) | ||||
|             else:  # there was no logcat trace at this time | ||||
|                 with open(outfile, 'w') as _:  # NOQA | ||||
|                     pass | ||||
|  | ||||
|     def close(self): | ||||
|         self.logger.debug('closing poller') | ||||
|         if os.path.isfile(self.buffer_file): | ||||
|             os.remove(self.buffer_file) | ||||
|  | ||||
|     def poll(self): | ||||
|         self.last_poll = time.time() | ||||
|         self.target.dump_logcat(self.buffer_file, append=True, timeout=self.timeout) | ||||
|         self.target.clear_logcat() | ||||
							
								
								
									
										454
									
								
								wa/framework/target/runtime_config.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										454
									
								
								wa/framework/target/runtime_config.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,454 @@ | ||||
| from collections import defaultdict, OrderedDict | ||||
|  | ||||
| from wa.framework.plugin import Plugin | ||||
| from wa.framework.exception import ConfigError | ||||
|  | ||||
| from devlib.exception import TargetError | ||||
| from devlib.utils.misc import unique | ||||
| from devlib.utils.types import integer | ||||
|  | ||||
|  | ||||
| class RuntimeConfig(Plugin): | ||||
|  | ||||
|     kind = 'runtime-config' | ||||
|  | ||||
|     parameters = [ | ||||
|     ] | ||||
|  | ||||
| # class RuntimeConfig(object): | ||||
|  | ||||
|     @property | ||||
|     def supported_parameters(self): | ||||
|         raise NotImplementedError() | ||||
|  | ||||
|     @property | ||||
|     def core_names(self): | ||||
|         return unique(self.target.core_names) | ||||
|  | ||||
|     def __init__(self, target): | ||||
|         super(RuntimeConfig, self).__init__() | ||||
|         self.target = target | ||||
|  | ||||
|     def initialize(self, context): | ||||
|         pass | ||||
|  | ||||
|     def add(self, name, value): | ||||
|         raise NotImplementedError() | ||||
|  | ||||
|     def validate(self): | ||||
|         return True | ||||
|  | ||||
|     def set(self): | ||||
|         raise NotImplementedError() | ||||
|  | ||||
|     def clear(self): | ||||
|         raise NotImplementedError() | ||||
|  | ||||
|  | ||||
| class HotplugRuntimeConfig(RuntimeConfig): | ||||
| ##### NOTE: Currently if initialized with cores hotplugged, this will fail when trying to hotplug back in | ||||
|     @property | ||||
|     def supported_parameters(self): | ||||
|         params = ['cores'] | ||||
|         return params | ||||
|  | ||||
|     def __init__(self, target): | ||||
|         super(HotplugRuntimeConfig, self).__init__(target) | ||||
|         self.num_cores = defaultdict(dict) | ||||
|  | ||||
|     def add(self, name, value): | ||||
|         if not self.target.has('hotplug'): | ||||
|             raise TargetError('Target does not support hotplug.') | ||||
|         core, _ = split_parameter_name(name, self.supported_parameters) | ||||
|  | ||||
|         # cpus = cpusFromPrefix(core, self.target) | ||||
|         # core = name.split('_')[0] | ||||
|         value = integer(value) | ||||
|         if core not in self.core_names: | ||||
|             raise ValueError(name) | ||||
|         max_cores = self.core_count(core) | ||||
|         if value > max_cores: | ||||
|             message = 'Cannot set number of {}\'s to {}; max is {}' | ||||
|             raise ValueError(message.format(core, value, max_cores)) | ||||
|         self.num_cores[core] = value | ||||
|         if all(v == 0 for v in self.num_cores.values()): | ||||
|             raise ValueError('Cannot set number of all cores to 0') | ||||
|  | ||||
|     def set(self): | ||||
|         for c, n in reversed(sorted(self.num_cores.iteritems(), | ||||
|                                     key=lambda x: x[1])): | ||||
|             self.set_num_online_cpus(c, n) | ||||
|  | ||||
|     def clear(self): | ||||
|         self.num_cores = defaultdict(dict) | ||||
|  | ||||
|     def set_num_online_cpus(self, core, number): | ||||
|         indexes = [i for i, c in enumerate(self.target.core_names) if c == core] | ||||
|         self.target.hotplug.online(*indexes[:number]) | ||||
|         self.target.hotplug.offline(*indexes[number:]) | ||||
|  | ||||
|     def core_count(self, core): | ||||
|         return sum(1 for c in self.target.core_names if c == core) | ||||
|  | ||||
|  | ||||
| class SysfileValuesRuntimeConfig(RuntimeConfig): | ||||
|  | ||||
|     @property | ||||
|     def supported_parameters(self): | ||||
|         return ['sysfile_values'] | ||||
|  | ||||
|     def __init__(self, target): | ||||
|         super(SysfileValuesRuntimeConfig, self).__init__(target) | ||||
|         self.sysfile_values = OrderedDict() | ||||
|  | ||||
|     def add(self, name, value): | ||||
|         for f, v in value.iteritems(): | ||||
|             if f.endswith('+'): | ||||
|                 f = f[:-1] | ||||
|             elif f.endswith('+!'): | ||||
|                 f = f[:-2] + '!' | ||||
|             else: | ||||
|                 if f.endswith('!'): | ||||
|                     self._check_exists(f[:-1]) | ||||
|                 else: | ||||
|                     self._check_exists(f) | ||||
|             self.sysfile_values[f] = v | ||||
|  | ||||
|     def set(self): | ||||
|         for f, v in self.sysfile_values.iteritems(): | ||||
|             verify = True | ||||
|             if f.endswith('!'): | ||||
|                 verify = False | ||||
|                 f = f[:-1] | ||||
|             self.target.write_value(f, v, verify=verify) | ||||
|  | ||||
|     def clear(self): | ||||
|         self.sysfile_values = OrderedDict() | ||||
|  | ||||
|     def _check_exists(self, path): | ||||
|         if not self.target.file_exists(path): | ||||
|             raise ConfigError('Sysfile "{}" does not exist.'.format(path)) | ||||
|  | ||||
|  | ||||
| class CpufreqRuntimeConfig(RuntimeConfig): | ||||
|  | ||||
|     @property | ||||
|     def supported_parameters(self): | ||||
|         params = ['frequency'] | ||||
|         params.extend(['max_frequency']) | ||||
|         params.extend(['min_frequency']) | ||||
|         params.extend(['governor']) | ||||
|         params.extend(['governor_tunables']) | ||||
|  | ||||
|         return params | ||||
|  | ||||
|     def __init__(self, target): | ||||
|         super(CpufreqRuntimeConfig, self).__init__(target) | ||||
|         self.config = defaultdict(dict) | ||||
|         self.supports_userspace = None | ||||
|         self.supported_freqs = {} | ||||
|         self.supported_govenors = {} | ||||
|         self.min_supported_freq = {} | ||||
|         self.max_supported_freq = {} | ||||
|  | ||||
|         for cpu in self.target.list_online_cpus(): | ||||
|             self.supported_freqs[cpu] = self.target.cpufreq.list_frequencies(cpu) or [] | ||||
|             self.supported_govenors[cpu] = self.target.cpufreq.list_governors(cpu) or [] | ||||
|  | ||||
|     def add(self, name, value): | ||||
|         if not self.target.has('cpufreq'): | ||||
|             raise TargetError('Target does not support cpufreq.') | ||||
|  | ||||
|         prefix, parameter = split_parameter_name(name, self.supported_parameters) | ||||
|         # Get list of valid cpus for a given prefix. | ||||
|         cpus = uniqueDomainCpusFromPrefix(prefix, self.target) | ||||
|  | ||||
|         for cpu in cpus: | ||||
|             # if cpu not in self.target.list_online_cpus(): | ||||
|             #     message = 'Unexpected core name "{}"; must be in {}' | ||||
|             #     raise ConfigError(message.format(core, self.core_names)) | ||||
|             # try: | ||||
|             #     cpu = self.target.list_online_cpus(core)[0] | ||||
|             # except IndexError: | ||||
|             #     message = 'Cannot retrieve frequencies for {} as no CPUs are online.' | ||||
|             #     raise TargetError(message.format(core)) | ||||
|             if parameter.endswith('frequency'): | ||||
|                 try: | ||||
|                     value = integer(value) | ||||
|                 except ValueError: | ||||
|                     if value.upper() == 'MAX': | ||||
|                         value = self.supported_freqs[cpu][-1] | ||||
|                     elif value.upper() == 'MIN': | ||||
|                         value = self.supported_freqs[cpu][0] | ||||
|                     else: | ||||
|                         msg = 'Invalid value {} specified for {}' | ||||
|                         raise ConfigError(msg.format(value, parameter)) | ||||
|             self.config[cpu][parameter] = value | ||||
|  | ||||
|     def set(self): | ||||
|         for cpu in self.config: | ||||
|             config = self.config[cpu] | ||||
|             if config.get('governor'): | ||||
|                 self.configure_governor(cpu, | ||||
|                                         config.get('governor'), | ||||
|                                         config.get('governor_tunables')) | ||||
|             self.configure_frequency(cpu, | ||||
|                                      config.get('frequency'), | ||||
|                                      config.get('min_frequency'), | ||||
|                                      config.get('max_frequency')) | ||||
|  | ||||
|     def clear(self): | ||||
|         self.config = defaultdict(dict) | ||||
|  | ||||
|     def validate(self): | ||||
|         for cpu in self.config: | ||||
|             if cpu not in self.target.list_online_cpus(): | ||||
|                 message = 'Cannot configure frequencies for {} as no CPUs are online.' | ||||
|                 raise TargetError(message.format(cpu)) | ||||
|  | ||||
|             config = self.config[cpu] | ||||
|             minf = config.get('min_frequency') | ||||
|             maxf = config.get('max_frequency') | ||||
|             freq = config.get('frequency') | ||||
|             governor = config.get('governor') | ||||
|             governor_tunables = config.get('governor_tunables') | ||||
|  | ||||
|             if maxf and minf > maxf: | ||||
|                 message = '{}: min_frequency ({}) cannot be greater than max_frequency ({})' | ||||
|                 raise ConfigError(message.format(cpu, minf, maxf)) | ||||
|             if maxf and freq > maxf: | ||||
|                 message = '{}: cpu frequency ({}) cannot be greater than max_frequency ({})' | ||||
|                 raise ConfigError(message.format(cpu, freq, maxf)) | ||||
|             if freq and minf > freq: | ||||
|                 message = '{}: min_frequency ({}) cannot be greater than cpu frequency ({})' | ||||
|                 raise ConfigError(message.format(cpu, minf, freq)) | ||||
|  | ||||
|             # Check that either userspace governor is available or min and max do not differ to frequency | ||||
|             if 'userspace' not in self.supported_govenors[cpu]: | ||||
|                 self.supports_userspace = False | ||||
|                 if minf and minf != freq: | ||||
|                     message = '{}: "userspace" governor not available, min frequency ({}) cannot be different to frequency {}' | ||||
|                     raise ConfigError(message.format(cpu, minf, freq)) | ||||
|                 if maxf and maxf != freq: | ||||
|                     message = '{}: "userspace" governor not available, max frequency ({}) cannot be different to frequency {}' | ||||
|                     raise ConfigError(message.format(cpu, maxf, freq)) | ||||
|             else: | ||||
|                 self.supports_userspace = True | ||||
|  | ||||
|             # Check that specified values are available on the cpu | ||||
|             if minf and not minf in self.supported_freqs[cpu]: | ||||
|                 msg = '{}: Minimum frequency {}Hz not available. Must be in {}'.format(cpu, minf, self.supported_freqs[cpu]) | ||||
|                 raise TargetError(msg) | ||||
|             if maxf and not maxf in self.supported_freqs[cpu]: | ||||
|                 msg = '{}: Maximum frequency {}Hz not available. Must be in {}'.format(cpu, maxf, self.supported_freqs[cpu]) | ||||
|                 raise TargetError(msg) | ||||
|             if freq and not freq in self.supported_freqs[cpu]: | ||||
|                 msg = '{}: Frequency {}Hz not available. Must be in {}'.format(cpu, freq, self.supported_freqs[cpu]) | ||||
|                 raise TargetError(msg) | ||||
|             if governor and governor not in self.supported_govenors[cpu]: | ||||
|                 raise TargetError('{}: {} governor not available'.format(cpu, governor)) | ||||
|             if governor_tunables and not governor: | ||||
|                 raise TargetError('{}: {} governor tunables cannot be provided without a governor'.format(cpu, governor)) | ||||
|  | ||||
|     def configure_frequency(self, cpu, freq=None, min_freq=None, max_freq=None): | ||||
|         if cpu not in self.target.list_online_cpus(): | ||||
|             message = 'Cannot configure frequencies for {} as no CPUs are online.' | ||||
|             raise TargetError(message.format(cpu)) | ||||
|  | ||||
|  | ||||
|         current_min_freq = self.target.cpufreq.get_min_frequency(cpu) | ||||
|         current_freq = self.target.cpufreq.get_frequency(cpu) | ||||
|         current_max_freq = self.target.cpufreq.get_max_frequency(cpu) | ||||
|  | ||||
|         if freq: | ||||
|             # If 'userspace' governor is not available 'spoof' functionality | ||||
|             if not self.supports_userspace: | ||||
|                 min_freq = max_freq = freq | ||||
|             # else:  # Find better alternative for this.   | ||||
|                 # Set min/max frequency if required | ||||
|                 # if not min_freq: | ||||
|                 #     min_freq = self.target.cpufreq.get_min_frequency(cpu) | ||||
|                 # if not max_freq: | ||||
|                 #     max_freq = self.target.cpufreq.get_max_frequency(cpu) | ||||
|  | ||||
|             if freq < current_freq: | ||||
|                 self.target.cpufreq.set_min_frequency(cpu, min_freq) | ||||
|                 if self.supports_userspace: | ||||
|                     self.target.cpufreq.set_frequency(cpu, freq) | ||||
|                 self.target.cpufreq.set_max_frequency(cpu, max_freq) | ||||
|             else: | ||||
|                 self.target.cpufreq.set_max_frequency(cpu, max_freq) | ||||
|                 if self.supports_userspace: | ||||
|                     self.target.cpufreq.set_frequency(cpu, freq) | ||||
|                 self.target.cpufreq.set_min_frequency(cpu, min_freq) | ||||
|             return | ||||
|  | ||||
|         if max_freq: | ||||
|             if max_freq < current_min_freq: | ||||
|                 if min_freq: | ||||
|                     self.target.cpufreq.set_min_frequency(cpu, min_freq) | ||||
|                     self.target.cpufreq.set_max_frequency(cpu, max_freq) | ||||
|                     min_freq_set = True | ||||
|                 else: | ||||
|                     message = '{}: Cannot set max_frequency ({}) below current min frequency ({}).' | ||||
|                     raise TargetError(message.format(cpu, max_freq, current_min_freq)) | ||||
|             else: | ||||
|                 self.target.cpufreq.set_max_frequency(cpu, max_freq) | ||||
|         if min_freq and not min_freq_set: | ||||
|             current_max_freq = max_freq or current_max_freq | ||||
|             if min_freq > current_max_freq: | ||||
|                 message = '{}: Cannot set min_frequency ({}) below current max frequency ({}).' | ||||
|                 raise TargetError(message.format(cpu, max_freq, current_min_freq)) | ||||
|             self.target.cpufreq.set_min_frequency(cpu, min_freq) | ||||
|  | ||||
|  | ||||
|     def configure_governor(self, cpu, governor, governor_tunables=None): | ||||
|         if cpu not in self.target.list_online_cpus(): | ||||
|             message = 'Cannot configure governor for {} as no CPUs are online.' | ||||
|             raise TargetError(message.format(cpu)) | ||||
|  | ||||
|         # for cpu in self.target.list_online_cpus(cpu): #All cpus or only online? | ||||
|         if governor not in self.supported_govenors[cpu]: | ||||
|             raise TargetError('{}: {} governor not available'.format(cpu, governor)) | ||||
|         if governor_tunables: | ||||
|             self.target.cpufreq.set_governor(cpu, governor, **governor_tunables) | ||||
|         else: | ||||
|             self.target.cpufreq.set_governor(cpu, governor) | ||||
|  | ||||
|  | ||||
|  | ||||
| class CpuidleRuntimeConfig(RuntimeConfig): | ||||
|  | ||||
|     @property | ||||
|     def supported_parameters(self): | ||||
|         params = ['idle_states'] | ||||
|         return params | ||||
|  | ||||
|     def __init__(self, target): | ||||
|         super(CpuidleRuntimeConfig, self).__init__(target) | ||||
|         self.config = defaultdict(dict) | ||||
|         self.aliases = ['ENABLE_ALL', 'DISABLE_ALL'] | ||||
|         self.available_states = {} | ||||
|  | ||||
|         for cpu in self.target.list_online_cpus(): | ||||
|             self.available_states[cpu] = self.target.cpuidle.get_states(cpu) or [] | ||||
|  | ||||
|     def add(self, name, values): | ||||
|         if not self.target.has('cpufreq'): | ||||
|             raise TargetError('Target does not support cpufreq.') | ||||
|  | ||||
|         prefix, _ = split_parameter_name(name, self.supported_parameters) | ||||
|         cpus = uniqueDomainCpusFromPrefix(prefix, self.target) | ||||
|  | ||||
|         for cpu in cpus: | ||||
|             if values in self.aliases: | ||||
|                 self.config[cpu] = [values] | ||||
|             else: | ||||
|                 self.config[cpu] = values | ||||
|  | ||||
|     def validate(self): | ||||
|         for cpu in self.config: | ||||
|             if cpu not in self.target.list_online_cpus(): | ||||
|                 message = 'Cannot configure idle states for {} as no CPUs are online.' | ||||
|                 raise TargetError(message.format(cpu)) | ||||
|             for state in self.config[cpu]: | ||||
|                 state = state[1:] if state.startswith('~') else state | ||||
|                 # self.available_states.extend(self.aliases) | ||||
|                 if state not in self.available_states[cpu] + self.aliases: | ||||
|                     message = 'Unexpected idle state "{}"; must be in {}' | ||||
|                     raise ConfigError(message.format(state, self.available_states)) | ||||
|  | ||||
|     def clear(self): | ||||
|         self.config = defaultdict(dict) | ||||
|  | ||||
|     def set(self): | ||||
|         for cpu in self.config: | ||||
|             for state in self.config[cpu]: | ||||
|                 self.configure_idle_state(state, cpu) | ||||
|  | ||||
|     def configure_idle_state(self, state, cpu=None): | ||||
|         if cpu is not None: | ||||
|             if cpu not in self.target.list_online_cpus(): | ||||
|                 message = 'Cannot configure idle state for {} as no CPUs are online {}.' | ||||
|                 raise TargetError(message.format(self.target.core_names[cpu], self.target.list_online_cpus())) | ||||
|         else: | ||||
|             cpu = 0 | ||||
|  | ||||
|         # Check for aliases | ||||
|         if state == 'ENABLE_ALL': | ||||
|             self.target.cpuidle.enable_all(cpu) | ||||
|         elif state == 'DISABLE_ALL': | ||||
|             self.target.cpuidle.disable_all(cpu) | ||||
|         elif state.startswith('~'): | ||||
|             self.target.cpuidle.disable(state[1:], cpu) | ||||
|         else: | ||||
|             self.target.cpuidle.enable(state, cpu) | ||||
|  | ||||
| # TO BE MOVED TO UTILS FILE | ||||
|  | ||||
| import re | ||||
| # Function to return the cpu prefix without the trailing underscore if | ||||
| # present from a given list of parameters, and its matching parameter | ||||
| def split_parameter_name(name, params): | ||||
|     for param in sorted(params, key=len)[::-1]: # Try matching longest parameter first | ||||
|         if len(name.split(param)) > 1: | ||||
|             prefix, _ = name.split(param) | ||||
|             return prefix[:-1], param | ||||
|     message = 'Cannot split {}, must in the form [core_]parameter' | ||||
|     raise ConfigError(message.format(name)) | ||||
|  | ||||
| def cpusFromPrefix(prefix, target): | ||||
|  | ||||
|     # Deal with big little substitution | ||||
|     if prefix.lower() == 'big': | ||||
|         prefix = target.big_core | ||||
|         if not prefix: | ||||
|             raise ConfigError('big core name could not be retrieved') | ||||
|     elif prefix.lower() == 'little': | ||||
|         prefix = target.little_core | ||||
|         if not prefix: | ||||
|             raise ConfigError('little core name could not be retrieved') | ||||
|  | ||||
|     cpu_list = target.list_online_cpus() + target.list_offline_cpus() | ||||
|  | ||||
|     # Apply to all cpus | ||||
|     if not prefix: | ||||
|         cpus = cpu_list | ||||
|  | ||||
|     # Return all cores with specified name | ||||
|     elif prefix in target.core_names: | ||||
|         cpus = target.core_cpus(prefix) | ||||
|  | ||||
|     # Check if core number has been supplied. | ||||
|     else: | ||||
|         # core_no = prefix[4] | ||||
|         core_no = re.match('cpu([0-9]+)', prefix, re.IGNORECASE) | ||||
|         if core_no: | ||||
|             cpus = [int(core_no.group(1))] | ||||
|             if cpus[0] not in cpu_list: | ||||
|                 message = 'CPU{} is not available, must be in {}' | ||||
|                 raise ConfigError(message.format(cpus[0], cpu_list)) | ||||
|  | ||||
|         else: | ||||
|             message = 'Unexpected core name "{}"' | ||||
|             raise ConfigError(message.format(prefix)) | ||||
|     # Should this be applied for everything or just all cpus? | ||||
|     # Make sure not to include any cpus within the same frequency domain | ||||
|     # for cpu in cpus: | ||||
|     #     if cpu not in cpus: # Already removed | ||||
|     #         continue | ||||
|     #     cpus = [c for c in cpus if (c is cpu) or | ||||
|     #                 (c not in target.cpufreq.get_domain_cpus(cpu))] | ||||
|     # print 'Final results ' + str(cpus) | ||||
|     # return cpus | ||||
|     return cpus | ||||
|  | ||||
| # Function to only return cpus list on different frequency domains. | ||||
| def uniqueDomainCpusFromPrefix(prefix, target): | ||||
|     cpus = cpusFromPrefix(prefix, target) | ||||
|     for cpu in cpus: | ||||
|         if cpu not in cpus: # Already removed | ||||
|             continue | ||||
|         cpus = [c for c in cpus if (c is cpu) or | ||||
|                     (c not in target.cpufreq.get_domain_cpus(cpu))] | ||||
|     return cpus | ||||
		Reference in New Issue
	
	Block a user