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 def NumCpuCores(self): |
Sami
2014/06/19 18:12:42
nit: This could be a @property (and perhaps privat
epenner
2014/06/19 23:58:50
Done.
| |
33 return self._kernel_max + 1 | |
34 | |
35 def CpuOnlineFormat(self): | |
36 return self._CPU_ONLINE_FMT | |
Sami
2014/06/19 18:12:43
These two functions are only used in test code. I
epenner
2014/06/19 23:58:50
Yeah this was just for the test since accessing pr
pasko
2014/06/20 13:48:36
One other option:
CpuOnlineFormatForTesting()
| |
37 | |
38 def ScalingGovernorFormat(self): | |
39 return self._SCALING_GOVERNOR_FMT | |
32 | 40 |
33 def SetHighPerfMode(self): | 41 def SetHighPerfMode(self): |
42 # TODO(epenner): Enable on all devices and remove ForceHighPerfMode | |
43 # http://crbug.com/383566 | |
44 if 'Nexus 4' == self._device.old_interface.GetProductModel(): | |
45 self._ForceAllCpusOnline(True) | |
46 self._SetScalingGovernorInternal('performance') | |
47 | |
48 def ForceHighPerfMode(self): | |
pasko
2014/06/19 17:48:31
I think SetProfilingMode() would be more readable
epenner
2014/06/19 23:58:49
Changed to SetPerfProfilingMode.
| |
34 """Sets the highest possible performance mode for the device.""" | 49 """Sets the highest possible performance mode for the device.""" |
50 self._ForceAllCpusOnline(True) | |
35 self._SetScalingGovernorInternal('performance') | 51 self._SetScalingGovernorInternal('performance') |
36 | 52 |
37 def SetDefaultPerfMode(self): | 53 def SetDefaultPerfMode(self): |
38 """Sets the performance mode for the device to its default mode.""" | 54 """Sets the performance mode for the device to its default mode.""" |
39 product_model = self._device.old_interface.GetProductModel() | 55 product_model = self._device.old_interface.GetProductModel() |
40 governor_mode = { | 56 governor_mode = { |
41 'GT-I9300': 'pegasusq', | 57 'GT-I9300': 'pegasusq', |
42 'Galaxy Nexus': 'interactive', | 58 'Galaxy Nexus': 'interactive', |
43 'Nexus 4': 'ondemand', | 59 'Nexus 4': 'ondemand', |
44 'Nexus 7': 'interactive', | 60 'Nexus 7': 'interactive', |
45 'Nexus 10': 'interactive' | 61 'Nexus 10': 'interactive' |
46 }.get(product_model, 'ondemand') | 62 }.get(product_model, 'ondemand') |
47 self._SetScalingGovernorInternal(governor_mode) | 63 self._SetScalingGovernorInternal(governor_mode) |
48 | 64 self._ForceAllCpusOnline(False) |
49 def RestoreOriginalPerfMode(self): | |
50 """Resets the original performance mode of the device.""" | |
51 self._SetScalingGovernorInternal(self._original_scaling_governor) | |
52 | 65 |
53 def _SetScalingGovernorInternal(self, value): | 66 def _SetScalingGovernorInternal(self, value): |
54 for cpu in range(self._kernel_max + 1): | 67 for cpu in range(self.NumCpuCores()): |
55 scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu | 68 scaling_governor_file = PerfControl._SCALING_GOVERNOR_FMT % cpu |
56 if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): | 69 if self._device.old_interface.FileExistsOnDevice(scaling_governor_file): |
57 logging.info('Writing scaling governor mode \'%s\' -> %s', | 70 logging.info('Writing scaling governor mode \'%s\' -> %s', |
58 value, scaling_governor_file) | 71 value, scaling_governor_file) |
59 self._device.old_interface.SetProtectedFileContents( | 72 self._device.old_interface.SetProtectedFileContents( |
60 scaling_governor_file, value) | 73 scaling_governor_file, value) |
61 | 74 |
62 def ForceAllCpusOnline(self, force_online): | 75 def _AllCpusAreOnline(self): |
63 """Force all CPUs on a device to be online. | 76 for cpu in range(self.NumCpuCores()): |
77 online_path = PerfControl._CPU_ONLINE_FMT % cpu | |
78 if self._device.old_interface.GetFileContents(online_path)[0] == '0': | |
79 return False | |
80 return True | |
64 | 81 |
65 Force every CPU core on an Android device to remain online, or return the | 82 def _ForceAllCpusOnline(self, force_online): |
66 cores under system power management control. This is needed to work around | 83 """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 | 84 |
70 Args: | 85 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 | 86 to measurements: |
72 system power management control. | 87 - In perf, samples are only taken for the CPUs that are online when the |
88 measurement is started. | |
89 - The scaling governor can't be set for an offline CPU and frequency scaling | |
90 on newly enabled CPUs adds noise to both perf and tracing measurements. | |
91 | |
92 It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm | |
93 this is done by "mpdecision". | |
94 | |
73 """ | 95 """ |
74 def ForceCpuOnline(online_path): | 96 if self._have_mpdecision: |
75 script = 'chmod 644 {0}; echo 1 > {0}; chmod 444 {0}'.format(online_path) | 97 script = 'stop mpdecision' if force_online else 'start mpdecision' |
76 self._device.RunShellCommand(script, root=True) | 98 self._device.RunShellCommand(script, root=True) |
77 return self._device.old_interface.GetFileContents(online_path)[0] == '1' | |
78 | 99 |
79 def ResetCpu(online_path): | 100 if not self._have_mpdecision and not self._AllCpusAreOnline(): |
80 self._device.RunShellCommand('chmod 644 %s' % online_path, root=True) | 101 logging.warn('Unexpected cpu hot plugging detected.') |
pasko
2014/06/19 17:48:31
loggin.warn is deprecated, please use logging.warn
epenner
2014/06/19 23:58:50
Done.
| |
81 | 102 |
82 def WaitFor(condition): | 103 if not force_online: |
83 for _ in range(100): | 104 return |
84 if condition(): | |
85 return | |
86 time.sleep(0.1) | |
87 raise RuntimeError('Timed out') | |
88 | 105 |
89 cpu_online_files = self._device.RunShellCommand( | 106 for cpu in range(self.NumCpuCores()): |
90 'ls -d /sys/devices/system/cpu/cpu[0-9]*/online') | 107 online_path = PerfControl._CPU_ONLINE_FMT % cpu |
91 for online_path in cpu_online_files: | 108 self._device.old_interface.SetProtectedFileContents( |
92 if force_online: | 109 online_path, "1") |
Sami
2014/06/19 18:12:42
nit: '1' instead of "1"
epenner
2014/06/19 23:58:49
Done.
| |
93 WaitFor(lambda: ForceCpuOnline(online_path)) | 110 |
94 else: | 111 # Double check all cores stayed online. |
95 ResetCpu(online_path) | 112 time.sleep(0.25) |
113 if not self._AllCpusAreOnline(): | |
114 raise RuntimeError('Failed to force CPUs online') | |
OLD | NEW |