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 :ivar experiments: After calling `meth`:run:, the list of 171 :class:`Experiment` s that were run 172 173 :ivar iterations: The number of iterations run for each wload/conf pair 174 (i.e. ``experiments_conf['iterations']``. 175 176 """ 177 178 critical_tasks = { 179 'linux': ['init', 'systemd', 'sh', 'ssh', 'rsyslogd', 'jbd2'], 180 'android': [ 181 'sh', 'adbd', 182 'usb', 'transport', 183 # We don't actually need this task but on Google Pixel it apparently 184 # cannot be frozen, so the cgroup state gets stuck in FREEZING if we 185 # try to freeze it. 186 'thermal-engine' 187 ] 188 } 189 """ 190 Dictionary mapping OS name to list of task names that we can't afford to 191 freeze when using freeeze_userspace. 192 """ 193 194 def __init__(self, test_env, experiments_conf): 195 # Initialize globals 196 self._default_cgroup = None 197 self._cgroup = None 198 199 # Setup logging 200 self._log = logging.getLogger('Executor') 201 202 # Setup test configuration 203 if isinstance(experiments_conf, dict): 204 self._log.info('Loading custom (inline) test configuration') 205 self._experiments_conf = experiments_conf 206 elif isinstance(experiments_conf, str): 207 self._log.info('Loading custom (file) test configuration') 208 json_conf = JsonConf(experiments_conf) 209 self._experiments_conf = json_conf.load() 210 else: 211 raise ValueError( 212 'experiments_conf must be either a dictionary or a filepath') 213 214 # Check for mandatory configurations 215 if not self._experiments_conf.get('confs', None): 216 raise ValueError('Configuration error: ' 217 'missing "conf" definitions') 218 if not self._experiments_conf.get('wloads', None): 219 raise ValueError('Configuration error: ' 220 'missing "wloads" definitions') 221 222 self.te = test_env 223 self.target = self.te.target 224 225 self.iterations = self._experiments_conf.get('iterations', 1) 226 # Compute total number of experiments 227 self._exp_count = self.iterations \ 228 * len(self._experiments_conf['wloads']) \ 229 * len(self._experiments_conf['confs']) 230 231 self._print_section('Experiments configuration') 232 233 self._log.info('Configured to run:') 234 235 self._log.info(' %3d target configurations:', 236 len(self._experiments_conf['confs'])) 237 target_confs = [conf['tag'] for conf in self._experiments_conf['confs']] 238 target_confs = ', '.join(target_confs) 239 self._log.info(' %s', target_confs) 240 241 self._log.info(' %3d workloads (%d iterations each)', 242 len(self._experiments_conf['wloads']), 243 self.iterations) 244 wload_confs = ', '.join(self._experiments_conf['wloads']) 245 self._log.info(' %s', wload_confs) 246 247 self._log.info('Total: %d experiments', self._exp_count) 248 249 self._log.info('Results will be collected under:') 250 self._log.info(' %s', self.te.res_dir) 251 252 if any(wl['type'] == 'rt-app' 253 for wl in self._experiments_conf['wloads'].values()): 254 self._log.info('rt-app workloads found, installing tool on target') 255 self.te.install_tools(['rt-app']) 256 257 def run(self): 258 self._print_section('Experiments execution') 259 260 self.experiments = [] 261 262 # Run all the configured experiments 263 exp_idx = 0 264 for tc in self._experiments_conf['confs']: 265 # TARGET: configuration 266 if not self._target_configure(tc): 267 continue 268 for wl_idx in self._experiments_conf['wloads']: 269 # TEST: configuration 270 wload, test_dir = self._wload_init(tc, wl_idx) 271 for itr_idx in range(1, self.iterations + 1): 272 exp = Experiment( 273 wload_name=wl_idx, 274 wload=wload, 275 conf=tc, 276 iteration=itr_idx, 277 out_dir=os.path.join(test_dir, str(itr_idx))) 278 self.experiments.append(exp) 279 280 # WORKLOAD: execution 281 self._wload_run(exp_idx, exp) 282 exp_idx += 1 283 self._target_cleanup(tc) 284 285 self._print_section('Experiments execution completed') 286 self._log.info('Results available in:') 287 self._log.info(' %s', self.te.res_dir) 288 289 290################################################################################ 291# Target Configuration 292################################################################################ 293 294 def _cgroups_init(self, tc): 295 self._default_cgroup = None 296 if 'cgroups' not in tc: 297 return True 298 if 'cgroups' not in self.target.modules: 299 raise RuntimeError('CGroups module not available. Please ensure ' 300 '"cgroups" is listed in your target/test modules') 301 self._log.info('Initialize CGroups support...') 302 errors = False 303 for kind in tc['cgroups']['conf']: 304 self._log.info('Setup [%s] CGroup controller...', kind) 305 controller = self.target.cgroups.controller(kind) 306 if not controller: 307 self._log.warning('CGroups controller [%s] NOT available', 308 kind) 309 errors = True 310 return not errors 311 312 def _setup_kernel(self, tc): 313 # Deploy kernel on the device 314 self.te.install_kernel(tc, reboot=True) 315 # Setup the rootfs for the experiments 316 self._setup_rootfs(tc) 317 318 def _setup_sched_features(self, tc): 319 if 'sched_features' not in tc: 320 self._log.debug('Scheduler features configuration not provided') 321 return 322 feats = tc['sched_features'].split(",") 323 for feat in feats: 324 self._log.info('Set scheduler feature: %s', feat) 325 self.target.execute('echo {} > /sys/kernel/debug/sched_features'.format(feat), 326 as_root=True) 327 328 def _setup_rootfs(self, tc): 329 # Initialize CGroups if required 330 self._cgroups_init(tc) 331 # Setup target folder for experiments execution 332 self.te.run_dir = os.path.join( 333 self.target.working_directory, TGT_RUN_DIR) 334 # Create run folder as tmpfs 335 self._log.debug('Setup RT-App run folder [%s]...', self.te.run_dir) 336 self.target.execute('[ -d {0} ] || mkdir {0}'\ 337 .format(self.te.run_dir)) 338 self.target.execute( 339 'grep schedtest /proc/mounts || '\ 340 ' mount -t tmpfs -o size=1024m {} {}'\ 341 .format('schedtest', self.te.run_dir), 342 as_root=True) 343 # tmpfs mounts have an SELinux context with "tmpfs" as the type (while 344 # other files we create have "shell_data_file"). That prevents non-root 345 # users from creating files in tmpfs mounts. For now, just put SELinux 346 # in permissive mode to get around that. 347 try: 348 # First, save the old SELinux mode 349 self._old_selinux_mode = self.target.execute('getenforce') 350 self._log.warning('Setting target SELinux in permissive mode') 351 self.target.execute('setenforce 0', as_root=True) 352 except TargetError: 353 # Probably the target doesn't have SELinux, or there are no 354 # contexts set up. No problem. 355 self._log.warning("Couldn't set SELinux in permissive mode. " 356 "This is probably fine.") 357 self._old_selinux_mode = None 358 359 def _setup_cpufreq(self, tc): 360 if 'cpufreq' not in tc: 361 self._log.warning('cpufreq governor not specified, ' 362 'using currently configured governor') 363 return 364 365 cpufreq = tc['cpufreq'] 366 self._log.info('Configuring all CPUs to use [%s] cpufreq governor', 367 cpufreq['governor']) 368 369 self.target.cpufreq.set_all_governors(cpufreq['governor']) 370 371 if 'freqs' in cpufreq: 372 if cpufreq['governor'] != 'userspace': 373 raise ValueError('Must use userspace governor to set CPU freqs') 374 self._log.info(r'%14s - CPU frequencies: %s', 375 'CPUFreq', str(cpufreq['freqs'])) 376 for cpu, freq in cpufreq['freqs'].iteritems(): 377 self.target.cpufreq.set_frequency(cpu, freq) 378 379 if 'params' in cpufreq: 380 self._log.info('governor params: %s', str(cpufreq['params'])) 381 for cpu in self.target.list_online_cpus(): 382 self.target.cpufreq.set_governor_tunables( 383 cpu, 384 cpufreq['governor'], 385 **cpufreq['params']) 386 387 def _setup_cgroups(self, tc): 388 if 'cgroups' not in tc: 389 return True 390 # Setup default CGroup to run tasks into 391 if 'default' in tc['cgroups']: 392 self._default_cgroup = tc['cgroups']['default'] 393 # Configure each required controller 394 if 'conf' not in tc['cgroups']: 395 return True 396 errors = False 397 for kind in tc['cgroups']['conf']: 398 controller = self.target.cgroups.controller(kind) 399 if not controller: 400 self._log.warning('Configuration error: ' 401 '[%s] contoller NOT supported', 402 kind) 403 errors = True 404 continue 405 self._setup_controller(tc, controller) 406 return not errors 407 408 def _setup_controller(self, tc, controller): 409 kind = controller.kind 410 # Configure each required groups for that controller 411 errors = False 412 for name in tc['cgroups']['conf'][controller.kind]: 413 if name[0] != '/': 414 raise ValueError('Wrong CGroup name [{}]. ' 415 'CGroups names must start by "/".' 416 .format(name)) 417 group = controller.cgroup(name) 418 if not group: 419 self._log.warning('Configuration error: ' 420 '[%s/%s] cgroup NOT available', 421 kind, name) 422 errors = True 423 continue 424 self._setup_group(tc, group) 425 return not errors 426 427 def _setup_group(self, tc, group): 428 kind = group.controller.kind 429 name = group.name 430 # Configure each required attribute 431 group.set(**tc['cgroups']['conf'][kind][name]) 432 433 def _setup_files(self, tc): 434 if 'files' not in tc: 435 self._log.debug('\'files\' Configuration block not provided') 436 return True 437 for name, value in tc['files'].iteritems(): 438 check = False 439 if name.startswith('!/'): 440 check = True 441 name = name[1:] 442 self._log.info('File Write(check=%s): \'%s\' -> \'%s\'', 443 check, value, name) 444 try: 445 self.target.write_value(name, value, True) 446 except TargetError: 447 self._log.info('File Write Failed: \'%s\' -> \'%s\'', 448 value, name) 449 if check: 450 raise 451 return False 452 453 def _target_configure(self, tc): 454 self._print_header( 455 'configuring target for [{}] experiments'\ 456 .format(tc['tag'])) 457 self._setup_kernel(tc) 458 self._setup_sched_features(tc) 459 self._setup_cpufreq(tc) 460 self._setup_files(tc) 461 return self._setup_cgroups(tc) 462 463 def _target_conf_flag(self, tc, flag): 464 if 'flags' not in tc: 465 has_flag = False 466 else: 467 has_flag = flag in tc['flags'] 468 self._log.debug('Check if target configuration [%s] has flag [%s]: %s', 469 tc['tag'], flag, has_flag) 470 return has_flag 471 472 def _target_cleanup(self, tc): 473 if self._old_selinux_mode is not None: 474 self._log.info('Restoring target SELinux mode: %s', 475 self._old_selinux_mode) 476 self.target.execute('setenforce ' + self._old_selinux_mode, 477 as_root=True) 478 479################################################################################ 480# Workload Setup and Execution 481################################################################################ 482 483 def _wload_cpus(self, wl_idx, wlspec): 484 if not 'cpus' in wlspec['conf']: 485 return None 486 cpus = wlspec['conf']['cpus'] 487 488 if type(cpus) == list: 489 return cpus 490 if type(cpus) == int: 491 return [cpus] 492 493 # SMP target (or not bL module loaded) 494 if not hasattr(self.target, 'bl'): 495 if 'first' in cpus: 496 return [ self.target.list_online_cpus()[0] ] 497 if 'last' in cpus: 498 return [ self.target.list_online_cpus()[-1] ] 499 return self.target.list_online_cpus() 500 501 # big.LITTLE target 502 if cpus.startswith('littles'): 503 if 'first' in cpus: 504 return [ self.target.bl.littles_online[0] ] 505 if 'last' in cpus: 506 return [ self.target.bl.littles_online[-1] ] 507 return self.target.bl.littles_online 508 if cpus.startswith('bigs'): 509 if 'first' in cpus: 510 return [ self.target.bl.bigs_online[0] ] 511 if 'last' in cpus: 512 return [ self.target.bl.bigs_online[-1] ] 513 return self.target.bl.bigs_online 514 raise ValueError('unsupported [{}] "cpus" value for [{}] ' 515 'workload specification' 516 .format(cpus, wl_idx)) 517 518 def _wload_task_idxs(self, wl_idx, tasks): 519 if type(tasks) == int: 520 return range(tasks) 521 if tasks == 'cpus': 522 return range(len(self.target.core_names)) 523 if tasks == 'little': 524 return range(len([t 525 for t in self.target.core_names 526 if t == self.target.little_core])) 527 if tasks == 'big': 528 return range(len([t 529 for t in self.target.core_names 530 if t == self.target.big_core])) 531 raise ValueError('unsupported "tasks" value for [{}] RT-App ' 532 'workload specification' 533 .format(wl_idx)) 534 535 def _wload_rtapp(self, wl_idx, wlspec, cpus): 536 conf = wlspec['conf'] 537 self._log.debug('Configuring [%s] rt-app...', conf['class']) 538 539 # Setup a default "empty" task name prefix 540 if 'prefix' not in conf: 541 conf['prefix'] = 'task_' 542 543 # Setup a default loadref CPU 544 loadref = None 545 if 'loadref' in wlspec: 546 loadref = wlspec['loadref'] 547 548 if conf['class'] == 'profile': 549 params = {} 550 # Load each task specification 551 for task_name, task in conf['params'].items(): 552 if task['kind'] not in wlgen.__dict__: 553 self._log.error('RTA task of kind [%s] not supported', 554 task['kind']) 555 raise ValueError('unsupported "kind" value for task [{}] ' 556 'in RT-App workload specification' 557 .format(task)) 558 task_ctor = getattr(wlgen, task['kind']) 559 num_tasks = task.get('tasks', 1) 560 task_idxs = self._wload_task_idxs(wl_idx, num_tasks) 561 for idx in task_idxs: 562 idx_name = "_{}".format(idx) if len(task_idxs) > 1 else "" 563 task_name_idx = conf['prefix'] + task_name + idx_name 564 params[task_name_idx] = task_ctor(**task['params']).get() 565 566 rtapp = wlgen.RTA(self.target, 567 wl_idx, calibration = self.te.calibration()) 568 rtapp.conf(kind='profile', params=params, loadref=loadref, 569 cpus=cpus, run_dir=self.te.run_dir, 570 duration=conf.get('duration')) 571 return rtapp 572 573 if conf['class'] == 'periodic': 574 task_idxs = self._wload_task_idxs(wl_idx, conf['tasks']) 575 params = {} 576 for idx in task_idxs: 577 task = conf['prefix'] + str(idx) 578 params[task] = wlgen.Periodic(**conf['params']).get() 579 rtapp = wlgen.RTA(self.target, 580 wl_idx, calibration = self.te.calibration()) 581 rtapp.conf(kind='profile', params=params, loadref=loadref, 582 cpus=cpus, run_dir=self.te.run_dir, 583 duration=conf.get('duration')) 584 return rtapp 585 586 if conf['class'] == 'custom': 587 rtapp = wlgen.RTA(self.target, 588 wl_idx, calibration = self.te.calibration()) 589 rtapp.conf(kind='custom', 590 params=conf['json'], 591 duration=conf.get('duration'), 592 loadref=loadref, 593 cpus=cpus, run_dir=self.te.run_dir) 594 return rtapp 595 596 raise ValueError('unsupported \'class\' value for [{}] ' 597 'RT-App workload specification' 598 .format(wl_idx)) 599 600 def _wload_perf_bench(self, wl_idx, wlspec, cpus): 601 conf = wlspec['conf'] 602 self._log.debug('Configuring perf_message...') 603 604 if conf['class'] == 'messaging': 605 perf_bench = wlgen.PerfMessaging(self.target, wl_idx) 606 perf_bench.conf(**conf['params']) 607 return perf_bench 608 609 if conf['class'] == 'pipe': 610 perf_bench = wlgen.PerfPipe(self.target, wl_idx) 611 perf_bench.conf(**conf['params']) 612 return perf_bench 613 614 raise ValueError('unsupported "class" value for [{}] ' 615 'perf bench workload specification' 616 .format(wl_idx)) 617 618 def _wload_conf(self, wl_idx, wlspec): 619 620 # CPUS: setup execution on CPUs if required by configuration 621 cpus = self._wload_cpus(wl_idx, wlspec) 622 623 # CGroup: setup CGroups if requried by configuration 624 self._cgroup = self._default_cgroup 625 if 'cgroup' in wlspec: 626 if 'cgroups' not in self.target.modules: 627 raise RuntimeError('Target not supporting CGroups or CGroups ' 628 'not configured for the current test configuration') 629 self._cgroup = wlspec['cgroup'] 630 631 if wlspec['type'] == 'rt-app': 632 return self._wload_rtapp(wl_idx, wlspec, cpus) 633 if wlspec['type'] == 'perf_bench': 634 return self._wload_perf_bench(wl_idx, wlspec, cpus) 635 636 637 raise ValueError('unsupported "type" value for [{}] ' 638 'workload specification' 639 .format(wl_idx)) 640 641 def _wload_init(self, tc, wl_idx): 642 tc_idx = tc['tag'] 643 644 # Configure the test workload 645 wlspec = self._experiments_conf['wloads'][wl_idx] 646 wload = self._wload_conf(wl_idx, wlspec) 647 648 # Keep track of platform configuration 649 test_dir = '{}/{}:{}:{}'\ 650 .format(self.te.res_dir, wload.wtype, tc_idx, wl_idx) 651 os.makedirs(test_dir) 652 self.te.platform_dump(test_dir) 653 654 # Keep track of kernel configuration and version 655 config = self.target.config 656 with gzip.open(os.path.join(test_dir, 'kernel.config'), 'wb') as fh: 657 fh.write(config.text) 658 output = self.target.execute('{} uname -a'\ 659 .format(self.target.busybox)) 660 with open(os.path.join(test_dir, 'kernel.version'), 'w') as fh: 661 fh.write(output) 662 663 return wload, test_dir 664 665 def _wload_run(self, exp_idx, experiment): 666 tc = experiment.conf 667 wload = experiment.wload 668 tc_idx = tc['tag'] 669 670 self._print_title('Experiment {}/{}, [{}:{}] {}/{}'\ 671 .format(exp_idx, self._exp_count, 672 tc_idx, experiment.wload_name, 673 experiment.iteration, self.iterations)) 674 675 # Setup local results folder 676 self._log.debug('out_dir set to [%s]', experiment.out_dir) 677 os.system('mkdir -p ' + experiment.out_dir) 678 679 # Freeze all userspace tasks that we don't need for running tests 680 need_thaw = False 681 if self._target_conf_flag(tc, 'freeze_userspace'): 682 need_thaw = self._freeze_userspace() 683 684 # FTRACE: start (if a configuration has been provided) 685 if self.te.ftrace and self._target_conf_flag(tc, 'ftrace'): 686 self._log.warning('FTrace events collection enabled') 687 self.te.ftrace.start() 688 689 # ENERGY: start sampling 690 if self.te.emeter: 691 self.te.emeter.reset() 692 693 # WORKLOAD: Run the configured workload 694 wload.run(out_dir=experiment.out_dir, cgroup=self._cgroup) 695 696 # ENERGY: collect measurements 697 if self.te.emeter: 698 self.te.emeter.report(experiment.out_dir) 699 700 # FTRACE: stop and collect measurements 701 if self.te.ftrace and self._target_conf_flag(tc, 'ftrace'): 702 self.te.ftrace.stop() 703 704 trace_file = experiment.out_dir + '/trace.dat' 705 self.te.ftrace.get_trace(trace_file) 706 self._log.info('Collected FTrace binary trace:') 707 self._log.info(' %s', 708 trace_file.replace(self.te.res_dir, '<res_dir>')) 709 710 stats_file = experiment.out_dir + '/trace_stat.json' 711 self.te.ftrace.get_stats(stats_file) 712 self._log.info('Collected FTrace function profiling:') 713 self._log.info(' %s', 714 stats_file.replace(self.te.res_dir, '<res_dir>')) 715 716 # Unfreeze the tasks we froze 717 if need_thaw: 718 self._thaw_userspace() 719 720 self._print_footer() 721 722 def _freeze_userspace(self): 723 if 'cgroups' not in self.target.modules: 724 raise RuntimeError( 725 'Failed to freeze userspace. Ensure "cgroups" module is listed ' 726 'among modules in target/test configuration') 727 controllers = [s.name for s in self.target.cgroups.list_subsystems()] 728 if 'freezer' not in controllers: 729 self._log.warning('No freezer cgroup controller on target. ' 730 'Not freezing userspace') 731 return False 732 733 exclude = self.critical_tasks[self.te.target.os] 734 self._log.info('Freezing all tasks except: %s', ','.join(exclude)) 735 self.te.target.cgroups.freeze(exclude) 736 return True 737 738 739 def _thaw_userspace(self): 740 self._log.info('Un-freezing userspace tasks') 741 self.te.target.cgroups.freeze(thaw=True) 742 743################################################################################ 744# Utility Functions 745################################################################################ 746 747 def _print_section(self, message): 748 self._log.info('') 749 self._log.info(FMT_SECTION) 750 self._log.info(message) 751 self._log.info(FMT_SECTION) 752 753 def _print_header(self, message): 754 self._log.info('') 755 self._log.info(FMT_HEADER) 756 self._log.info(message) 757 758 def _print_title(self, message): 759 self._log.info(FMT_TITLE) 760 self._log.info(message) 761 762 def _print_footer(self, message=None): 763 if message: 764 self._log.info(message) 765 self._log.info(FMT_FOOTER) 766 767 768################################################################################ 769# Globals 770################################################################################ 771 772# Regular expression for comments 773JSON_COMMENTS_RE = re.compile( 774 '(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?', 775 re.DOTALL | re.MULTILINE 776) 777 778# Target specific paths 779TGT_RUN_DIR = 'run_dir' 780 781# Logging formatters 782FMT_SECTION = r'{:#<80}'.format('') 783FMT_HEADER = r'{:=<80}'.format('') 784FMT_TITLE = r'{:~<80}'.format('') 785FMT_FOOTER = r'{:-<80}'.format('') 786 787# vim :set tabstop=4 shiftwidth=4 expandtab 788