• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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