1# SPDX-License-Identifier: Apache-2.0 2# 3# Copyright (C) 2015, ARM Limited and contributors. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); you may 6# not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18from bart.common.Analyzer import Analyzer 19import collections 20from collections import namedtuple 21import datetime 22import gzip 23import json 24import os 25import re 26import time 27import trappy 28from devlib import TargetError 29 30# Configure logging 31import logging 32 33# Add JSON parsing support 34from conf import JsonConf 35 36import wlgen 37 38from devlib import TargetError 39 40Experiment = namedtuple('Experiment', ['wload_name', 'wload', 41 'conf', 'iteration', 'out_dir']) 42 43class Executor(): 44 """ 45 Abstraction for running sets of experiments and gathering data from targets 46 47 An executor can be configured to run a set of workloads (wloads) in each 48 different target configuration of a specified set (confs). These wloads and 49 confs can be specified by the "experiments_conf" input dictionary. Each 50 (workload, conf, iteration) tuple is called an "experiment". 51 52 After the workloads have been run, the Executor object's `experiments` 53 attribute is a list of Experiment objects. The `out_dir` attribute of these 54 objects can be used to find the results of the experiment. This output 55 directory follows this format: 56 57 results/<test_id>/<wltype>:<conf>:<wload>/<run_id> 58 59 where: 60 61 test_id 62 Is the "tid" defined by the experiments_conf, or a timestamp based 63 folder in case "tid" is not specified. 64 wltype 65 Is the class of workload executed, e.g. rtapp or sched_perf. 66 conf 67 Is the "tag" of one of the specified **confs**. 68 wload 69 Is the identifier of one of the specified **wloads**. 70 run_id 71 Is the progressive execution number from 1 up to the specified 72 **iterations**. 73 74 :param experiments_conf: Dict with experiment configuration. Keys are: 75 76 **confs** 77 Mandatory. Platform configurations to be tested. List of dicts, 78 each with keys: 79 80 tag 81 String to identify this configuration. Required, may be empty. 82 flags 83 List of strings describing features required for this 84 conf. Available flags are: 85 86 "ftrace" 87 Enable collecting ftrace during the experiment. 88 "freeze_userspace" 89 Use the cgroups freezer to freeze as many userspace tasks as 90 possible during the experiment execution, in order to reduce 91 system noise. Some tasks cannot be frozen, such as those 92 required to maintain a connection to LISA. 93 94 sched_features 95 Optional list of features to be written to 96 /sys/kernel/debug/sched_features. Prepend "NO\_" to a feature to 97 actively disable it. Requires ``CONFIG_SCHED_DEBUG`` in target 98 kernel. 99 cpufreq 100 Parameters to configure cpufreq via Devlib's cpufreq 101 module. Dictionary with fields: 102 103 .. TODO link to devlib cpufreq module docs (which don't exist) 104 105 governor 106 cpufreq governor to set (for all CPUs) before execution. The 107 previous governor is not restored when execution is finished. 108 governor_tunables 109 Dictionary of governor-specific tunables, expanded and passed as 110 kwargs to the cpufreq module's ``set_governor_tunables`` method. 111 freq 112 Requires "governor" to be "userspace". Dictionary mapping CPU 113 numbers to frequencies. Exact frequencies should be available on 114 those CPUs. It is not necessary to provide a frequency for every 115 CPU - the frequency for unspecified CPUs is not affected. Note 116 that cpufreq will transparrently set the frequencies of any 117 other CPUs sharing a clock domain. 118 119 cgroups 120 Optional cgroups configuration. To use this, ensure the 'cgroups' 121 devlib module is enabled in your test_conf Contains fields: 122 123 .. TODO reference test_conf 124 .. TODO link to devlib cgroup module's docs (which don't exist) 125 126 conf 127 Dict specifying the cgroup controllers, cgroups, and cgroup 128 parameters to setup. If a controller listed here is not 129 enabled in the target kernel, a message is logged and the 130 configuration is **ignored**. Of the form: 131 132 :: 133 134 "<controller>" : { 135 "<group1>" : { "<group_param" : <value> } 136 "<group2>" : { "<group_param" : <value> } 137 } 138 139 These cgroups can then be used in the "cgroup" field of workload 140 specifications. 141 142 default 143 The default cgroup to run workloads in, if no "cgroup" is 144 specified. 145 146 For example, to create a cpuset cgroup named "/big" which 147 restricts constituent tasks to CPUs 1 and 2: 148 149 :: 150 151 "cgroups" : { 152 "conf" : { 153 "cpuset" : { 154 "/big" : {"cpus" : "1-2"}, 155 } 156 }, 157 "default" : "/", 158 } 159 160 **wloads** 161 .. TODO document wloads field. 162 163 Mandatory. Workloads to run on each platform configuration 164 165 **iterations** 166 Number of iterations for each workload/conf combination. Default 167 is 1. 168 :type experiments_conf: dict 169 """ 170 171 critical_tasks = { 172 'linux': ['init', 'systemd', 'sh', 'ssh'], 173 'android': [ 174 'sh', 'adbd', 175 'usb', 'transport', 176 # We don't actually need this task but on Google Pixel it apparently 177 # cannot be frozen, so the cgroup state gets stuck in FREEZING if we 178 # try to freeze it. 179 'thermal-engine' 180 ] 181 } 182 """ 183 Dictionary mapping OS name to list of task names that we can't afford to 184 freeze when using freeeze_userspace. 185 """ 186 187 def __init__(self, test_env, experiments_conf): 188 # Initialize globals 189 self._default_cgroup = None 190 self._cgroup = None 191 192 # Setup logging 193 self._log = logging.getLogger('Executor') 194 195 # Setup test configuration 196 if isinstance(experiments_conf, dict): 197 self._log.info('Loading custom (inline) test configuration') 198 self._experiments_conf = experiments_conf 199 elif isinstance(experiments_conf, str): 200 self._log.info('Loading custom (file) test configuration') 201 json_conf = JsonConf(experiments_conf) 202 self._experiments_conf = json_conf.load() 203 else: 204 raise ValueError( 205 'experiments_conf must be either a dictionary or a filepath') 206 207 # Check for mandatory configurations 208 if not self._experiments_conf.get('confs', None): 209 raise ValueError('Configuration error: ' 210 'missing "conf" definitions') 211 if not self._experiments_conf.get('wloads', None): 212 raise ValueError('Configuration error: ' 213 'missing "wloads" definitions') 214 215 self.te = test_env 216 self.target = self.te.target 217 218 self._iterations = self._experiments_conf.get('iterations', 1) 219 # Compute total number of experiments 220 self._exp_count = self._iterations \ 221 * len(self._experiments_conf['wloads']) \ 222 * len(self._experiments_conf['confs']) 223 224 self._print_section('Experiments configuration') 225 226 self._log.info('Configured to run:') 227 228 self._log.info(' %3d target configurations:', 229 len(self._experiments_conf['confs'])) 230 target_confs = [conf['tag'] for conf in self._experiments_conf['confs']] 231 target_confs = ', '.join(target_confs) 232 self._log.info(' %s', target_confs) 233 234 self._log.info(' %3d workloads (%d iterations each)', 235 len(self._experiments_conf['wloads']), 236 self._iterations) 237 wload_confs = ', '.join(self._experiments_conf['wloads']) 238 self._log.info(' %s', wload_confs) 239 240 self._log.info('Total: %d experiments', self._exp_count) 241 242 self._log.info('Results will be collected under:') 243 self._log.info(' %s', self.te.res_dir) 244 245 if any(wl['type'] == 'rt-app' 246 for wl in self._experiments_conf['wloads'].values()): 247 self._log.info('rt-app workloads found, installing tool on target') 248 self.te.install_tools(['rt-app']) 249 250 def run(self): 251 self._print_section('Experiments execution') 252 253 self.experiments = [] 254 255 # Run all the configured experiments 256 exp_idx = 0 257 for tc in self._experiments_conf['confs']: 258 # TARGET: configuration 259 if not self._target_configure(tc): 260 continue 261 for wl_idx in self._experiments_conf['wloads']: 262 # TEST: configuration 263 wload, test_dir = self._wload_init(tc, wl_idx) 264 for itr_idx in range(1, self._iterations + 1): 265 exp = Experiment( 266 wload_name=wl_idx, 267 wload=wload, 268 conf=tc, 269 iteration=itr_idx, 270 out_dir=os.path.join(test_dir, str(itr_idx))) 271 self.experiments.append(exp) 272 273 # WORKLOAD: execution 274 self._wload_run(exp_idx, exp) 275 exp_idx += 1 276 self._target_cleanup(tc) 277 278 self._print_section('Experiments execution completed') 279 self._log.info('Results available in:') 280 self._log.info(' %s', self.te.res_dir) 281 282 283################################################################################ 284# Target Configuration 285################################################################################ 286 287 def _cgroups_init(self, tc): 288 self._default_cgroup = None 289 if 'cgroups' not in tc: 290 return True 291 if 'cgroups' not in self.target.modules: 292 raise RuntimeError('CGroups module not available. Please ensure ' 293 '"cgroups" is listed in your target/test modules') 294 self._log.info('Initialize CGroups support...') 295 errors = False 296 for kind in tc['cgroups']['conf']: 297 self._log.info('Setup [%s] CGroup controller...', kind) 298 controller = self.target.cgroups.controller(kind) 299 if not controller: 300 self._log.warning('CGroups controller [%s] NOT available', 301 kind) 302 errors = True 303 return not errors 304 305 def _setup_kernel(self, tc): 306 # Deploy kernel on the device 307 self.te.install_kernel(tc, reboot=True) 308 # Setup the rootfs for the experiments 309 self._setup_rootfs(tc) 310 311 def _setup_sched_features(self, tc): 312 if 'sched_features' not in tc: 313 self._log.debug('Scheduler features configuration not provided') 314 return 315 feats = tc['sched_features'].split(",") 316 for feat in feats: 317 self._log.info('Set scheduler feature: %s', feat) 318 self.target.execute('echo {} > /sys/kernel/debug/sched_features'.format(feat), 319 as_root=True) 320 321 def _setup_rootfs(self, tc): 322 # Initialize CGroups if required 323 self._cgroups_init(tc) 324 # Setup target folder for experiments execution 325 self.te.run_dir = os.path.join( 326 self.target.working_directory, TGT_RUN_DIR) 327 # Create run folder as tmpfs 328 self._log.debug('Setup RT-App run folder [%s]...', self.te.run_dir) 329 self.target.execute('[ -d {0} ] || mkdir {0}'\ 330 .format(self.te.run_dir)) 331 self.target.execute( 332 'grep schedtest /proc/mounts || '\ 333 ' mount -t tmpfs -o size=1024m {} {}'\ 334 .format('schedtest', self.te.run_dir), 335 as_root=True) 336 # tmpfs mounts have an SELinux context with "tmpfs" as the type (while 337 # other files we create have "shell_data_file"). That prevents non-root 338 # users from creating files in tmpfs mounts. For now, just put SELinux 339 # in permissive mode to get around that. 340 try: 341 # First, save the old SELinux mode 342 self._old_selinux_mode = self.target.execute('getenforce') 343 self._log.warning('Setting target SELinux in permissive mode') 344 self.target.execute('setenforce 0', as_root=True) 345 except TargetError: 346 # Probably the target doesn't have SELinux, or there are no 347 # contexts set up. No problem. 348 self._log.warning("Couldn't set SELinux in permissive mode. " 349 "This is probably fine.") 350 self._old_selinux_mode = None 351 352 def _setup_cpufreq(self, tc): 353 if 'cpufreq' not in tc: 354 self._log.warning('cpufreq governor not specified, ' 355 'using currently configured governor') 356 return 357 358 cpufreq = tc['cpufreq'] 359 self._log.info('Configuring all CPUs to use [%s] cpufreq governor', 360 cpufreq['governor']) 361 362 self.target.cpufreq.set_all_governors(cpufreq['governor']) 363 364 if 'freqs' in cpufreq: 365 if cpufreq['governor'] != 'userspace': 366 raise ValueError('Must use userspace governor to set CPU freqs') 367 self._log.info(r'%14s - CPU frequencies: %s', 368 'CPUFreq', str(cpufreq['freqs'])) 369 for cpu, freq in cpufreq['freqs'].iteritems(): 370 self.target.cpufreq.set_frequency(cpu, freq) 371 372 if 'params' in cpufreq: 373 self._log.info('governor params: %s', str(cpufreq['params'])) 374 for cpu in self.target.list_online_cpus(): 375 self.target.cpufreq.set_governor_tunables( 376 cpu, 377 cpufreq['governor'], 378 **cpufreq['params']) 379 380 def _setup_cgroups(self, tc): 381 if 'cgroups' not in tc: 382 return True 383 # Setup default CGroup to run tasks into 384 if 'default' in tc['cgroups']: 385 self._default_cgroup = tc['cgroups']['default'] 386 # Configure each required controller 387 if 'conf' not in tc['cgroups']: 388 return True 389 errors = False 390 for kind in tc['cgroups']['conf']: 391 controller = self.target.cgroups.controller(kind) 392 if not controller: 393 self._log.warning('Configuration error: ' 394 '[%s] contoller NOT supported', 395 kind) 396 errors = True 397 continue 398 self._setup_controller(tc, controller) 399 return not errors 400 401 def _setup_controller(self, tc, controller): 402 kind = controller.kind 403 # Configure each required groups for that controller 404 errors = False 405 for name in tc['cgroups']['conf'][controller.kind]: 406 if name[0] != '/': 407 raise ValueError('Wrong CGroup name [{}]. ' 408 'CGroups names must start by "/".' 409 .format(name)) 410 group = controller.cgroup(name) 411 if not group: 412 self._log.warning('Configuration error: ' 413 '[%s/%s] cgroup NOT available', 414 kind, name) 415 errors = True 416 continue 417 self._setup_group(tc, group) 418 return not errors 419 420 def _setup_group(self, tc, group): 421 kind = group.controller.kind 422 name = group.name 423 # Configure each required attribute 424 group.set(**tc['cgroups']['conf'][kind][name]) 425 426 def _setup_files(self, tc): 427 if 'files' not in tc: 428 self._log.debug('\'files\' Configuration block not provided') 429 return True 430 for name, value in tc['files'].iteritems(): 431 check = False 432 if name.startswith('!/'): 433 check = True 434 name = name[1:] 435 self._log.info('File Write(check=%s): \'%s\' -> \'%s\'', 436 check, value, name) 437 try: 438 self.target.write_value(name, value, True) 439 except TargetError: 440 self._log.info('File Write Failed: \'%s\' -> \'%s\'', 441 value, name) 442 if check: 443 raise 444 return False 445 446 def _target_configure(self, tc): 447 self._print_header( 448 'configuring target for [{}] experiments'\ 449 .format(tc['tag'])) 450 self._setup_kernel(tc) 451 self._setup_sched_features(tc) 452 self._setup_cpufreq(tc) 453 self._setup_files(tc) 454 return self._setup_cgroups(tc) 455 456 def _target_conf_flag(self, tc, flag): 457 if 'flags' not in tc: 458 has_flag = False 459 else: 460 has_flag = flag in tc['flags'] 461 self._log.debug('Check if target configuration [%s] has flag [%s]: %s', 462 tc['tag'], flag, has_flag) 463 return has_flag 464 465 def _target_cleanup(self, tc): 466 if self._old_selinux_mode is not None: 467 self._log.info('Restoring target SELinux mode: %s', 468 self._old_selinux_mode) 469 self.target.execute('setenforce ' + self._old_selinux_mode, 470 as_root=True) 471 472################################################################################ 473# Workload Setup and Execution 474################################################################################ 475 476 def _wload_cpus(self, wl_idx, wlspec): 477 if not 'cpus' in wlspec['conf']: 478 return None 479 cpus = wlspec['conf']['cpus'] 480 481 if type(cpus) == list: 482 return cpus 483 if type(cpus) == int: 484 return [cpus] 485 486 # SMP target (or not bL module loaded) 487 if not hasattr(self.target, 'bl'): 488 if 'first' in cpus: 489 return [ self.target.list_online_cpus()[0] ] 490 if 'last' in cpus: 491 return [ self.target.list_online_cpus()[-1] ] 492 return self.target.list_online_cpus() 493 494 # big.LITTLE target 495 if cpus.startswith('littles'): 496 if 'first' in cpus: 497 return [ self.target.bl.littles_online[0] ] 498 if 'last' in cpus: 499 return [ self.target.bl.littles_online[-1] ] 500 return self.target.bl.littles_online 501 if cpus.startswith('bigs'): 502 if 'first' in cpus: 503 return [ self.target.bl.bigs_online[0] ] 504 if 'last' in cpus: 505 return [ self.target.bl.bigs_online[-1] ] 506 return self.target.bl.bigs_online 507 raise ValueError('unsupported [{}] "cpus" value for [{}] ' 508 'workload specification' 509 .format(cpus, wl_idx)) 510 511 def _wload_task_idxs(self, wl_idx, tasks): 512 if type(tasks) == int: 513 return range(tasks) 514 if tasks == 'cpus': 515 return range(len(self.target.core_names)) 516 if tasks == 'little': 517 return range(len([t 518 for t in self.target.core_names 519 if t == self.target.little_core])) 520 if tasks == 'big': 521 return range(len([t 522 for t in self.target.core_names 523 if t == self.target.big_core])) 524 raise ValueError('unsupported "tasks" value for [{}] RT-App ' 525 'workload specification' 526 .format(wl_idx)) 527 528 def _wload_rtapp(self, wl_idx, wlspec, cpus): 529 conf = wlspec['conf'] 530 self._log.debug('Configuring [%s] rt-app...', conf['class']) 531 532 # Setup a default "empty" task name prefix 533 if 'prefix' not in conf: 534 conf['prefix'] = 'task_' 535 536 # Setup a default loadref CPU 537 loadref = None 538 if 'loadref' in wlspec: 539 loadref = wlspec['loadref'] 540 541 if conf['class'] == 'profile': 542 params = {} 543 # Load each task specification 544 for task_name, task in conf['params'].items(): 545 if task['kind'] not in wlgen.__dict__: 546 self._log.error('RTA task of kind [%s] not supported', 547 task['kind']) 548 raise ValueError('unsupported "kind" value for task [{}] ' 549 'in RT-App workload specification' 550 .format(task)) 551 task_ctor = getattr(wlgen, task['kind']) 552 num_tasks = task.get('tasks', 1) 553 task_idxs = self._wload_task_idxs(wl_idx, num_tasks) 554 for idx in task_idxs: 555 idx_name = "_{}".format(idx) if len(task_idxs) > 1 else "" 556 task_name_idx = conf['prefix'] + task_name + idx_name 557 params[task_name_idx] = task_ctor(**task['params']).get() 558 559 rtapp = wlgen.RTA(self.target, 560 wl_idx, calibration = self.te.calibration()) 561 rtapp.conf(kind='profile', params=params, loadref=loadref, 562 cpus=cpus, run_dir=self.te.run_dir, 563 duration=conf.get('duration')) 564 return rtapp 565 566 if conf['class'] == 'periodic': 567 task_idxs = self._wload_task_idxs(wl_idx, conf['tasks']) 568 params = {} 569 for idx in task_idxs: 570 task = conf['prefix'] + str(idx) 571 params[task] = wlgen.Periodic(**conf['params']).get() 572 rtapp = wlgen.RTA(self.target, 573 wl_idx, calibration = self.te.calibration()) 574 rtapp.conf(kind='profile', params=params, loadref=loadref, 575 cpus=cpus, run_dir=self.te.run_dir, 576 duration=conf.get('duration')) 577 return rtapp 578 579 if conf['class'] == 'custom': 580 rtapp = wlgen.RTA(self.target, 581 wl_idx, calibration = self.te.calib) 582 rtapp.conf(kind='custom', 583 params=conf['json'], 584 duration=conf['duration'], 585 loadref=loadref, 586 cpus=cpus, run_dir=self.te.run_dir) 587 return rtapp 588 589 raise ValueError('unsupported \'class\' value for [{}] ' 590 'RT-App workload specification' 591 .format(wl_idx)) 592 593 def _wload_perf_bench(self, wl_idx, wlspec, cpus): 594 conf = wlspec['conf'] 595 self._log.debug('Configuring perf_message...') 596 597 if conf['class'] == 'messaging': 598 perf_bench = wlgen.PerfMessaging(self.target, wl_idx) 599 perf_bench.conf(**conf['params']) 600 return perf_bench 601 602 if conf['class'] == 'pipe': 603 perf_bench = wlgen.PerfPipe(self.target, wl_idx) 604 perf_bench.conf(**conf['params']) 605 return perf_bench 606 607 raise ValueError('unsupported "class" value for [{}] ' 608 'perf bench workload specification' 609 .format(wl_idx)) 610 611 def _wload_conf(self, wl_idx, wlspec): 612 613 # CPUS: setup execution on CPUs if required by configuration 614 cpus = self._wload_cpus(wl_idx, wlspec) 615 616 # CGroup: setup CGroups if requried by configuration 617 self._cgroup = self._default_cgroup 618 if 'cgroup' in wlspec: 619 if 'cgroups' not in self.target.modules: 620 raise RuntimeError('Target not supporting CGroups or CGroups ' 621 'not configured for the current test configuration') 622 self._cgroup = wlspec['cgroup'] 623 624 if wlspec['type'] == 'rt-app': 625 return self._wload_rtapp(wl_idx, wlspec, cpus) 626 if wlspec['type'] == 'perf_bench': 627 return self._wload_perf_bench(wl_idx, wlspec, cpus) 628 629 630 raise ValueError('unsupported "type" value for [{}] ' 631 'workload specification' 632 .format(wl_idx)) 633 634 def _wload_init(self, tc, wl_idx): 635 tc_idx = tc['tag'] 636 637 # Configure the test workload 638 wlspec = self._experiments_conf['wloads'][wl_idx] 639 wload = self._wload_conf(wl_idx, wlspec) 640 641 # Keep track of platform configuration 642 test_dir = '{}/{}:{}:{}'\ 643 .format(self.te.res_dir, wload.wtype, tc_idx, wl_idx) 644 os.makedirs(test_dir) 645 self.te.platform_dump(test_dir) 646 647 # Keep track of kernel configuration and version 648 config = self.target.config 649 with gzip.open(os.path.join(test_dir, 'kernel.config'), 'wb') as fh: 650 fh.write(config.text) 651 output = self.target.execute('{} uname -a'\ 652 .format(self.target.busybox)) 653 with open(os.path.join(test_dir, 'kernel.version'), 'w') as fh: 654 fh.write(output) 655 656 return wload, test_dir 657 658 def _wload_run(self, exp_idx, experiment): 659 tc = experiment.conf 660 wload = experiment.wload 661 tc_idx = tc['tag'] 662 663 self._print_title('Experiment {}/{}, [{}:{}] {}/{}'\ 664 .format(exp_idx, self._exp_count, 665 tc_idx, experiment.wload_name, 666 experiment.iteration, self._iterations)) 667 668 # Setup local results folder 669 self._log.debug('out_dir set to [%s]', experiment.out_dir) 670 os.system('mkdir -p ' + experiment.out_dir) 671 672 # Freeze all userspace tasks that we don't need for running tests 673 need_thaw = False 674 if self._target_conf_flag(tc, 'freeze_userspace'): 675 need_thaw = self._freeze_userspace() 676 677 # FTRACE: start (if a configuration has been provided) 678 if self.te.ftrace and self._target_conf_flag(tc, 'ftrace'): 679 self._log.warning('FTrace events collection enabled') 680 self.te.ftrace.start() 681 682 # ENERGY: start sampling 683 if self.te.emeter: 684 self.te.emeter.reset() 685 686 # WORKLOAD: Run the configured workload 687 wload.run(out_dir=experiment.out_dir, cgroup=self._cgroup) 688 689 # ENERGY: collect measurements 690 if self.te.emeter: 691 self.te.emeter.report(experiment.out_dir) 692 693 # FTRACE: stop and collect measurements 694 if self.te.ftrace and self._target_conf_flag(tc, 'ftrace'): 695 self.te.ftrace.stop() 696 697 trace_file = experiment.out_dir + '/trace.dat' 698 self.te.ftrace.get_trace(trace_file) 699 self._log.info('Collected FTrace binary trace:') 700 self._log.info(' %s', 701 trace_file.replace(self.te.res_dir, '<res_dir>')) 702 703 stats_file = experiment.out_dir + '/trace_stat.json' 704 self.te.ftrace.get_stats(stats_file) 705 self._log.info('Collected FTrace function profiling:') 706 self._log.info(' %s', 707 stats_file.replace(self.te.res_dir, '<res_dir>')) 708 709 # Unfreeze the tasks we froze 710 if need_thaw: 711 self._thaw_userspace() 712 713 self._print_footer() 714 715 def _freeze_userspace(self): 716 if 'cgroups' not in self.target.modules: 717 raise RuntimeError( 718 'Failed to freeze userspace. Ensure "cgroups" module is listed ' 719 'among modules in target/test configuration') 720 controllers = [s.name for s in self.target.cgroups.list_subsystems()] 721 if 'freezer' not in controllers: 722 self._log.warning('No freezer cgroup controller on target. ' 723 'Not freezing userspace') 724 return False 725 726 exclude = self.critical_tasks[self.te.target.os] 727 self._log.info('Freezing all tasks except: %s', ','.join(exclude)) 728 self.te.target.cgroups.freeze(exclude) 729 return True 730 731 732 def _thaw_userspace(self): 733 self._log.info('Un-freezing userspace tasks') 734 self.te.target.cgroups.freeze(thaw=True) 735 736################################################################################ 737# Utility Functions 738################################################################################ 739 740 def _print_section(self, message): 741 self._log.info('') 742 self._log.info(FMT_SECTION) 743 self._log.info(message) 744 self._log.info(FMT_SECTION) 745 746 def _print_header(self, message): 747 self._log.info('') 748 self._log.info(FMT_HEADER) 749 self._log.info(message) 750 751 def _print_title(self, message): 752 self._log.info(FMT_TITLE) 753 self._log.info(message) 754 755 def _print_footer(self, message=None): 756 if message: 757 self._log.info(message) 758 self._log.info(FMT_FOOTER) 759 760 761################################################################################ 762# Globals 763################################################################################ 764 765# Regular expression for comments 766JSON_COMMENTS_RE = re.compile( 767 '(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?', 768 re.DOTALL | re.MULTILINE 769) 770 771# Target specific paths 772TGT_RUN_DIR = 'run_dir' 773 774# Logging formatters 775FMT_SECTION = r'{:#<80}'.format('') 776FMT_HEADER = r'{:=<80}'.format('') 777FMT_TITLE = r'{:~<80}'.format('') 778FMT_FOOTER = r'{:-<80}'.format('') 779 780# vim :set tabstop=4 shiftwidth=4 expandtab 781