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 | 6 |
7 from telemetry import decorators | 7 from telemetry import decorators |
8 from telemetry.core import cros_interface | 8 from telemetry.core import cros_interface |
9 from telemetry.core import platform | 9 from telemetry.core import platform |
10 from telemetry.core import util | 10 from telemetry.core import util |
11 from telemetry.internal.forwarders import cros_forwarder | 11 from telemetry.internal.forwarders import cros_forwarder |
12 from telemetry.internal.platform import cros_device | 12 from telemetry.internal.platform import cros_device |
13 from telemetry.internal.platform import linux_based_platform_backend | 13 from telemetry.internal.platform import linux_based_platform_backend |
14 from telemetry.internal.platform.power_monitor import cros_power_monitor | 14 from telemetry.internal.platform.power_monitor import cros_power_monitor |
15 from telemetry.internal.util import ps_util | 15 from telemetry.internal.util import ps_util |
| 16 from telemetry.internal.util import repeating_timer |
16 | 17 |
17 | 18 |
18 class CrosPlatformBackend( | 19 class CrosPlatformBackend( |
19 linux_based_platform_backend.LinuxBasedPlatformBackend): | 20 linux_based_platform_backend.LinuxBasedPlatformBackend): |
20 def __init__(self, device=None): | 21 def __init__(self, device=None): |
21 super(CrosPlatformBackend, self).__init__(device) | 22 super(CrosPlatformBackend, self).__init__(device) |
22 if device and not device.is_local: | 23 if device and not device.is_local: |
23 self._cri = cros_interface.CrOSInterface( | 24 self._cri = cros_interface.CrOSInterface( |
24 device.host_name, device.ssh_port, device.ssh_identity) | 25 device.host_name, device.ssh_port, device.ssh_identity) |
25 self._cri.TryLogin() | 26 self._cri.TryLogin() |
26 else: | 27 else: |
27 self._cri = cros_interface.CrOSInterface() | 28 self._cri = cros_interface.CrOSInterface() |
28 self._powermonitor = cros_power_monitor.CrosPowerMonitor(self) | 29 self._powermonitor = cros_power_monitor.CrosPowerMonitor(self) |
| 30 self._InitialiseInteractiveBoost() |
29 | 31 |
30 @classmethod | 32 @classmethod |
31 def IsPlatformBackendForHost(cls): | 33 def IsPlatformBackendForHost(cls): |
32 return util.IsRunningOnCrosDevice() | 34 return util.IsRunningOnCrosDevice() |
33 | 35 |
34 @classmethod | 36 @classmethod |
35 def SupportsDevice(cls, device): | 37 def SupportsDevice(cls, device): |
36 return isinstance(device, cros_device.CrOSDevice) | 38 return isinstance(device, cros_device.CrOSDevice) |
37 | 39 |
38 @classmethod | 40 @classmethod |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 logging.warning( | 163 logging.warning( |
162 'PathExists: params timeout and retries are not support on CrOS.') | 164 'PathExists: params timeout and retries are not support on CrOS.') |
163 return self._cri.FileExistsOnDevice(path) | 165 return self._cri.FileExistsOnDevice(path) |
164 | 166 |
165 def CanTakeScreenshot(self): | 167 def CanTakeScreenshot(self): |
166 # crbug.com/609001: screenshots don't work on VMs. | 168 # crbug.com/609001: screenshots don't work on VMs. |
167 return not self.cri.IsRunningOnVM() | 169 return not self.cri.IsRunningOnVM() |
168 | 170 |
169 def TakeScreenshot(self, file_path): | 171 def TakeScreenshot(self, file_path): |
170 return self._cri.TakeScreenshot(file_path) | 172 return self._cri.TakeScreenshot(file_path) |
| 173 |
| 174 def _InitialiseInteractiveBoost(self): |
| 175 """On ChromeOS on ARM, the kernel boosts CPU frequency when an input (key |
| 176 press, touch event, etc) is received. For simulated inputs, this doesn't |
| 177 happen, so this functionality activates the same boost mechanism by |
| 178 using the sysfs controls. |
| 179 The boost should be turned on for specific interaction events, for the |
| 180 duration of the interaction, to simulate normal system behaviour. |
| 181 On systems which do not support frequency boost, this has no effect |
| 182 (consistent with normal non-simulated behaviour). |
| 183 """ |
| 184 self._boost_duration = None |
| 185 self._boost_timer = None |
| 186 governor = self.GetFileContents( |
| 187 "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor").strip() |
| 188 if governor != "interactive": |
| 189 logging.warning("no interactive cpufreq governor present, not setting" |
| 190 " up interactivity boost") |
| 191 return |
| 192 core = 0 |
| 193 durations = {} |
| 194 while True: |
| 195 path = "/sys/devices/system/cpu/cpu" + str(core) + "/cpufreq/interactive" |
| 196 if self.PathExists(path + "/boostpulse_duration") \ |
| 197 and self.PathExists(path + "/boostpulse"): |
| 198 durations[core] = self.GetFileContents(path |
| 199 + "/boostpulse_duration").strip() |
| 200 else: |
| 201 break |
| 202 core = core + 1 |
| 203 if durations: |
| 204 # Set timer to drive boost every 9 minutes. It lasts for 10 minutes |
| 205 # so this ensures it will remain constantly on with minimal |
| 206 # overhead, until turned off. |
| 207 self._boost_timer = repeating_timer.RepeatingTimer( |
| 208 lambda: self._WriteCPUFreqControl("boostpulse", "1"), 9 * 60) |
| 209 self._boost_duration = durations |
| 210 logging.info("interactivity boost set up") |
| 211 else: |
| 212 logging.info("kernel does not support frequency boost controls," |
| 213 "interactivity boost could not be set up") |
| 214 |
| 215 def _WriteCPUFreqControl(self, leaf, content): |
| 216 if self._boost_duration is not None: |
| 217 cmds = [] |
| 218 for core in self._boost_duration: |
| 219 path = "/sys/devices/system/cpu/cpu" + str(core) \ |
| 220 + "/cpufreq/interactive" |
| 221 value = content if not isinstance(content, dict) else content[core] |
| 222 if cmds: |
| 223 cmds.append(';') |
| 224 cmds.extend(['echo', value, '>', path + "/" + leaf]) |
| 225 self.RunCommand(cmds) |
| 226 |
| 227 def SetInteractivityBoost(self, enable): |
| 228 if self._boost_duration is not None \ |
| 229 and self._boost_timer.IsRunning() != enable: |
| 230 logging.info('SetInteractivityBoost: %s boost on %d cores', |
| 231 ("enabling" if enable else "disabling"), len(self._boost_duration)) |
| 232 |
| 233 # enable a timer to initiate a regular boost pulse |
| 234 # |
| 235 # the boost pulse duration and timer are such that boost will remain |
| 236 # constantly enabled |
| 237 if enable: |
| 238 # set the boost duration to 10 minutes |
| 239 self._WriteCPUFreqControl("boostpulse_duration", "600000000") |
| 240 self._WriteCPUFreqControl("boostpulse", "1") |
| 241 self._boost_timer.Start() |
| 242 else: |
| 243 # set the boost duration back to the default setting if disabling. |
| 244 self._WriteCPUFreqControl("boostpulse_duration", self._boost_duration) |
| 245 self._boost_timer.Stop() |
| 246 # this ensures that the pulse continues for the default pulse duration |
| 247 # (typically 80 ms) after the input event ends - as would normally |
| 248 # happen with real user interaction. |
| 249 self._WriteCPUFreqControl("boostpulse", "1") |
OLD | NEW |