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 5import logging 6 7from telemetry.core import cros_interface 8from telemetry.core import platform 9from telemetry.core import util 10from telemetry.internal import forwarders 11from telemetry.internal.forwarders import cros_forwarder 12from telemetry.internal.platform import cros_device 13from telemetry.internal.platform import linux_based_platform_backend 14from telemetry.internal.platform.power_monitor import cros_power_monitor 15from telemetry.internal.util import ps_util 16 17 18class 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 GetWprPortPairs(self): 59 """Return suitable port pairs to be used for web page replay.""" 60 default_local_ports = super(CrosPlatformBackend, self).GetWprPortPairs( 61 ).local_ports 62 return forwarders.PortPairs.Zip( 63 default_local_ports, 64 forwarders.PortSet( 65 http=self.GetRemotePort(default_local_ports.http), 66 https=self.GetRemotePort(default_local_ports.https), 67 dns=None)) 68 69 def IsThermallyThrottled(self): 70 raise NotImplementedError() 71 72 def HasBeenThermallyThrottled(self): 73 raise NotImplementedError() 74 75 def RunCommand(self, args): 76 if not isinstance(args, list): 77 args = [args] 78 stdout, stderr = self._cri.RunCmdOnDevice(args) 79 if stderr: 80 raise IOError('Failed to run: cmd = %s, stderr = %s' % 81 (str(args), stderr)) 82 return stdout 83 84 def GetFileContents(self, filename): 85 try: 86 return self.RunCommand(['cat', filename]) 87 except AssertionError: 88 return '' 89 90 def GetPsOutput(self, columns, pid=None): 91 return ps_util.GetPsOutputWithPlatformBackend(self, columns, pid) 92 93 @staticmethod 94 def ParseCStateSample(sample): 95 sample_stats = {} 96 for cpu in sample: 97 values = sample[cpu].splitlines() 98 # There are three values per state after excluding the single time value. 99 num_states = (len(values) - 1) / 3 100 names = values[:num_states] 101 times = values[num_states:2 * num_states] 102 latencies = values[2 * num_states:] 103 # The last line in the sample contains the time. 104 cstates = {'C0': int(values[-1]) * 10 ** 6} 105 for i, state in enumerate(names): 106 if names[i] == 'POLL' and not int(latencies[i]): 107 # C0 state. Kernel stats aren't right, so calculate by 108 # subtracting all other states from total time (using epoch 109 # timer since we calculate differences in the end anyway). 110 # NOTE: Only x86 lists C0 under cpuidle, ARM does not. 111 continue 112 cstates['C0'] -= int(times[i]) 113 if names[i] == '<null>': 114 # Kernel race condition that can happen while a new C-state gets 115 # added (e.g. AC->battery). Don't know the 'name' of the state 116 # yet, but its 'time' would be 0 anyway. 117 continue 118 cstates[state] = int(times[i]) 119 sample_stats[cpu] = cstates 120 return sample_stats 121 122 def GetOSName(self): 123 return 'chromeos' 124 125 def GetOSVersionName(self): 126 return '' # TODO: Implement this. 127 128 def GetChildPids(self, pid): 129 """Returns a list of child pids of |pid|.""" 130 all_process_info = self._cri.ListProcesses() 131 processes = [(curr_pid, curr_ppid, curr_state) 132 for curr_pid, _, curr_ppid, curr_state in all_process_info] 133 return ps_util.GetChildPids(processes, pid) 134 135 def GetCommandLine(self, pid): 136 procs = self._cri.ListProcesses() 137 return next((proc[1] for proc in procs if proc[0] == pid), None) 138 139 def CanFlushIndividualFilesFromSystemCache(self): 140 return True 141 142 def FlushEntireSystemCache(self): 143 raise NotImplementedError() 144 145 def FlushSystemCacheForDirectory(self, directory): 146 flush_command = ( 147 '/usr/local/telemetry/src/src/out/Release/clear_system_cache') 148 self.RunCommand(['chmod', '+x', flush_command]) 149 self.RunCommand([flush_command, '--recurse', directory]) 150 151 def CanMonitorPower(self): 152 return self._powermonitor.CanMonitorPower() 153 154 def StartMonitoringPower(self, browser): 155 self._powermonitor.StartMonitoringPower(browser) 156 157 def StopMonitoringPower(self): 158 return self._powermonitor.StopMonitoringPower() 159 160 def PathExists(self, path, timeout=None, retries=None): 161 if timeout or retries: 162 logging.warning( 163 'PathExists: params timeout and retries are not support on CrOS.') 164 return self._cri.FileExistsOnDevice(path) 165