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 6import tempfile 7 8from telemetry import decorators 9from telemetry.core import exceptions 10from telemetry.core import platform 11from telemetry.core import util 12from telemetry.core import video 13from telemetry.core.platform import proc_supporting_platform_backend 14from telemetry.core.platform.power_monitor import android_ds2784_power_monitor 15from telemetry.core.platform.power_monitor import android_dumpsys_power_monitor 16from telemetry.core.platform.power_monitor import android_temperature_monitor 17from telemetry.core.platform.power_monitor import monsoon_power_monitor 18from telemetry.core.platform.power_monitor import power_monitor_controller 19from telemetry.core.platform.profiler import android_prebuilt_profiler_helper 20 21# Get build/android scripts into our path. 22util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') 23from pylib import screenshot # pylint: disable=F0401 24from pylib.perf import cache_control # pylint: disable=F0401 25from pylib.perf import perf_control # pylint: disable=F0401 26from pylib.perf import thermal_throttle # pylint: disable=F0401 27 28try: 29 from pylib.perf import surface_stats_collector # pylint: disable=F0401 30except Exception: 31 surface_stats_collector = None 32 33 34_HOST_APPLICATIONS = [ 35 'avconv', 36 'ipfw', 37 'perfhost', 38 ] 39 40 41class AndroidPlatformBackend( 42 proc_supporting_platform_backend.ProcSupportingPlatformBackend): 43 def __init__(self, device, no_performance_mode): 44 super(AndroidPlatformBackend, self).__init__() 45 self._device = device 46 self._surface_stats_collector = None 47 self._perf_tests_setup = perf_control.PerfControl(self._device) 48 self._thermal_throttle = thermal_throttle.ThermalThrottle(self._device) 49 self._no_performance_mode = no_performance_mode 50 self._raw_display_frame_rate_measurements = [] 51 self._can_access_protected_file_contents = \ 52 self._device.old_interface.CanAccessProtectedFileContents() 53 power_controller = power_monitor_controller.PowerMonitorController([ 54 monsoon_power_monitor.MonsoonPowerMonitor(), 55 android_ds2784_power_monitor.DS2784PowerMonitor(device), 56 android_dumpsys_power_monitor.DumpsysPowerMonitor(device), 57 ]) 58 self._powermonitor = android_temperature_monitor.AndroidTemperatureMonitor( 59 power_controller, device) 60 self._video_recorder = None 61 self._video_output = None 62 if self._no_performance_mode: 63 logging.warning('CPU governor will not be set!') 64 65 def IsRawDisplayFrameRateSupported(self): 66 return True 67 68 def StartRawDisplayFrameRateMeasurement(self): 69 assert not self._surface_stats_collector 70 # Clear any leftover data from previous timed out tests 71 self._raw_display_frame_rate_measurements = [] 72 self._surface_stats_collector = \ 73 surface_stats_collector.SurfaceStatsCollector(self._device) 74 self._surface_stats_collector.Start() 75 76 def StopRawDisplayFrameRateMeasurement(self): 77 if not self._surface_stats_collector: 78 return 79 80 self._surface_stats_collector.Stop() 81 for r in self._surface_stats_collector.GetResults(): 82 self._raw_display_frame_rate_measurements.append( 83 platform.Platform.RawDisplayFrameRateMeasurement( 84 r.name, r.value, r.unit)) 85 86 self._surface_stats_collector = None 87 88 def GetRawDisplayFrameRateMeasurements(self): 89 ret = self._raw_display_frame_rate_measurements 90 self._raw_display_frame_rate_measurements = [] 91 return ret 92 93 def SetFullPerformanceModeEnabled(self, enabled): 94 if self._no_performance_mode: 95 return 96 if enabled: 97 self._perf_tests_setup.SetHighPerfMode() 98 else: 99 self._perf_tests_setup.SetDefaultPerfMode() 100 101 def CanMonitorThermalThrottling(self): 102 return True 103 104 def IsThermallyThrottled(self): 105 return self._thermal_throttle.IsThrottled() 106 107 def HasBeenThermallyThrottled(self): 108 return self._thermal_throttle.HasBeenThrottled() 109 110 def GetCpuStats(self, pid): 111 if not self._can_access_protected_file_contents: 112 logging.warning('CPU stats cannot be retrieved on non-rooted device.') 113 return {} 114 return super(AndroidPlatformBackend, self).GetCpuStats(pid) 115 116 def GetCpuTimestamp(self): 117 if not self._can_access_protected_file_contents: 118 logging.warning('CPU timestamp cannot be retrieved on non-rooted device.') 119 return {} 120 return super(AndroidPlatformBackend, self).GetCpuTimestamp() 121 122 def PurgeUnpinnedMemory(self): 123 """Purges the unpinned ashmem memory for the whole system. 124 125 This can be used to make memory measurements more stable. Requires root. 126 """ 127 if not self._can_access_protected_file_contents: 128 logging.warning('Cannot run purge_ashmem. Requires a rooted device.') 129 return 130 131 if not android_prebuilt_profiler_helper.InstallOnDevice( 132 self._device, 'purge_ashmem'): 133 raise Exception('Error installing purge_ashmem.') 134 (status, output) = self._device.old_interface.GetAndroidToolStatusAndOutput( 135 android_prebuilt_profiler_helper.GetDevicePath('purge_ashmem'), 136 log_result=True) 137 if status != 0: 138 raise Exception('Error while purging ashmem: ' + '\n'.join(output)) 139 140 def GetMemoryStats(self, pid): 141 memory_usage = self._device.old_interface.GetMemoryUsageForPid(pid) 142 return {'ProportionalSetSize': memory_usage['Pss'] * 1024, 143 'SharedDirty': memory_usage['Shared_Dirty'] * 1024, 144 'PrivateDirty': memory_usage['Private_Dirty'] * 1024, 145 'VMPeak': memory_usage['VmHWM'] * 1024} 146 147 def GetIOStats(self, pid): 148 return {} 149 150 def GetChildPids(self, pid): 151 child_pids = [] 152 ps = self._GetPsOutput(['pid', 'name']) 153 for curr_pid, curr_name in ps: 154 if int(curr_pid) == pid: 155 name = curr_name 156 for curr_pid, curr_name in ps: 157 if curr_name.startswith(name) and curr_name != name: 158 child_pids.append(int(curr_pid)) 159 break 160 return child_pids 161 162 @decorators.Cache 163 def GetCommandLine(self, pid): 164 ps = self._GetPsOutput(['pid', 'name'], pid) 165 if not ps: 166 raise exceptions.ProcessGoneException() 167 return ps[0][1] 168 169 def GetOSName(self): 170 return 'android' 171 172 @decorators.Cache 173 def GetOSVersionName(self): 174 return self._device.old_interface.GetBuildId()[0] 175 176 def CanFlushIndividualFilesFromSystemCache(self): 177 return False 178 179 def FlushEntireSystemCache(self): 180 cache = cache_control.CacheControl(self._device) 181 cache.DropRamCaches() 182 183 def FlushSystemCacheForDirectory(self, directory, ignoring=None): 184 raise NotImplementedError() 185 186 def FlushDnsCache(self): 187 self._device.RunShellCommand('ndc resolver flushdefaultif', root=True) 188 189 def LaunchApplication( 190 self, application, parameters=None, elevate_privilege=False): 191 if application in _HOST_APPLICATIONS: 192 platform.GetHostPlatform().LaunchApplication( 193 application, parameters, elevate_privilege=elevate_privilege) 194 return 195 if elevate_privilege: 196 raise NotImplementedError("elevate_privilege isn't supported on android.") 197 if not parameters: 198 parameters = '' 199 self._device.RunShellCommand('am start ' + parameters + ' ' + application) 200 201 def IsApplicationRunning(self, application): 202 if application in _HOST_APPLICATIONS: 203 return platform.GetHostPlatform().IsApplicationRunning(application) 204 return len(self._device.old_interface.ExtractPid(application)) > 0 205 206 def CanLaunchApplication(self, application): 207 if application in _HOST_APPLICATIONS: 208 return platform.GetHostPlatform().CanLaunchApplication(application) 209 return True 210 211 def InstallApplication(self, application): 212 if application in _HOST_APPLICATIONS: 213 platform.GetHostPlatform().InstallApplication(application) 214 return 215 raise NotImplementedError( 216 'Please teach Telemetry how to install ' + application) 217 218 @decorators.Cache 219 def CanCaptureVideo(self): 220 return self.GetOSVersionName() >= 'K' 221 222 def StartVideoCapture(self, min_bitrate_mbps): 223 """Starts the video capture at specified bitrate.""" 224 min_bitrate_mbps = max(min_bitrate_mbps, 0.1) 225 if min_bitrate_mbps > 100: 226 raise ValueError('Android video capture cannot capture at %dmbps. ' 227 'Max capture rate is 100mbps.' % min_bitrate_mbps) 228 self._video_output = tempfile.mkstemp()[1] 229 if self.is_video_capture_running: 230 self._video_recorder.Stop() 231 self._video_recorder = screenshot.VideoRecorder( 232 self._device, self._video_output, megabits_per_second=min_bitrate_mbps) 233 self._video_recorder.Start() 234 util.WaitFor(self._video_recorder.IsStarted, 5) 235 236 @property 237 def is_video_capture_running(self): 238 return self._video_recorder is not None 239 240 def StopVideoCapture(self): 241 assert self.is_video_capture_running, 'Must start video capture first' 242 self._video_recorder.Stop() 243 self._video_recorder.Pull() 244 self._video_recorder = None 245 246 return video.Video(self, self._video_output) 247 248 def CanMonitorPower(self): 249 return self._powermonitor.CanMonitorPower() 250 251 def StartMonitoringPower(self, browser): 252 self._powermonitor.StartMonitoringPower(browser) 253 254 def StopMonitoringPower(self): 255 return self._powermonitor.StopMonitoringPower() 256 257 def _GetFileContents(self, fname): 258 if not self._can_access_protected_file_contents: 259 logging.warning('%s cannot be retrieved on non-rooted device.' % fname) 260 return '' 261 return '\n'.join( 262 self._device.old_interface.GetProtectedFileContents(fname)) 263 264 def _GetPsOutput(self, columns, pid=None): 265 assert columns == ['pid', 'name'] or columns == ['pid'], \ 266 'Only know how to return pid and name. Requested: ' + columns 267 command = 'ps' 268 if pid: 269 command += ' -p %d' % pid 270 ps = self._device.RunShellCommand(command)[1:] 271 output = [] 272 for line in ps: 273 data = line.split() 274 curr_pid = data[1] 275 curr_name = data[-1] 276 if columns == ['pid', 'name']: 277 output.append([curr_pid, curr_name]) 278 else: 279 output.append([curr_pid]) 280 return output 281