Adding Peter Thatcher to the owners file.
[chromium-blink-merge.git] / build / android / pylib / perf / perf_control.py
blob97fa4a7ee16c55709aa1658fdee466bfbe942a95
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
3 # found in the LICENSE file.
5 import atexit
6 import logging
8 from pylib import android_commands
9 from pylib.device import device_utils
11 class PerfControl(object):
12 """Provides methods for setting the performance mode of a device."""
13 _CPU_PATH = '/sys/devices/system/cpu'
14 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
16 def __init__(self, device):
17 # TODO(jbudorick) Remove once telemetry gets switched over.
18 if isinstance(device, android_commands.AndroidCommands):
19 device = device_utils.DeviceUtils(device)
20 self._device = device
21 # this will raise an AdbCommandFailedError if no CPU files are found
22 self._cpu_files = self._device.RunShellCommand(
23 'ls -d cpu[0-9]*', cwd=self._CPU_PATH, check_return=True, as_root=True)
24 assert self._cpu_files, 'Failed to detect CPUs.'
25 self._cpu_file_list = ' '.join(self._cpu_files)
26 logging.info('CPUs found: %s', self._cpu_file_list)
27 self._have_mpdecision = self._device.FileExists('/system/bin/mpdecision')
29 def SetHighPerfMode(self):
30 """Sets the highest stable performance mode for the device."""
31 if not self._device.old_interface.IsRootEnabled():
32 message = 'Need root for performance mode. Results may be NOISY!!'
33 logging.warning(message)
34 # Add an additional warning at exit, such that it's clear that any results
35 # may be different/noisy (due to the lack of intended performance mode).
36 atexit.register(logging.warning, message)
37 return
39 product_model = self._device.product_model
40 # TODO(epenner): Enable on all devices (http://crbug.com/383566)
41 if 'Nexus 4' == product_model:
42 self._ForceAllCpusOnline(True)
43 if not self._AllCpusAreOnline():
44 logging.warning('Failed to force CPUs online. Results may be NOISY!')
45 self._SetScalingGovernorInternal('performance')
46 elif 'Nexus 5' == product_model:
47 self._ForceAllCpusOnline(True)
48 if not self._AllCpusAreOnline():
49 logging.warning('Failed to force CPUs online. Results may be NOISY!')
50 self._SetScalingGovernorInternal('performance')
51 self._SetScalingMaxFreq(1190400)
52 self._SetMaxGpuClock(200000000)
53 else:
54 self._SetScalingGovernorInternal('performance')
56 def SetPerfProfilingMode(self):
57 """Enables all cores for reliable perf profiling."""
58 self._ForceAllCpusOnline(True)
59 self._SetScalingGovernorInternal('performance')
60 if not self._AllCpusAreOnline():
61 if not self._device.old_interface.IsRootEnabled():
62 raise RuntimeError('Need root to force CPUs online.')
63 raise RuntimeError('Failed to force CPUs online.')
65 def SetDefaultPerfMode(self):
66 """Sets the performance mode for the device to its default mode."""
67 if not self._device.old_interface.IsRootEnabled():
68 return
69 product_model = self._device.product_model
70 if 'Nexus 5' == product_model:
71 if self._AllCpusAreOnline():
72 self._SetScalingMaxFreq(2265600)
73 self._SetMaxGpuClock(450000000)
75 governor_mode = {
76 'GT-I9300': 'pegasusq',
77 'Galaxy Nexus': 'interactive',
78 'Nexus 4': 'ondemand',
79 'Nexus 5': 'ondemand',
80 'Nexus 7': 'interactive',
81 'Nexus 10': 'interactive'
82 }.get(product_model, 'ondemand')
83 self._SetScalingGovernorInternal(governor_mode)
84 self._ForceAllCpusOnline(False)
86 def GetCpuInfo(self):
87 online = (output.rstrip() == '1' and status == 0
88 for (_, output, status) in self._ForEachCpu('cat "$CPU/online"'))
89 governor = (output.rstrip() if status == 0 else None
90 for (_, output, status)
91 in self._ForEachCpu('cat "$CPU/cpufreq/scaling_governor"'))
92 return zip(self._cpu_files, online, governor)
94 def _ForEachCpu(self, cmd):
95 script = '; '.join([
96 'for CPU in %s' % self._cpu_file_list,
97 'do %s' % cmd,
98 'echo -n "%~%$?%~%"',
99 'done'
101 output = self._device.RunShellCommand(
102 script, cwd=self._CPU_PATH, check_return=True, as_root=True)
103 output = '\n'.join(output).split('%~%')
104 return zip(self._cpu_files, output[0::2], (int(c) for c in output[1::2]))
106 def _WriteEachCpuFile(self, path, value):
107 results = self._ForEachCpu(
108 'test -e "$CPU/{path}" && echo {value} > "$CPU/{path}"'.format(
109 path=path, value=value))
110 cpus = ' '.join(cpu for (cpu, _, status) in results if status == 0)
111 if cpus:
112 logging.info('Successfully set %s to %r on: %s', path, value, cpus)
113 else:
114 logging.warning('Failed to set %s to %r on any cpus')
116 def _SetScalingGovernorInternal(self, value):
117 self._WriteEachCpuFile('cpufreq/scaling_governor', value)
119 def _SetScalingMaxFreq(self, value):
120 self._WriteEachCpuFile('cpufreq/scaling_max_freq', '%d' % value)
122 def _SetMaxGpuClock(self, value):
123 self._device.WriteFile('/sys/class/kgsl/kgsl-3d0/max_gpuclk',
124 str(value),
125 as_root=True)
127 def _AllCpusAreOnline(self):
128 results = self._ForEachCpu('cat "$CPU/online"')
129 # TODO(epenner): Investigate why file may be missing
130 # (http://crbug.com/397118)
131 return all(output.rstrip() == '1' and status == 0
132 for (cpu, output, status) in results
133 if cpu != 'cpu0')
135 def _ForceAllCpusOnline(self, force_online):
136 """Enable all CPUs on a device.
138 Some vendors (or only Qualcomm?) hot-plug their CPUs, which can add noise
139 to measurements:
140 - In perf, samples are only taken for the CPUs that are online when the
141 measurement is started.
142 - The scaling governor can't be set for an offline CPU and frequency scaling
143 on newly enabled CPUs adds noise to both perf and tracing measurements.
145 It appears Qualcomm is the only vendor that hot-plugs CPUs, and on Qualcomm
146 this is done by "mpdecision".
149 if self._have_mpdecision:
150 script = 'stop mpdecision' if force_online else 'start mpdecision'
151 self._device.RunShellCommand(script, check_return=True, as_root=True)
153 if not self._have_mpdecision and not self._AllCpusAreOnline():
154 logging.warning('Unexpected cpu hot plugging detected.')
156 if force_online:
157 self._ForEachCpu('echo 1 > "$CPU/online"')