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. 4 5 import logging 6 7 from telemetry import decorators 8 from telemetry.core import cros_interface 9 from telemetry.core import platform 10 from telemetry.core import util 11 from telemetry.internal.forwarders import cros_forwarder 12 from telemetry.internal.platform import cros_device 13 from telemetry.internal.platform import linux_based_platform_backend 14 from telemetry.internal.platform.power_monitor import cros_power_monitor 15 from telemetry.internal.util import ps_util 16 17 18 class CrosPlatformBackend( 19 linux_based_platform_backend.LinuxBasedPlatformBackend): 20 def __init__(self, device=None): 21 super(CrosPlatformBackend, self).__init__(device) 22 if device and not device.is_local: 23 self._cri = cros_interface.CrOSInterface( 24 device.host_name, device.ssh_port, device.ssh_identity) 25 self._cri.TryLogin() 26 else: 27 self._cri = cros_interface.CrOSInterface() 28 self._powermonitor = cros_power_monitor.CrosPowerMonitor(self) 29 30 @classmethod 31 def IsPlatformBackendForHost(cls): 32 return util.IsRunningOnCrosDevice() 33 34 @classmethod 35 def SupportsDevice(cls, device): 36 return isinstance(device, cros_device.CrOSDevice) 37 38 @classmethod 39 def CreatePlatformForDevice(cls, device, finder_options): 40 assert cls.SupportsDevice(device) 41 return platform.Platform(CrosPlatformBackend(device)) 42 43 @property 44 def cri(self): 45 return self._cri 46 47 @property 48 def forwarder_factory(self): 49 if not self._forwarder_factory: 50 self._forwarder_factory = cros_forwarder.CrOsForwarderFactory(self._cri) 51 return self._forwarder_factory 52 53 def GetRemotePort(self, port): 54 if self._cri.local: 55 return port 56 return self._cri.GetRemotePort() 57 58 def IsThermallyThrottled(self): 59 raise NotImplementedError() 60 61 def HasBeenThermallyThrottled(self): 62 raise NotImplementedError() 63 64 def RunCommand(self, args): 65 if not isinstance(args, list): 66 args = [args] 67 stdout, stderr = self._cri.RunCmdOnDevice(args) 68 if stderr: 69 raise IOError('Failed to run: cmd = %s, stderr = %s' % 70 (str(args), stderr)) 71 return stdout 72 73 def GetFileContents(self, filename): 74 try: 75 return self.RunCommand(['cat', filename]) 76 except AssertionError: 77 return '' 78 79 def GetPsOutput(self, columns, pid=None): 80 return ps_util.GetPsOutputWithPlatformBackend(self, columns, pid) 81 82 @staticmethod 83 def ParseCStateSample(sample): 84 sample_stats = {} 85 for cpu in sample: 86 values = sample[cpu].splitlines() 87 # There are three values per state after excluding the single time value. 88 num_states = (len(values) - 1) / 3 89 names = values[:num_states] 90 times = values[num_states:2 * num_states] 91 latencies = values[2 * num_states:] 92 # The last line in the sample contains the time. 93 cstates = {'C0': int(values[-1]) * 10 ** 6} 94 for i, state in enumerate(names): 95 if names[i] == 'POLL' and not int(latencies[i]): 96 # C0 state. Kernel stats aren't right, so calculate by 97 # subtracting all other states from total time (using epoch 98 # timer since we calculate differences in the end anyway). 99 # NOTE: Only x86 lists C0 under cpuidle, ARM does not. 100 continue 101 cstates['C0'] -= int(times[i]) 102 if names[i] == '<null>': 103 # Kernel race condition that can happen while a new C-state gets 104 # added (e.g. AC->battery). Don't know the 'name' of the state 105 # yet, but its 'time' would be 0 anyway. 106 continue 107 cstates[state] = int(times[i]) 108 sample_stats[cpu] = cstates 109 return sample_stats 110 111 def GetDeviceTypeName(self): 112 return self._cri.GetDeviceTypeName() 113 114 @decorators.Cache 115 def GetArchName(self): 116 return self._cri.GetArchName() 117 118 def GetOSName(self): 119 return 'chromeos' 120 121 def GetOSVersionName(self): 122 return '' # TODO: Implement this. 123 124 def GetChildPids(self, pid): 125 """Returns a list of child pids of |pid|.""" 126 all_process_info = self._cri.ListProcesses() 127 processes = [(curr_pid, curr_ppid, curr_state) 128 for curr_pid, _, curr_ppid, curr_state in all_process_info] 129 return ps_util.GetChildPids(processes, pid) 130 131 def GetCommandLine(self, pid): 132 procs = self._cri.ListProcesses() 133 return next((proc[1] for proc in procs if proc[0] == pid), None) 134 135 def CanFlushIndividualFilesFromSystemCache(self): 136 return True 137 138 def FlushEntireSystemCache(self): 139 raise NotImplementedError() 140 141 def FlushSystemCacheForDirectory(self, directory): 142 flush_command = ( 143 '/usr/local/telemetry/src/src/out/Release/clear_system_cache') 144 self.RunCommand(['chmod', '+x', flush_command]) 145 self.RunCommand([flush_command, '--recurse', directory]) 146 147 def CanMonitorPower(self): 148 return self._powermonitor.CanMonitorPower() 149 150 def StartMonitoringPower(self, browser): 151 self._powermonitor.StartMonitoringPower(browser) 152 153 def StopMonitoringPower(self): 154 return self._powermonitor.StopMonitoringPower() 155 156 def PathExists(self, path, timeout=None, retries=None): 157 if timeout or retries: 158 logging.warning( 159 'PathExists: params timeout and retries are not support on CrOS.') 160 return self._cri.FileExistsOnDevice(path) 161 162 def CanTakeScreenshot(self): 163 # crbug.com/609001: screenshots don't work on VMs. 164 return not self.cri.IsRunningOnVM() 165 166 def TakeScreenshot(self, file_path): 167 return self._cri.TakeScreenshot(file_path) 168