Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(639)

Side by Side Diff: build/android/pylib/perf/perf_control.py

Issue 732473003: A more robust way to set/query CPU properties on devices (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: second revision Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | build/android/pylib/perf/perf_control_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« no previous file with comments | « no previous file | build/android/pylib/perf/perf_control_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698