class Executor(): def __init__(self, target_conf=None, tests_conf=None): """ Tests Executor A tests executor is a module which support the execution of a configured set of experiments. Each experiment is composed by: - a target configuration - a worload to execute The executor module can be configured to run a set of workloads (wloads) in each different target configuration of a specified set (confs). These wloads and confs can be specified by the "tests_config" input dictionary. All the results generated by each experiment will be collected a result folder which is named according to this template: results/<test_id>/<wltype>:<conf>:<wload>/<run_id> where: - <test_id> : the "tid" defined by the tests_config, or a timestamp based folder in case "tid" is not specified - <wltype> : the class of workload executed, e.g. rtapp or sched_perf - <conf> : the identifier of one of the specified configurations - <wload> : the identified of one of the specified workload - <run_id> : the progressive execution number from 1 up to the specified iterations """ # Initialize globals self._default_cgroup = None self._cgroup = None # Setup test configuration if isinstance(tests_conf, dict): logging.info('%14s - Loading custom (inline) test configuration', 'Target') self._tests_conf = tests_conf elif isinstance(tests_conf, str): logging.info('%14s - Loading custom (file) test configuration', 'Target') json_conf = JsonConf(tests_conf) self._tests_conf = json_conf.load() else: raise ValueError('test_conf must be either a dictionary or a filepath') # Check for mandatory configurations if 'confs' not in self._tests_conf or not self._tests_conf['confs']: raise ValueError( 'Configuration error: missing \'conf\' definitions') if 'wloads' not in self._tests_conf or not self._tests_conf['wloads']: raise ValueError( 'Configuration error: missing \'wloads\' definitions') # Setup devlib to access the configured target self.te = TestEnv(target_conf, tests_conf) self.target = self.te.target # Compute total number of experiments self._exp_count = self._tests_conf['iterations'] \ * len(self._tests_conf['wloads']) \ * len(self._tests_conf['confs']) self._print_section('Executor', 'Experiments configuration') logging.info('%14s - Configured to run:', 'Executor') logging.info('%14s - %3d targt configurations:', 'Executor', len(self._tests_conf['confs'])) target_confs = [conf['tag'] for conf in self._tests_conf['confs']] target_confs = ', '.join(target_confs) logging.info('%14s - %s', 'Executor', target_confs) logging.info('%14s - %3d workloads (%d iterations each)', 'Executor', len(self._tests_conf['wloads']), self._tests_conf['iterations']) wload_confs = ', '.join(self._tests_conf['wloads']) logging.info('%14s - %s', 'Executor', wload_confs) logging.info('%14s - Total: %d experiments', 'Executor', self._exp_count) logging.info('%14s - Results will be collected under:', 'Executor') logging.info('%14s - %s', 'Executor', self.te.res_dir) def run(self): self._print_section('Executor', 'Experiments execution') # Run all the configured experiments exp_idx = 1 for tc in self._tests_conf['confs']: # TARGET: configuration if not self._target_configure(tc): continue for wl_idx in self._tests_conf['wloads']: # TEST: configuration wload = self._wload_init(tc, wl_idx) for itr_idx in range(1, self._tests_conf['iterations']+1): # WORKLOAD: execution self._wload_run(exp_idx, tc, wl_idx, wload, itr_idx) exp_idx += 1 self._print_section('Executor', 'Experiments execution completed') logging.info('%14s - Results available in:', 'Executor') logging.info('%14s - %s', 'Executor', self.te.res_dir) ################################################################################ # Target Configuration ################################################################################ def _cgroups_init(self, tc): self._default_cgroup = None if 'cgroups' not in tc: return True if 'cgroups' not in self.target.modules: raise RuntimeError('CGroups module not available. Please ensure ' '"cgroups" is listed in your target/test modules') logging.info(r'%14s - Initialize CGroups support...', 'CGroups') errors = False for kind in tc['cgroups']['conf']: logging.info(r'%14s - Setup [%s] controller...', 'CGroups', kind) controller = self.target.cgroups.controller(kind) if not controller: logging.warning(r'%14s - CGroups controller [%s] NOT available', 'CGroups', kind) errors = True return not errors def _setup_kernel(self, tc): # Deploy kernel on the device self.te.install_kernel(tc, reboot=True) # Setup the rootfs for the experiments self._setup_rootfs(tc) def _setup_sched_features(self, tc): if 'sched_features' not in tc: logging.debug('%14s - Configuration not provided', 'SchedFeatures') return feats = tc['sched_features'].split(",") for feat in feats: logging.info('%14s - Set scheduler feature: %s', 'SchedFeatures', feat) self.target.execute('echo {} > /sys/kernel/debug/sched_features'.format(feat)) def _setup_rootfs(self, tc): # Initialize CGroups if required self._cgroups_init(tc) # Setup target folder for experiments execution self.te.run_dir = os.path.join( self.target.working_directory, TGT_RUN_DIR) # Create run folder as tmpfs logging.debug('%14s - Setup RT-App run folder [%s]...', 'TargetSetup', self.te.run_dir) self.target.execute('[ -d {0} ] || mkdir {0}'\ .format(self.te.run_dir), as_root=True) self.target.execute( 'grep schedtest /proc/mounts || '\ ' mount -t tmpfs -o size=1024m {} {}'\ .format('schedtest', self.te.run_dir), as_root=True) def _setup_cpufreq(self, tc): if 'cpufreq' not in tc: logging.warning(r'%14s - governor not specified, '\ 'using currently configured governor', 'CPUFreq') return cpufreq = tc['cpufreq'] logging.info(r'%14s - Configuring all CPUs to use [%s] governor', 'CPUFreq', cpufreq['governor']) self.target.cpufreq.set_all_governors(cpufreq['governor']) if 'params' in cpufreq: logging.info(r'%14s - governor params: %s', 'CPUFreq', str(cpufreq['params'])) for cpu in self.target.list_online_cpus(): self.target.cpufreq.set_governor_tunables( cpu, cpufreq['governor'], **cpufreq['params']) def _setup_cgroups(self, tc): if 'cgroups' not in tc: return True # Setup default CGroup to run tasks into if 'default' in tc['cgroups']: self._default_cgroup = tc['cgroups']['default'] # Configure each required controller if 'conf' not in tc['cgroups']: return True errors = False for kind in tc['cgroups']['conf']: controller = self.target.cgroups.controller(kind) if not controller: logging.warning(r'%14s - Configuration error: '\ '[%s] contoller NOT supported', 'CGroups', kind) errors = True continue self._setup_controller(tc, controller) return not errors def _setup_controller(self, tc, controller): kind = controller.kind # Configure each required groups for that controller errors = False for name in tc['cgroups']['conf'][controller.kind]: if name[0] != '/': raise ValueError('Wrong CGroup name [{}]. ' 'CGroups names must start by "/".'\ .format(name)) group = controller.cgroup(name) if not group: logging.warning(r'%14s - Configuration error: '\ '[%s/%s] cgroup NOT available', 'CGroups', kind, name) errors = True continue self._setup_group(tc, group) return not errors def _setup_group(self, tc, group): kind = group.controller.kind name = group.name # Configure each required attribute group.set(**tc['cgroups']['conf'][kind][name]) def _target_configure(self, tc): self._print_header('TargetConfig', r'configuring target for [{}] experiments'\ .format(tc['tag'])) self._setup_kernel(tc) self._setup_sched_features(tc) self._setup_cpufreq(tc) return self._setup_cgroups(tc) def _target_conf_flag(self, tc, flag): if 'flags' not in tc: has_flag = False else: has_flag = flag in tc['flags'] logging.debug('%14s - Check if target conf [%s] has flag [%s]: %s', 'TargetConf', tc['tag'], flag, has_flag) return has_flag ################################################################################ # Workload Setup and Execution ################################################################################ def _wload_cpus(self, wl_idx, wlspec): if not 'cpus' in wlspec['conf']: return None cpus = wlspec['conf']['cpus'] if type(cpus) == list: return cpus if type(cpus) == int: return [cpus] # SMP target (or not bL module loaded) if not hasattr(self.target, 'bl'): if 'first' in cpus: return [ self.target.list_online_cpus()[0] ] if 'last' in cpus: return [ self.target.list_online_cpus()[-1] ] return self.target.list_online_cpus() # big.LITTLE target if cpus.startswith('littles'): if 'first' in cpus: return [ self.target.bl.littles_online[0] ] if 'last' in cpus: return [ self.target.bl.littles_online[-1] ] return self.target.bl.littles_online if cpus.startswith('bigs'): if 'first' in cpus: return [ self.target.bl.bigs_online[0] ] if 'last' in cpus: return [ self.target.bl.bigs_online[-1] ] return self.target.bl.bigs_online raise ValueError('Configuration error - ' 'unsupported [{}] \'cpus\' value for [{}] '\ 'workload specification'\ .format(cpus, wl_idx)) def _wload_task_idxs(self, wl_idx, tasks): if type(tasks) == int: return range(tasks) if tasks == 'cpus': return range(len(self.target.core_names)) if tasks == 'little': return range(len([t for t in self.target.core_names if t == self.target.little_core])) if tasks == 'big': return range(len([t for t in self.target.core_names if t == self.target.big_core])) raise ValueError('Configuration error - ' 'unsupported \'tasks\' value for [{}] '\ 'RT-App workload specification'\ .format(wl_idx)) def _wload_rtapp(self, wl_idx, wlspec, cpus): conf = wlspec['conf'] logging.debug(r'%14s - Configuring [%s] rt-app...', 'RTApp', conf['class']) # Setup a default "empty" task name prefix if 'prefix' not in conf: conf['prefix'] = 'task_' # Setup a default loadref CPU loadref = None if 'loadref' in wlspec: loadref = wlspec['loadref'] if conf['class'] == 'profile': params = {} # Load each task specification for task_name in conf['params']: task = conf['params'][task_name] task_name = conf['prefix'] + task_name if task['kind'] not in wlgen.RTA.__dict__: logging.error(r'%14s - RTA task of kind [%s] not supported', 'RTApp', task['kind']) raise ValueError('Configuration error - ' 'unsupported \'kind\' value for task [{}] '\ 'in RT-App workload specification'\ .format(task)) task_ctor = getattr(wlgen.RTA, task['kind']) params[task_name] = task_ctor(**task['params']) rtapp = wlgen.RTA(self.target, wl_idx, calibration = self.te.calibration()) rtapp.conf(kind='profile', params=params, loadref=loadref, cpus=cpus, run_dir=self.te.run_dir) return rtapp if conf['class'] == 'periodic': task_idxs = self._wload_task_idxs(wl_idx, conf['tasks']) params = {} for idx in task_idxs: task = conf['prefix'] + str(idx) params[task] = wlgen.Periodic(**conf['params']).get() rtapp = wlgen.RTA(self.target, wl_idx, calibration = self.te.calibration()) rtapp.conf(kind='profile', params=params, loadref=loadref, cpus=cpus, run_dir=self.te.run_dir) return rtapp if conf['class'] == 'custom': rtapp = wlgen.RTA(self.target, wl_idx, calibration = self.te.calib) rtapp.conf(kind='custom', params=conf['json'], duration=conf['duration'], loadref=loadref, cpus=cpus, run_dir=self.te.run_dir) return rtapp raise ValueError('Configuration error - ' 'unsupported \'class\' value for [{}] '\ 'RT-App workload specification'\ .format(wl_idx)) def _wload_perf_bench(self, wl_idx, wlspec, cpus): conf = wlspec['conf'] logging.debug(r'%14s - Configuring perf_message...', 'PerfMessage') if conf['class'] == 'messaging': perf_bench = wlgen.PerfMessaging(self.target, wl_idx) perf_bench.conf(**conf['params']) return perf_bench if conf['class'] == 'pipe': perf_bench = wlgen.PerfPipe(self.target, wl_idx) perf_bench.conf(**conf['params']) return perf_bench raise ValueError('Configuration error - '\ 'unsupported \'class\' value for [{}] '\ 'perf bench workload specification'\ .format(wl_idx)) def _wload_conf(self, wl_idx, wlspec): # CPUS: setup execution on CPUs if required by configuration cpus = self._wload_cpus(wl_idx, wlspec) # CGroup: setup CGroups if requried by configuration self._cgroup = self._default_cgroup if 'cgroup' in wlspec: if 'cgroups' not in self.target.modules: raise RuntimeError('Target not supporting CGroups or CGroups ' 'not configured for the current test configuration') self._cgroup = wlspec['cgroup'] if wlspec['type'] == 'rt-app': return self._wload_rtapp(wl_idx, wlspec, cpus) if wlspec['type'] == 'perf_bench': return self._wload_perf_bench(wl_idx, wlspec, cpus) raise ValueError('Configuration error - ' 'unsupported \'type\' value for [{}] '\ 'workload specification'\ .format(wl_idx)) def _wload_init(self, tc, wl_idx): tc_idx = tc['tag'] # Configure the test workload wlspec = self._tests_conf['wloads'][wl_idx] wload = self._wload_conf(wl_idx, wlspec) # Keep track of platform configuration self.te.test_dir = '{}/{}:{}:{}'\ .format(self.te.res_dir, wload.wtype, tc_idx, wl_idx) os.system('mkdir -p ' + self.te.test_dir) self.te.platform_dump(self.te.test_dir) # Keep track of kernel configuration and version config = self.target.config with gzip.open(os.path.join(self.te.test_dir, 'kernel.config'), 'wb') as fh: fh.write(config.text) output = self.target.execute('{} uname -a'\ .format(self.target.busybox)) with open(os.path.join(self.te.test_dir, 'kernel.version'), 'w') as fh: fh.write(output) return wload def _wload_run_init(self, run_idx): self.te.out_dir = '{}/{}'\ .format(self.te.test_dir, run_idx) logging.debug(r'%14s - out_dir [%s]', 'Executor', self.te.out_dir) os.system('mkdir -p ' + self.te.out_dir) logging.debug(r'%14s - cleanup target output folder', 'Executor') target_dir = self.target.working_directory logging.debug('%14s - setup target directory [%s]', 'Executor', target_dir) def _wload_run(self, exp_idx, tc, wl_idx, wload, run_idx): tc_idx = tc['tag'] self._print_title('Executor', 'Experiment {}/{}, [{}:{}] {}/{}'\ .format(exp_idx, self._exp_count, tc_idx, wl_idx, run_idx, self._tests_conf['iterations'])) # Setup local results folder self._wload_run_init(run_idx) # FTRACE: start (if a configuration has been provided) if self.te.ftrace and self._target_conf_flag(tc, 'ftrace'): logging.warning('%14s - FTrace events collection enabled', 'Executor') self.te.ftrace.start() # ENERGY: start sampling if self.te.emeter: self.te.emeter.reset() # WORKLOAD: Run the configured workload wload.run(out_dir=self.te.out_dir, cgroup=self._cgroup) # ENERGY: collect measurements if self.te.emeter: self.te.emeter.report(self.te.out_dir) # FTRACE: stop and collect measurements if self.te.ftrace and self._target_conf_flag(tc, 'ftrace'): self.te.ftrace.stop() trace_file = self.te.out_dir + '/trace.dat' self.te.ftrace.get_trace(trace_file) logging.info(r'%14s - Collected FTrace binary trace:', 'Executor') logging.info(r'%14s - %s', 'Executor', trace_file.replace(self.te.res_dir, '<res_dir>')) stats_file = self.te.out_dir + '/trace_stat.json' self.te.ftrace.get_stats(stats_file) logging.info(r'%14s - Collected FTrace function profiling:', 'Executor') logging.info(r'%14s - %s', 'Executor', stats_file.replace(self.te.res_dir, '<res_dir>')) self._print_footer('Executor') ################################################################################ # Utility Functions ################################################################################ def _print_section(self, tag, message): logging.info('') logging.info(FMT_SECTION) logging.info(r'%14s - %s', tag, message) logging.info(FMT_SECTION) def _print_header(self, tag, message): logging.info('') logging.info(FMT_HEADER) logging.info(r'%14s - %s', tag, message) def _print_title(self, tag, message): logging.info(FMT_TITLE) logging.info(r'%14s - %s', tag, message) def _print_footer(self, tag, message=None): if message: logging.info(r'%14s - %s', tag, message) logging.info(FMT_FOOTER)
class Executor(): def __init__(self, target_conf=None, tests_conf=None): """ Tests Executor A tests executor is a module which support the execution of a configured set of experiments. Each experiment is composed by: - a target configuration - a worload to execute The executor module can be configured to run a set of workloads (wloads) in each different target configuration of a specified set (confs). These wloads and confs can be specified by the "tests_config" input dictionary. All the results generated by each experiment will be collected a result folder which is named according to this template: results/<test_id>/<wltype>:<conf>:<wload>/<run_id> where: - <test_id> : the "tid" defined by the tests_config, or a timestamp based folder in case "tid" is not specified - <wltype> : the class of workload executed, e.g. rtapp or sched_perf - <conf> : the identifier of one of the specified configurations - <wload> : the identified of one of the specified workload - <run_id> : the progressive execution number from 1 up to the specified iterations """ # Initialize globals self._default_cgroup = None self._cgroup = None # Setup test configuration if isinstance(tests_conf, dict): logging.info('%14s - Loading custom (inline) test configuration', 'Target') self._tests_conf = tests_conf elif isinstance(tests_conf, str): logging.info('%14s - Loading custom (file) test configuration', 'Target') json_conf = JsonConf(tests_conf) self._tests_conf = json_conf.load() else: raise ValueError('test_conf must be either a dictionary or a filepath') # Check for mandatory configurations if 'confs' not in self._tests_conf or not self._tests_conf['confs']: raise ValueError( 'Configuration error: missing \'conf\' definitions') if 'wloads' not in self._tests_conf or not self._tests_conf['wloads']: raise ValueError( 'Configuration error: missing \'wloads\' definitions') # Setup devlib to access the configured target self.te = TestEnv(target_conf, tests_conf) self.target = self.te.target # Compute total number of experiments self._exp_count = self._tests_conf['iterations'] \ * len(self._tests_conf['wloads']) \ * len(self._tests_conf['confs']) self._print_section('Executor', 'Experiments configuration') logging.info('%14s - Configured to run:', 'Executor') logging.info('%14s - %3d targt configurations:', 'Executor', len(self._tests_conf['confs'])) target_confs = [conf['tag'] for conf in self._tests_conf['confs']] target_confs = ', '.join(target_confs) logging.info('%14s - %s', 'Executor', target_confs) logging.info('%14s - %3d workloads (%d iterations each)', 'Executor', len(self._tests_conf['wloads']), self._tests_conf['iterations']) wload_confs = ', '.join(self._tests_conf['wloads']) logging.info('%14s - %s', 'Executor', wload_confs) logging.info('%14s - Total: %d experiments', 'Executor', self._exp_count) logging.info('%14s - Results will be collected under:', 'Executor') logging.info('%14s - %s', 'Executor', self.te.res_dir) def run(self): self._print_section('Executor', 'Experiments execution') # Run all the configured experiments exp_idx = 1 for tc in self._tests_conf['confs']: # TARGET: configuration if not self._target_configure(tc): continue for wl_idx in self._tests_conf['wloads']: # TEST: configuration wload = self._wload_init(tc, wl_idx) for itr_idx in range(1, self._tests_conf['iterations']+1): # WORKLOAD: execution self._wload_run(exp_idx, tc, wl_idx, wload, itr_idx) exp_idx += 1 self._print_section('Executor', 'Experiments execution completed') logging.info('%14s - Results available in:', 'Executor') logging.info('%14s - %s', 'Executor', self.te.res_dir) ################################################################################ # Target Configuration ################################################################################ def _cgroups_init(self, tc): self._default_cgroup = None if 'cgroups' not in tc: return True if 'cgroups' not in self.target.modules: raise RuntimeError('CGroups module not available. Please ensure ' '"cgroups" is listed in your target/test modules') logging.info(r'%14s - Initialize CGroups support...', 'CGroups') errors = False for kind in tc['cgroups']['conf']: logging.info(r'%14s - Setup [%s] controller...', 'CGroups', kind) controller = self.target.cgroups.controller(kind) if not controller: logging.warning(r'%14s - CGroups controller [%s] NOT available', 'CGroups', kind) errors = True return not errors def _setup_kernel(self, tc): # Deploy kernel on the device self.te.install_kernel(tc, reboot=True) # Setup the rootfs for the experiments self._setup_rootfs(tc) def _setup_sched_features(self, tc): if 'sched_features' not in tc: logging.debug('%14s - Configuration not provided', 'SchedFeatures') return feats = tc['sched_features'].split(",") for feat in feats: logging.info('%14s - Set scheduler feature: %s', 'SchedFeatures', feat) self.target.execute('echo {} > /sys/kernel/debug/sched_features'.format(feat)) def _setup_rootfs(self, tc): # Initialize CGroups if required self._cgroups_init(tc) # Setup target folder for experiments execution self.te.run_dir = os.path.join( self.target.working_directory, TGT_RUN_DIR) # Create run folder as tmpfs logging.debug('%14s - Setup RT-App run folder [%s]...', 'TargetSetup', self.te.run_dir) self.target.execute('[ -d {0} ] || mkdir {0}'\ .format(self.te.run_dir), as_root=True) self.target.execute( 'grep schedtest /proc/mounts || '\ ' mount -t tmpfs -o size=1024m {} {}'\ .format('schedtest', self.te.run_dir), as_root=True) def _setup_cpufreq(self, tc): if 'cpufreq' not in tc: logging.warning(r'%14s - governor not specified, '\ 'using currently configured governor', 'CPUFreq') return cpufreq = tc['cpufreq'] logging.info(r'%14s - Configuring all CPUs to use [%s] governor', 'CPUFreq', cpufreq['governor']) self.target.cpufreq.set_all_governors(cpufreq['governor']) if 'params' in cpufreq: logging.info(r'%14s - governor params: %s', 'CPUFreq', str(cpufreq['params'])) for cpu in self.target.list_online_cpus(): self.target.cpufreq.set_governor_tunables( cpu, cpufreq['governor'], **cpufreq['params']) def _setup_cgroups(self, tc): if 'cgroups' not in tc: return True # Setup default CGroup to run tasks into if 'default' in tc['cgroups']: self._default_cgroup = tc['cgroups']['default'] # Configure each required controller if 'conf' not in tc['cgroups']: return True errors = False for kind in tc['cgroups']['conf']: controller = self.target.cgroups.controller(kind) if not controller: logging.warning(r'%14s - Configuration error: '\ '[%s] contoller NOT supported', 'CGroups', kind) errors = True continue self._setup_controller(tc, controller) return not errors def _setup_controller(self, tc, controller): kind = controller.kind # Configure each required groups for that controller errors = False for name in tc['cgroups']['conf'][controller.kind]: if name[0] != '/': raise ValueError('Wrong CGroup name [{}]. ' 'CGroups names must start by "/".'\ .format(name)) group = controller.cgroup(name) if not group: logging.warning(r'%14s - Configuration error: '\ '[%s/%s] cgroup NOT available', 'CGroups', kind, name) errors = True continue self._setup_group(tc, group) return not errors def _setup_group(self, tc, group): kind = group.controller.kind name = group.name # Configure each required attribute group.set(**tc['cgroups']['conf'][kind][name]) def _target_configure(self, tc): self._print_header('TargetConfig', r'configuring target for [{}] experiments'\ .format(tc['tag'])) self._setup_kernel(tc) self._setup_sched_features(tc) self._setup_cpufreq(tc) return self._setup_cgroups(tc) def _target_conf_flag(self, tc, flag): if 'flags' not in tc: has_flag = False else: has_flag = flag in tc['flags'] logging.debug('%14s - Check if target conf [%s] has flag [%s]: %s', 'TargetConf', tc['tag'], flag, has_flag) return has_flag ################################################################################ # Workload Setup and Execution ################################################################################ def _wload_cpus(self, wl_idx, wlspec): if not 'cpus' in wlspec['conf']: return None cpus = wlspec['conf']['cpus'] if type(cpus) == list: return cpus if type(cpus) == int: return [cpus] # SMP target (or not bL module loaded) if not hasattr(self.target, 'bl'): if 'first' in cpus: return [ self.target.list_online_cpus()[0] ] if 'last' in cpus: return [ self.target.list_online_cpus()[-1] ] return self.target.list_online_cpus() # big.LITTLE target if cpus.startswith('littles'): if 'first' in cpus: return [ self.target.bl.littles_online[0] ] if 'last' in cpus: return [ self.target.bl.littles_online[-1] ] return self.target.bl.littles_online if cpus.startswith('bigs'): if 'first' in cpus: return [ self.target.bl.bigs_online[0] ] if 'last' in cpus: return [ self.target.bl.bigs_online[-1] ] return self.target.bl.bigs_online raise ValueError('Configuration error - ' 'unsupported [{}] \'cpus\' value for [{}] '\ 'workload specification'\ .format(cpus, wl_idx)) def _wload_task_idxs(self, wl_idx, tasks): if type(tasks) == int: return range(tasks) if tasks == 'cpus': return range(len(self.target.core_names)) if tasks == 'little': return range(len([t for t in self.target.core_names if t == self.target.little_core])) if tasks == 'big': return range(len([t for t in self.target.core_names if t == self.target.big_core])) raise ValueError('Configuration error - ' 'unsupported \'tasks\' value for [{}] '\ 'RT-App workload specification'\ .format(wl_idx)) def _wload_rtapp(self, wl_idx, wlspec, cpus): conf = wlspec['conf'] logging.debug(r'%14s - Configuring [%s] rt-app...', 'RTApp', conf['class']) # Setup a default "empty" task name prefix if 'prefix' not in conf: conf['prefix'] = 'task_' # Setup a default loadref CPU loadref = None if 'loadref' in wlspec: loadref = wlspec['loadref'] if conf['class'] == 'profile': params = {} # Load each task specification for task_name in conf['params']: task = conf['params'][task_name] task_name = conf['prefix'] + task_name if task['kind'] not in wlgen.__dict__: logging.error(r'%14s - RTA task of kind [%s] not supported', 'RTApp', task['kind']) raise ValueError('Configuration error - ' 'unsupported \'kind\' value for task [{}] '\ 'in RT-App workload specification'\ .format(task)) task_ctor = getattr(wlgen, task['kind']) params[task_name] = task_ctor(**task['params']).get() rtapp = wlgen.RTA(self.target, wl_idx, calibration = self.te.calibration()) rtapp.conf(kind='profile', params=params, loadref=loadref, cpus=cpus, run_dir=self.te.run_dir) return rtapp if conf['class'] == 'periodic': task_idxs = self._wload_task_idxs(wl_idx, conf['tasks']) params = {} for idx in task_idxs: task = conf['prefix'] + str(idx) params[task] = wlgen.Periodic(**conf['params']).get() rtapp = wlgen.RTA(self.target, wl_idx, calibration = self.te.calibration()) rtapp.conf(kind='profile', params=params, loadref=loadref, cpus=cpus, run_dir=self.te.run_dir) return rtapp if conf['class'] == 'custom': rtapp = wlgen.RTA(self.target, wl_idx, calibration = self.te.calib) rtapp.conf(kind='custom', params=conf['json'], duration=conf['duration'], loadref=loadref, cpus=cpus, run_dir=self.te.run_dir) return rtapp raise ValueError('Configuration error - ' 'unsupported \'class\' value for [{}] '\ 'RT-App workload specification'\ .format(wl_idx)) def _wload_perf_bench(self, wl_idx, wlspec, cpus): conf = wlspec['conf'] logging.debug(r'%14s - Configuring perf_message...', 'PerfMessage') if conf['class'] == 'messaging': perf_bench = wlgen.PerfMessaging(self.target, wl_idx) perf_bench.conf(**conf['params']) return perf_bench if conf['class'] == 'pipe': perf_bench = wlgen.PerfPipe(self.target, wl_idx) perf_bench.conf(**conf['params']) return perf_bench raise ValueError('Configuration error - '\ 'unsupported \'class\' value for [{}] '\ 'perf bench workload specification'\ .format(wl_idx)) def _wload_conf(self, wl_idx, wlspec): # CPUS: setup execution on CPUs if required by configuration cpus = self._wload_cpus(wl_idx, wlspec) # CGroup: setup CGroups if requried by configuration self._cgroup = self._default_cgroup if 'cgroup' in wlspec: if 'cgroups' not in self.target.modules: raise RuntimeError('Target not supporting CGroups or CGroups ' 'not configured for the current test configuration') self._cgroup = wlspec['cgroup'] if wlspec['type'] == 'rt-app': return self._wload_rtapp(wl_idx, wlspec, cpus) if wlspec['type'] == 'perf_bench': return self._wload_perf_bench(wl_idx, wlspec, cpus) raise ValueError('Configuration error - ' 'unsupported \'type\' value for [{}] '\ 'workload specification'\ .format(wl_idx)) def _wload_init(self, tc, wl_idx): tc_idx = tc['tag'] # Configure the test workload wlspec = self._tests_conf['wloads'][wl_idx] wload = self._wload_conf(wl_idx, wlspec) # Keep track of platform configuration self.te.test_dir = '{}/{}:{}:{}'\ .format(self.te.res_dir, wload.wtype, tc_idx, wl_idx) os.system('mkdir -p ' + self.te.test_dir) self.te.platform_dump(self.te.test_dir) # Keep track of kernel configuration and version config = self.target.config with gzip.open(os.path.join(self.te.test_dir, 'kernel.config'), 'wb') as fh: fh.write(config.text) output = self.target.execute('{} uname -a'\ .format(self.target.busybox)) with open(os.path.join(self.te.test_dir, 'kernel.version'), 'w') as fh: fh.write(output) return wload def _wload_run_init(self, run_idx): self.te.out_dir = '{}/{}'\ .format(self.te.test_dir, run_idx) logging.debug(r'%14s - out_dir [%s]', 'Executor', self.te.out_dir) os.system('mkdir -p ' + self.te.out_dir) logging.debug(r'%14s - cleanup target output folder', 'Executor') target_dir = self.target.working_directory logging.debug('%14s - setup target directory [%s]', 'Executor', target_dir) def _wload_run(self, exp_idx, tc, wl_idx, wload, run_idx): tc_idx = tc['tag'] self._print_title('Executor', 'Experiment {}/{}, [{}:{}] {}/{}'\ .format(exp_idx, self._exp_count, tc_idx, wl_idx, run_idx, self._tests_conf['iterations'])) # Setup local results folder self._wload_run_init(run_idx) # FTRACE: start (if a configuration has been provided) if self.te.ftrace and self._target_conf_flag(tc, 'ftrace'): logging.warning('%14s - FTrace events collection enabled', 'Executor') self.te.ftrace.start() # ENERGY: start sampling if self.te.emeter: self.te.emeter.reset() # WORKLOAD: Run the configured workload wload.run(out_dir=self.te.out_dir, cgroup=self._cgroup) # ENERGY: collect measurements if self.te.emeter: self.te.emeter.report(self.te.out_dir) # FTRACE: stop and collect measurements if self.te.ftrace and self._target_conf_flag(tc, 'ftrace'): self.te.ftrace.stop() trace_file = self.te.out_dir + '/trace.dat' self.te.ftrace.get_trace(trace_file) logging.info(r'%14s - Collected FTrace binary trace:', 'Executor') logging.info(r'%14s - %s', 'Executor', trace_file.replace(self.te.res_dir, '<res_dir>')) stats_file = self.te.out_dir + '/trace_stat.json' self.te.ftrace.get_stats(stats_file) logging.info(r'%14s - Collected FTrace function profiling:', 'Executor') logging.info(r'%14s - %s', 'Executor', stats_file.replace(self.te.res_dir, '<res_dir>')) self._print_footer('Executor') ################################################################################ # Utility Functions ################################################################################ def _print_section(self, tag, message): logging.info('') logging.info(FMT_SECTION) logging.info(r'%14s - %s', tag, message) logging.info(FMT_SECTION) def _print_header(self, tag, message): logging.info('') logging.info(FMT_HEADER) logging.info(r'%14s - %s', tag, message) def _print_title(self, tag, message): logging.info(FMT_TITLE) logging.info(r'%14s - %s', tag, message) def _print_footer(self, tag, message=None): if message: logging.info(r'%14s - %s', tag, message) logging.info(FMT_FOOTER)