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 import posixpath | |
7 | 8 |
8 from pylib import android_commands | 9 from pylib import android_commands |
10 from pylib import cmd_helper | |
9 from pylib.device import device_utils | 11 from pylib.device import device_utils |
10 | 12 |
11 class PerfControl(object): | 13 class PerfControl(object): |
12 """Provides methods for setting the performance mode of a device.""" | 14 """Provides methods for setting the performance mode of a device.""" |
13 _SCALING_GOVERNOR_FMT = ( | 15 _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' | 16 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' |
17 | 17 |
18 def __init__(self, device): | 18 def __init__(self, device): |
19 # TODO(jbudorick) Remove once telemetry gets switched over. | 19 # TODO(jbudorick) Remove once telemetry gets switched over. |
20 if isinstance(device, android_commands.AndroidCommands): | 20 if isinstance(device, android_commands.AndroidCommands): |
21 device = device_utils.DeviceUtils(device) | 21 device = device_utils.DeviceUtils(device) |
22 self._device = device | 22 self._device = device |
23 cpu_files = self._device.RunShellCommand( | 23 self._cpu_files = self._device.RunShellCommand( |
24 'ls -d /sys/devices/system/cpu/cpu[0-9]*') | 24 'cd %s && ls -d cpu[0-9]*' % self._CPU_PATH, |
Sami
2014/11/18 13:24:04
nit: You could replace "ls -d" with "echo" here.
perezju
2014/11/18 17:42:41
"ls -d" is more robust. If for whatever reason the
| |
25 self._num_cpu_cores = len(cpu_files) | 25 check_return=True, as_root=True) |
perezju
2014/11/17 21:20:56
This will already throw an exception if either the
| |
26 assert self._num_cpu_cores > 0, 'Failed to detect CPUs.' | 26 self._cpu_file_list = ' '.join(self._cpu_files) |
27 logging.info('Number of CPUs: %d', self._num_cpu_cores) | 27 logging.info('CPUs found: %s', self._cpu_file_list) |
28 self._have_mpdecision = self._device.FileExists('/system/bin/mpdecision') | 28 self._have_mpdecision = self._device.FileExists('/system/bin/mpdecision') |
29 | 29 |
30 def SetHighPerfMode(self): | 30 def SetHighPerfMode(self): |
31 """Sets the highest stable performance mode for the device.""" | 31 """Sets the highest stable performance mode for the device.""" |
32 if not self._device.old_interface.IsRootEnabled(): | 32 if not self._device.old_interface.IsRootEnabled(): |
33 message = 'Need root for performance mode. Results may be NOISY!!' | 33 message = 'Need root for performance mode. Results may be NOISY!!' |
34 logging.warning(message) | 34 logging.warning(message) |
35 # Add an additional warning at exit, such that it's clear that any results | 35 # 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). | 36 # may be different/noisy (due to the lack of intended performance mode). |
37 atexit.register(logging.warning, message) | 37 atexit.register(logging.warning, message) |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
77 'GT-I9300': 'pegasusq', | 77 'GT-I9300': 'pegasusq', |
78 'Galaxy Nexus': 'interactive', | 78 'Galaxy Nexus': 'interactive', |
79 'Nexus 4': 'ondemand', | 79 'Nexus 4': 'ondemand', |
80 'Nexus 5': 'ondemand', | 80 'Nexus 5': 'ondemand', |
81 'Nexus 7': 'interactive', | 81 'Nexus 7': 'interactive', |
82 'Nexus 10': 'interactive' | 82 'Nexus 10': 'interactive' |
83 }.get(product_model, 'ondemand') | 83 }.get(product_model, 'ondemand') |
84 self._SetScalingGovernorInternal(governor_mode) | 84 self._SetScalingGovernorInternal(governor_mode) |
85 self._ForceAllCpusOnline(False) | 85 self._ForceAllCpusOnline(False) |
86 | 86 |
87 def GetCpuInfo(self): | |
88 online = (out.rstrip() == '1' and code == 0 | |
Sami
2014/11/18 13:24:04
nit: I think something like |exit_status| may be a
perezju
2014/11/18 17:42:42
Acknowledged. I went for |status| just to keep it
| |
89 for (_, out, code) in self._ForEachCpu('cat "$CPU/online"')) | |
90 governor = (out.rstrip() if code == 0 else None | |
91 for (_, out, code) in | |
92 self._ForEachCpu('cat "$CPU/cpufreq/scaling_governor"')) | |
93 return zip(self._cpu_files, online, governor) | |
94 | |
95 def _ForEachCpu(self, cmd): | |
96 script = ('cd %s\n' | |
97 'for CPU in %s; do\n' | |
98 ' %s; echo -n "%%~%%$?%%~%%"\n' | |
jbudorick
2014/11/18 02:29:38
What's the reasoning behind "%~%$?%~%"?
Do we jus
Sami
2014/11/18 13:24:04
Alternatively we could append the exit codes to a
perezju
2014/11/18 17:42:42
The reasoning is basically just that.
Also I thin
| |
99 'done' % (self._CPU_PATH, self._cpu_file_list, cmd)) | |
jbudorick
2014/11/18 02:29:38
I think this would be more readable if you handled
perezju
2014/11/18 17:42:42
Acknowledged.
| |
100 output = self._device.RunShellCommand(script, check_return=True, | |
101 as_root=True, raw_output=True) | |
jbudorick
2014/11/18 02:29:38
I don't think this justifies adding raw_output to
| |
102 output = output.split('%~%') | |
103 return zip(self._cpu_files, output[0::2], (int(c) for c in output[1::2])) | |
perezju
2014/11/17 21:20:56
This is where the magic happens now, for each cpu
| |
104 | |
105 def _WriteEachCpuFile(self, path, value): | |
106 cmd = ('test -e %(path)s && echo %(value)s > %(path)s' % { | |
107 'path': cmd_helper.DoubleQuote(posixpath.join('$CPU', path)), | |
jbudorick
2014/11/18 02:29:37
posixpath.join?
perezju
2014/11/18 17:42:42
ok, ok, _maybe_ I don't have to be this much defen
| |
108 'value': value}) | |
109 output = self._ForEachCpu(cmd) | |
110 cpus = ' '.join(cpu for (cpu, _, code) in output if code == 0) | |
111 if cpus: | |
112 logging.info('Succesfully set %s to %r on: %s', path, value, cpus) | |
113 else: | |
114 logging.warning('Failed to set %s to %r on any cpus') | |
115 | |
87 def _SetScalingGovernorInternal(self, value): | 116 def _SetScalingGovernorInternal(self, value): |
88 cpu_cores = ' '.join([str(x) for x in range(self._num_cpu_cores)]) | 117 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 | 118 |
96 def _SetScalingMaxFreq(self, value): | 119 def _SetScalingMaxFreq(self, value): |
97 cpu_cores = ' '.join([str(x) for x in range(self._num_cpu_cores)]) | 120 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 | 121 |
104 def _SetMaxGpuClock(self, value): | 122 def _SetMaxGpuClock(self, value): |
105 self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk', | 123 self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk', |
106 str(value), | 124 str(value), |
107 as_root=True) | 125 as_root=True) |
108 | 126 |
109 def _AllCpusAreOnline(self): | 127 def _AllCpusAreOnline(self): |
110 for cpu in range(1, self._num_cpu_cores): | 128 output = self._ForEachCpu('cat "$CPU/online"') |
111 online_path = PerfControl._CPU_ONLINE_FMT % cpu | 129 return all(out.rstrip() == '1' and code == 0 |
perezju
2014/11/17 21:20:56
code will be non-zero if the file does not exist,
| |
112 # TODO(epenner): Investigate why file may be missing | 130 for (cpu, out, code) in output |
113 # (http://crbug.com/397118) | 131 # TODO(epenner): Investigate why cpu0/online may be missing |
114 if not self._device.FileExists(online_path) or \ | 132 # (http://crbug.com/397118) |
115 self._device.ReadFile(online_path)[0] == '0': | 133 if cpu != 'cpu0') |
116 return False | |
117 return True | |
118 | 134 |
119 def _ForceAllCpusOnline(self, force_online): | 135 def _ForceAllCpusOnline(self, force_online): |
120 """Enable all CPUs on a device. | 136 """Enable all CPUs on a device. |
121 | 137 |
122 Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise | 138 Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise |
123 to measurements: | 139 to measurements: |
124 - In perf, samples are only taken for the CPUs that are online when the | 140 - In perf, samples are only taken for the CPUs that are online when the |
125 measurement is started. | 141 measurement is started. |
126 - The scaling governor can't be set for an offline CPU and frequency scaling | 142 - 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. | 143 on newly enabled CPUs adds noise to both perf and tracing measurements. |
128 | 144 |
129 It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm | 145 It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm |
130 this is done by "mpdecision". | 146 this is done by "mpdecision". |
131 | 147 |
132 """ | 148 """ |
133 if self._have_mpdecision: | 149 if self._have_mpdecision: |
134 script = 'stop mpdecision' if force_online else 'start mpdecision' | 150 script = 'stop mpdecision' if force_online else 'start mpdecision' |
135 self._device.RunShellCommand(script, as_root=True) | 151 self._device.RunShellCommand(script, check_return=True, as_root=True) |
136 | 152 |
137 if not self._have_mpdecision and not self._AllCpusAreOnline(): | 153 if not self._have_mpdecision and not self._AllCpusAreOnline(): |
138 logging.warning('Unexpected cpu hot plugging detected.') | 154 logging.warning('Unexpected cpu hot plugging detected.') |
139 | 155 |
140 if not force_online: | 156 if force_online: |
141 return | 157 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 |