1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging 6import time 7 8from pylib import android_commands 9from pylib.device import device_utils 10 11 12class PerfControl(object): 13 """Provides methods for setting the performance mode of a device.""" 14 _SCALING_GOVERNOR_FMT = ( 15 '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor') 16 _CPU_ONLINE_FMT = '/sys/devices/system/cpu/cpu%d/online' 17 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' 18 19 def __init__(self, device): 20 # TODO(jbudorick) Remove once telemetry gets switched over. 21 if isinstance(device, android_commands.AndroidCommands): 22 device = device_utils.DeviceUtils(device) 23 self._device = device 24 kernel_max = self._device.old_interface.GetFileContents( 25 PerfControl._KERNEL_MAX, log_result=False) 26 assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX 27 self._kernel_max = int(kernel_max[0]) 28 logging.info('Maximum CPU index: %d', self._kernel_max) 29 self._have_mpdecision = self._device.old_interface.FileExistsOnDevice( 30 '/system/bin/mpdecision') 31 32 @property 33 def _NumCpuCores(self): 34 return self._kernel_max + 1 35 36 def SetHighPerfMode(self): 37 # TODO(epenner): Enable on all devices (http://crbug.com/383566) 38 if 'Nexus 4' == self._device.old_interface.GetProductModel(): 39 self._ForceAllCpusOnline(True) 40 self._SetScalingGovernorInternal('performance') 41 42 def SetPerfProfilingMode(self): 43 """Sets the highest possible performance mode for the device.""" 44 self._ForceAllCpusOnline(True) 45 self._SetScalingGovernorInternal('performance') 46 47 def SetDefaultPerfMode(self): 48 """Sets the performance mode for the device to its default mode.""" 49 product_model = self._device.old_interface.GetProductModel() 50 governor_mode = { 51 'GT-I9300': 'pegasusq', 52 'Galaxy Nexus': 'interactive', 53 'Nexus 4': 'ondemand', 54 'Nexus 7': 'interactive', 55 'Nexus 10': 'interactive' 56 }.get(product_model, 'ondemand') 57 self._SetScalingGovernorInternal(governor_mode) 58 self._ForceAllCpusOnline(False) 59 60 def _SetScalingGovernorInternal(self, value): 61 for cpu in range(self._NumCpuCores): 62 scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu 63 if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): 64 logging.info('Writing scaling governor mode \'%s\' -> %s', 65 value, scaling_governor_file) 66 self._device.old_interface.SetProtectedFileContents( 67 scaling_governor_file, value) 68 69 def _AllCpusAreOnline(self): 70 for cpu in range(self._NumCpuCores): 71 online_path = PerfControl._CPU_ONLINE_FMT % cpu 72 if self._device.old_interface.GetFileContents(online_path)[0] == '0': 73 return False 74 return True 75 76 def _ForceAllCpusOnline(self, force_online): 77 """Enable all CPUs on a device. 78 79 Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise 80 to measurements: 81 - In perf, samples are only taken for the CPUs that are online when the 82 measurement is started. 83 - The scaling governor can't be set for an offline CPU and frequency scaling 84 on newly enabled CPUs adds noise to both perf and tracing measurements. 85 86 It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm 87 this is done by "mpdecision". 88 89 """ 90 if self._have_mpdecision: 91 script = 'stop mpdecision' if force_online else 'start mpdecision' 92 self._device.RunShellCommand(script, root=True) 93 94 if not self._have_mpdecision and not self._AllCpusAreOnline(): 95 logging.warning('Unexpected cpu hot plugging detected.') 96 97 if not force_online: 98 return 99 100 for cpu in range(self._NumCpuCores): 101 online_path = PerfControl._CPU_ONLINE_FMT % cpu 102 self._device.old_interface.SetProtectedFileContents( 103 online_path, '1') 104 105 # Double check all cores stayed online. 106 time.sleep(0.25) 107 if not self._AllCpusAreOnline(): 108 raise RuntimeError('Failed to force CPUs online') 109