Chromium Code Reviews| 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 atexit | 5 import atexit |
| 6 import logging | 6 import logging |
| 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 class PerfControl(object): | 11 class PerfControl(object): |
| 12 """Provides methods for setting the performance mode of a device.""" | 12 """Provides methods for setting the performance mode of a device.""" |
| 13 _SCALING_GOVERNOR_FMT = ( | 13 _CPU_PATH = '/sys/devices/system/cpu' |
| 14 '/sys/devices/system/cpu/cpu%d/cpufreq/scaling_governor') | |
| 15 _CPU_ONLINE_FMT = '/sys/devices/system/cpu/cpu%d/online' | |
| 16 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' | 14 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' |
| 17 | 15 |
| 18 def __init__(self, device): | 16 def __init__(self, device): |
| 19 # TODO(jbudorick) Remove once telemetry gets switched over. | 17 # TODO(jbudorick) Remove once telemetry gets switched over. |
| 20 if isinstance(device, android_commands.AndroidCommands): | 18 if isinstance(device, android_commands.AndroidCommands): |
| 21 device = device_utils.DeviceUtils(device) | 19 device = device_utils.DeviceUtils(device) |
| 22 self._device = device | 20 self._device = device |
| 23 cpu_files = self._device.RunShellCommand( | 21 self._cpu_files = self._device.RunShellCommand('ls -d cpu[0-9]*', |
|
jbudorick
2014/11/18 18:55:47
Same as below re param formatting.
| |
| 24 'ls -d /sys/devices/system/cpu/cpu[0-9]*') | 22 cwd=self._CPU_PATH, check_return=True, as_root=True) |
| 25 self._num_cpu_cores = len(cpu_files) | 23 self._cpu_file_list = ' '.join(self._cpu_files) |
| 26 assert self._num_cpu_cores > 0, 'Failed to detect CPUs.' | 24 logging.info('CPUs found: %s', self._cpu_file_list) |
| 27 logging.info('Number of CPUs: %d', self._num_cpu_cores) | |
| 28 self._have_mpdecision = self._device.FileExists('/system/bin/mpdecision') | 25 self._have_mpdecision = self._device.FileExists('/system/bin/mpdecision') |
| 29 | 26 |
| 30 def SetHighPerfMode(self): | 27 def SetHighPerfMode(self): |
| 31 """Sets the highest stable performance mode for the device.""" | 28 """Sets the highest stable performance mode for the device.""" |
| 32 if not self._device.old_interface.IsRootEnabled(): | 29 if not self._device.old_interface.IsRootEnabled(): |
| 33 message = 'Need root for performance mode. Results may be NOISY!!' | 30 message = 'Need root for performance mode. Results may be NOISY!!' |
| 34 logging.warning(message) | 31 logging.warning(message) |
| 35 # Add an additional warning at exit, such that it's clear that any results | 32 # Add an additional warning at exit, such that it's clear that any results |
| 36 # may be different/noisy (due to the lack of intended performance mode). | 33 # may be different/noisy (due to the lack of intended performance mode). |
| 37 atexit.register(logging.warning, message) | 34 atexit.register(logging.warning, message) |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 'GT-I9300': 'pegasusq', | 74 'GT-I9300': 'pegasusq', |
| 78 'Galaxy Nexus': 'interactive', | 75 'Galaxy Nexus': 'interactive', |
| 79 'Nexus 4': 'ondemand', | 76 'Nexus 4': 'ondemand', |
| 80 'Nexus 5': 'ondemand', | 77 'Nexus 5': 'ondemand', |
| 81 'Nexus 7': 'interactive', | 78 'Nexus 7': 'interactive', |
| 82 'Nexus 10': 'interactive' | 79 'Nexus 10': 'interactive' |
| 83 }.get(product_model, 'ondemand') | 80 }.get(product_model, 'ondemand') |
| 84 self._SetScalingGovernorInternal(governor_mode) | 81 self._SetScalingGovernorInternal(governor_mode) |
| 85 self._ForceAllCpusOnline(False) | 82 self._ForceAllCpusOnline(False) |
| 86 | 83 |
| 84 def GetCpuInfo(self): | |
| 85 online = (out.rstrip() == '1' and status == 0 | |
| 86 for (_, out, status) in self._ForEachCpu('cat "$CPU/online"')) | |
| 87 governor = (out.rstrip() if status == 0 else None | |
| 88 for (_, out, status) in | |
|
jbudorick
2014/11/18 18:55:47
nit: move "in" down a line.
| |
| 89 self._ForEachCpu('cat "$CPU/cpufreq/scaling_governor"')) | |
| 90 return zip(self._cpu_files, online, governor) | |
| 91 | |
| 92 def _ForEachCpu(self, cmd): | |
| 93 script = '; '.join([ | |
| 94 'for CPU in %s' % self._cpu_file_list, | |
| 95 'do %s' % cmd, | |
| 96 'echo -n "%~%$?%~%"', | |
| 97 'done' | |
| 98 ]) | |
| 99 output = self._device.RunShellCommand(script, cwd=self._CPU_PATH, | |
|
jbudorick
2014/11/18 18:55:47
Either:
1) put parameters on the same line as th
| |
| 100 check_return=True, as_root=True) | |
| 101 output = '\n'.join(output).split('%~%') | |
|
perezju
2014/11/18 17:42:42
I do get the argument of not littering RunShellCom
jbudorick
2014/11/18 18:55:47
I'm having a hard time coming up with a case where
| |
| 102 return zip(self._cpu_files, output[0::2], (int(c) for c in output[1::2])) | |
| 103 | |
| 104 def _WriteEachCpuFile(self, path, value): | |
| 105 output = self._ForEachCpu('test -e %(file)s && echo %(value)s > %(file)s' | |
|
jbudorick
2014/11/18 18:55:47
Same as above re param formatting.
| |
| 106 % {'file': '"$CPU/%s"' % path, 'value': value}) | |
| 107 cpus = ' '.join(cpu for (cpu, _, status) in output if status == 0) | |
| 108 if cpus: | |
| 109 logging.info('Succesfully set %s to %r on: %s', path, value, cpus) | |
| 110 else: | |
| 111 logging.warning('Failed to set %s to %r on any cpus') | |
| 112 | |
| 87 def _SetScalingGovernorInternal(self, value): | 113 def _SetScalingGovernorInternal(self, value): |
| 88 cpu_cores = ' '.join([str(x) for x in range(self._num_cpu_cores)]) | 114 self._WriteEachCpuFile('cpufreq/scaling_governor', value) |
| 89 script = ('for CPU in %s; do\n' | |
| 90 ' FILE="/sys/devices/system/cpu/cpu$CPU/cpufreq/scaling_governor"\n' | |
| 91 ' test -e $FILE && echo %s > $FILE\n' | |
| 92 'done\n') % (cpu_cores, value) | |
| 93 logging.info('Setting scaling governor mode: %s', value) | |
| 94 self._device.RunShellCommand(script, as_root=True) | |
| 95 | 115 |
| 96 def _SetScalingMaxFreq(self, value): | 116 def _SetScalingMaxFreq(self, value): |
| 97 cpu_cores = ' '.join([str(x) for x in range(self._num_cpu_cores)]) | 117 self._WriteEachCpuFile('cpufreq/scaling_max_freq', '%d' % value) |
| 98 script = ('for CPU in %s; do\n' | |
| 99 ' FILE="/sys/devices/system/cpu/cpu$CPU/cpufreq/scaling_max_freq"\n' | |
| 100 ' test -e $FILE && echo %d > $FILE\n' | |
| 101 'done\n') % (cpu_cores, value) | |
| 102 self._device.RunShellCommand(script, as_root=True) | |
| 103 | 118 |
| 104 def _SetMaxGpuClock(self, value): | 119 def _SetMaxGpuClock(self, value): |
| 105 self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk', | 120 self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk', |
| 106 str(value), | 121 str(value), |
| 107 as_root=True) | 122 as_root=True) |
| 108 | 123 |
| 109 def _AllCpusAreOnline(self): | 124 def _AllCpusAreOnline(self): |
| 110 for cpu in range(1, self._num_cpu_cores): | 125 output = self._ForEachCpu('cat "$CPU/online"') |
| 111 online_path = PerfControl._CPU_ONLINE_FMT % cpu | 126 # TODO(epenner): Investigate why file may be missing |
| 112 # TODO(epenner): Investigate why file may be missing | 127 # (http://crbug.com/397118) |
| 113 # (http://crbug.com/397118) | 128 return all(out.rstrip() == '1' and status == 0 |
| 114 if not self._device.FileExists(online_path) or \ | 129 for (cpu, out, status) in output |
| 115 self._device.ReadFile(online_path)[0] == '0': | 130 if cpu != 'cpu0') |
| 116 return False | |
| 117 return True | |
| 118 | 131 |
| 119 def _ForceAllCpusOnline(self, force_online): | 132 def _ForceAllCpusOnline(self, force_online): |
| 120 """Enable all CPUs on a device. | 133 """Enable all CPUs on a device. |
| 121 | 134 |
| 122 Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise | 135 Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise |
| 123 to measurements: | 136 to measurements: |
| 124 - In perf, samples are only taken for the CPUs that are online when the | 137 - In perf, samples are only taken for the CPUs that are online when the |
| 125 measurement is started. | 138 measurement is started. |
| 126 - The scaling governor can't be set for an offline CPU and frequency scaling | 139 - The scaling governor can't be set for an offline CPU and frequency scaling |
| 127 on newly enabled CPUs adds noise to both perf and tracing measurements. | 140 on newly enabled CPUs adds noise to both perf and tracing measurements. |
| 128 | 141 |
| 129 It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm | 142 It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm |
| 130 this is done by "mpdecision". | 143 this is done by "mpdecision". |
| 131 | 144 |
| 132 """ | 145 """ |
| 133 if self._have_mpdecision: | 146 if self._have_mpdecision: |
| 134 script = 'stop mpdecision' if force_online else 'start mpdecision' | 147 script = 'stop mpdecision' if force_online else 'start mpdecision' |
| 135 self._device.RunShellCommand(script, as_root=True) | 148 self._device.RunShellCommand(script, check_return=True, as_root=True) |
| 136 | 149 |
| 137 if not self._have_mpdecision and not self._AllCpusAreOnline(): | 150 if not self._have_mpdecision and not self._AllCpusAreOnline(): |
| 138 logging.warning('Unexpected cpu hot plugging detected.') | 151 logging.warning('Unexpected cpu hot plugging detected.') |
| 139 | 152 |
| 140 if not force_online: | 153 if force_online: |
| 141 return | 154 self._ForEachCpu('echo 1 > "$CPU/online"') |
| 142 | |
| 143 for cpu in range(self._num_cpu_cores): | |
| 144 online_path = PerfControl._CPU_ONLINE_FMT % cpu | |
| 145 self._device.WriteFile(online_path, '1', as_root=True) | |
| 146 | |
| OLD | NEW |