| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | 5 import logging |
| 6 import time | 6 import time |
| 7 | 7 |
| 8 from pylib import android_commands | 8 from pylib import android_commands |
| 9 from pylib.device import device_utils | 9 from pylib.device import device_utils |
| 10 | 10 |
| 11 | 11 |
| 12 class PerfControl(object): | 12 class PerfControl(object): |
| 13 """Provides methods for setting the performance mode of a device.""" | 13 """Provides methods for setting the performance mode of a device.""" |
| 14 _SCALING_GOVERNOR_FMT = ( | 14 _SCALING_GOVERNOR_FMT = ( |
| 15 '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor') | 15 '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor') |
| 16 _CPU_ONLINE_FMT = '/sys/devices/system/cpu/cpu%d/online' |
| 16 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' | 17 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' |
| 17 | 18 |
| 18 def __init__(self, device): | 19 def __init__(self, device): |
| 19 # TODO(jbudorick) Remove once telemetry gets switched over. | 20 # TODO(jbudorick) Remove once telemetry gets switched over. |
| 20 if isinstance(device, android_commands.AndroidCommands): | 21 if isinstance(device, android_commands.AndroidCommands): |
| 21 device = device_utils.DeviceUtils(device) | 22 device = device_utils.DeviceUtils(device) |
| 22 self._device = device | 23 self._device = device |
| 23 kernel_max = self._device.old_interface.GetFileContents( | 24 kernel_max = self._device.old_interface.GetFileContents( |
| 24 PerfControl._KERNEL_MAX, log_result=False) | 25 PerfControl._KERNEL_MAX, log_result=False) |
| 25 assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX | 26 assert kernel_max, 'Unable to find %s' % PerfControl._KERNEL_MAX |
| 26 self._kernel_max = int(kernel_max[0]) | 27 self._kernel_max = int(kernel_max[0]) |
| 27 logging.info('Maximum CPU index: %d', self._kernel_max) | 28 logging.info('Maximum CPU index: %d', self._kernel_max) |
| 28 self._original_scaling_governor = \ | 29 self._have_mpdecision = self._device.old_interface.FileExistsOnDevice( |
| 29 self._device.old_interface.GetFileContents( | 30 '/system/bin/mpdecision') |
| 30 PerfControl._SCALING_GOVERNOR_FMT % 0, | 31 |
| 31 log_result=False)[0] | 32 @property |
| 33 def _NumCpuCores(self): |
| 34 return self._kernel_max + 1 |
| 32 | 35 |
| 33 def SetHighPerfMode(self): | 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): |
| 34 """Sets the highest possible performance mode for the device.""" | 43 """Sets the highest possible performance mode for the device.""" |
| 44 self._ForceAllCpusOnline(True) |
| 35 self._SetScalingGovernorInternal('performance') | 45 self._SetScalingGovernorInternal('performance') |
| 36 | 46 |
| 37 def SetDefaultPerfMode(self): | 47 def SetDefaultPerfMode(self): |
| 38 """Sets the performance mode for the device to its default mode.""" | 48 """Sets the performance mode for the device to its default mode.""" |
| 39 product_model = self._device.old_interface.GetProductModel() | 49 product_model = self._device.old_interface.GetProductModel() |
| 40 governor_mode = { | 50 governor_mode = { |
| 41 'GT-I9300': 'pegasusq', | 51 'GT-I9300': 'pegasusq', |
| 42 'Galaxy Nexus': 'interactive', | 52 'Galaxy Nexus': 'interactive', |
| 43 'Nexus 4': 'ondemand', | 53 'Nexus 4': 'ondemand', |
| 44 'Nexus 7': 'interactive', | 54 'Nexus 7': 'interactive', |
| 45 'Nexus 10': 'interactive' | 55 'Nexus 10': 'interactive' |
| 46 }.get(product_model, 'ondemand') | 56 }.get(product_model, 'ondemand') |
| 47 self._SetScalingGovernorInternal(governor_mode) | 57 self._SetScalingGovernorInternal(governor_mode) |
| 48 | 58 self._ForceAllCpusOnline(False) |
| 49 def RestoreOriginalPerfMode(self): | |
| 50 """Resets the original performance mode of the device.""" | |
| 51 self._SetScalingGovernorInternal(self._original_scaling_governor) | |
| 52 | 59 |
| 53 def _SetScalingGovernorInternal(self, value): | 60 def _SetScalingGovernorInternal(self, value): |
| 54 for cpu in range(self._kernel_max + 1): | 61 for cpu in range(self._NumCpuCores): |
| 55 scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu | 62 scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu |
| 56 if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): | 63 if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): |
| 57 logging.info('Writing scaling governor mode \'%s\' -> %s', | 64 logging.info('Writing scaling governor mode \'%s\' -> %s', |
| 58 value, scaling_governor_file) | 65 value, scaling_governor_file) |
| 59 self._device.old_interface.SetProtectedFileContents( | 66 self._device.old_interface.SetProtectedFileContents( |
| 60 scaling_governor_file, value) | 67 scaling_governor_file, value) |
| 61 | 68 |
| 62 def ForceAllCpusOnline(self, force_online): | 69 def _AllCpusAreOnline(self): |
| 63 """Force all CPUs on a device to be online. | 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 |
| 64 | 75 |
| 65 Force every CPU core on an Android device to remain online, or return the | 76 def _ForceAllCpusOnline(self, force_online): |
| 66 cores under system power management control. This is needed to work around | 77 """Enable all CPUs on a device. |
| 67 a bug in perf which makes it unable to record samples from CPUs that become | |
| 68 online when recording is already underway. | |
| 69 | 78 |
| 70 Args: | 79 Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise |
| 71 force_online: True to set all CPUs online, False to return them under | 80 to measurements: |
| 72 system power management control. | 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 |
| 73 """ | 89 """ |
| 74 def ForceCpuOnline(online_path): | 90 if self._have_mpdecision: |
| 75 script = 'chmod 644 {0}; echo 1 > {0}; chmod 444 {0}'.format(online_path) | 91 script = 'stop mpdecision' if force_online else 'start mpdecision' |
| 76 self._device.RunShellCommand(script, root=True) | 92 self._device.RunShellCommand(script, root=True) |
| 77 return self._device.old_interface.GetFileContents(online_path)[0] == '1' | |
| 78 | 93 |
| 79 def ResetCpu(online_path): | 94 if not self._have_mpdecision and not self._AllCpusAreOnline(): |
| 80 self._device.RunShellCommand('chmod 644 %s' % online_path, root=True) | 95 logging.warning('Unexpected cpu hot plugging detected.') |
| 81 | 96 |
| 82 def WaitFor(condition): | 97 if not force_online: |
| 83 for _ in range(100): | 98 return |
| 84 if condition(): | |
| 85 return | |
| 86 time.sleep(0.1) | |
| 87 raise RuntimeError('Timed out') | |
| 88 | 99 |
| 89 cpu_online_files = self._device.RunShellCommand( | 100 for cpu in range(self._NumCpuCores): |
| 90 'ls -d /sys/devices/system/cpu/cpu[0-9]*/online') | 101 online_path = PerfControl._CPU_ONLINE_FMT % cpu |
| 91 for online_path in cpu_online_files: | 102 self._device.old_interface.SetProtectedFileContents( |
| 92 if force_online: | 103 online_path, '1') |
| 93 WaitFor(lambda: ForceCpuOnline(online_path)) | 104 |
| 94 else: | 105 # Double check all cores stayed online. |
| 95 ResetCpu(online_path) | 106 time.sleep(0.25) |
| 107 if not self._AllCpusAreOnline(): |
| 108 raise RuntimeError('Failed to force CPUs online') |
| OLD | NEW |