Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / tools / telemetry / telemetry / core / platform / android_platform_backend.py
1 # Copyright (c) 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 import subprocess
7 import tempfile
8
9 from telemetry import decorators
10 from telemetry.core import bitmap
11 from telemetry.core import exceptions
12 from telemetry.core import platform
13 from telemetry.core import util
14 from telemetry.core.platform import proc_supporting_platform_backend
15 from telemetry.core.platform.power_monitor import android_ds2784_power_monitor
16 from telemetry.core.platform.power_monitor import monsoon_power_monitor
17 from telemetry.core.platform.power_monitor import power_monitor_controller
18 from telemetry.core.platform.profiler import android_prebuilt_profiler_helper
19
20 # Get build/android scripts into our path.
21 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
22 from pylib import screenshot  # pylint: disable=F0401
23 from pylib.perf import cache_control  # pylint: disable=F0401
24 from pylib.perf import perf_control  # pylint: disable=F0401
25 from pylib.perf import thermal_throttle  # pylint: disable=F0401
26
27 try:
28   from pylib.perf import surface_stats_collector  # pylint: disable=F0401
29 except Exception:
30   surface_stats_collector = None
31
32
33 _HOST_APPLICATIONS = [
34     'avconv',
35     'ipfw',
36     ]
37
38
39 class AndroidPlatformBackend(
40     proc_supporting_platform_backend.ProcSupportingPlatformBackend):
41   def __init__(self, adb, no_performance_mode):
42     super(AndroidPlatformBackend, self).__init__()
43     self._adb = adb
44     self._surface_stats_collector = None
45     self._perf_tests_setup = perf_control.PerfControl(self._adb)
46     self._thermal_throttle = thermal_throttle.ThermalThrottle(self._adb)
47     self._no_performance_mode = no_performance_mode
48     self._raw_display_frame_rate_measurements = []
49     self._host_platform_backend = platform.CreatePlatformBackendForCurrentOS()
50     self._can_access_protected_file_contents = \
51         self._adb.CanAccessProtectedFileContents()
52     self._powermonitor = power_monitor_controller.PowerMonitorController([
53         monsoon_power_monitor.MonsoonPowerMonitor(),
54         android_ds2784_power_monitor.DS2784PowerMonitor(adb)
55     ])
56     self._video_recorder = None
57     self._video_output = None
58     if self._no_performance_mode:
59       logging.warning('CPU governor will not be set!')
60
61   def IsRawDisplayFrameRateSupported(self):
62     return True
63
64   def StartRawDisplayFrameRateMeasurement(self):
65     assert not self._surface_stats_collector
66     # Clear any leftover data from previous timed out tests
67     self._raw_display_frame_rate_measurements = []
68     self._surface_stats_collector = \
69         surface_stats_collector.SurfaceStatsCollector(self._adb)
70     self._surface_stats_collector.Start()
71
72   def StopRawDisplayFrameRateMeasurement(self):
73     self._surface_stats_collector.Stop()
74     for r in self._surface_stats_collector.GetResults():
75       self._raw_display_frame_rate_measurements.append(
76           platform.Platform.RawDisplayFrameRateMeasurement(
77               r.name, r.value, r.unit))
78
79     self._surface_stats_collector = None
80
81   def GetRawDisplayFrameRateMeasurements(self):
82     ret = self._raw_display_frame_rate_measurements
83     self._raw_display_frame_rate_measurements = []
84     return ret
85
86   def SetFullPerformanceModeEnabled(self, enabled):
87     if self._no_performance_mode:
88       return
89     if enabled:
90       self._perf_tests_setup.SetHighPerfMode()
91     else:
92       self._perf_tests_setup.SetDefaultPerfMode()
93
94   def CanMonitorThermalThrottling(self):
95     return True
96
97   def IsThermallyThrottled(self):
98     return self._thermal_throttle.IsThrottled()
99
100   def HasBeenThermallyThrottled(self):
101     return self._thermal_throttle.HasBeenThrottled()
102
103   def GetSystemCommitCharge(self):
104     for line in self._adb.RunShellCommand('dumpsys meminfo', log_result=False):
105       if line.startswith('Total PSS: '):
106         return int(line.split()[2]) * 1024
107     return 0
108
109   @decorators.Cache
110   def GetSystemTotalPhysicalMemory(self):
111     for line in self._adb.RunShellCommand('dumpsys meminfo', log_result=False):
112       if line.startswith('Total RAM: '):
113         return int(line.split()[2]) * 1024
114     return 0
115
116   def GetCpuStats(self, pid):
117     if not self._can_access_protected_file_contents:
118       logging.warning('CPU stats cannot be retrieved on non-rooted device.')
119       return {}
120     return super(AndroidPlatformBackend, self).GetCpuStats(pid)
121
122   def GetCpuTimestamp(self):
123     if not self._can_access_protected_file_contents:
124       logging.warning('CPU timestamp cannot be retrieved on non-rooted device.')
125       return {}
126     return super(AndroidPlatformBackend, self).GetCpuTimestamp()
127
128   def PurgeUnpinnedMemory(self):
129     """Purges the unpinned ashmem memory for the whole system.
130
131     This can be used to make memory measurements more stable in particular.
132     """
133     android_prebuilt_profiler_helper.InstallOnDevice(self._adb, 'purge_ashmem')
134     if self._adb.RunShellCommand(
135         android_prebuilt_profiler_helper.GetDevicePath('purge_ashmem'),
136         log_result=True):
137       return
138     raise Exception('Error while purging ashmem.')
139
140   def GetMemoryStats(self, pid):
141     memory_usage = self._adb.GetMemoryUsageForPid(pid)[0]
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   def GetCommandLine(self, pid):
163     ps = self._GetPsOutput(['pid', 'name'])
164     for curr_pid, curr_name in ps:
165       if int(curr_pid) == pid:
166         return curr_name
167     raise exceptions.ProcessGoneException()
168
169   def GetOSName(self):
170     return 'android'
171
172   @decorators.Cache
173   def GetOSVersionName(self):
174     return self._adb.GetBuildId()[0]
175
176   def CanFlushIndividualFilesFromSystemCache(self):
177     return False
178
179   def FlushEntireSystemCache(self):
180     cache = cache_control.CacheControl(self._adb)
181     cache.DropRamCaches()
182
183   def FlushSystemCacheForDirectory(self, directory, ignoring=None):
184     raise NotImplementedError()
185
186   def LaunchApplication(
187       self, application, parameters=None, elevate_privilege=False):
188     if application in _HOST_APPLICATIONS:
189       self._host_platform_backend.LaunchApplication(
190           application, parameters, elevate_privilege=elevate_privilege)
191       return
192     if elevate_privilege:
193       raise NotImplementedError("elevate_privilege isn't supported on android.")
194     if not parameters:
195       parameters = ''
196     self._adb.RunShellCommand('am start ' + parameters + ' ' + application)
197
198   def IsApplicationRunning(self, application):
199     if application in _HOST_APPLICATIONS:
200       return self._host_platform_backend.IsApplicationRunning(application)
201     return len(self._adb.ExtractPid(application)) > 0
202
203   def CanLaunchApplication(self, application):
204     if application in _HOST_APPLICATIONS:
205       return self._host_platform_backend.CanLaunchApplication(application)
206     return True
207
208   def InstallApplication(self, application):
209     if application in _HOST_APPLICATIONS:
210       self._host_platform_backend.InstallApplication(application)
211       return
212     raise NotImplementedError(
213         'Please teach Telemetry how to install ' + application)
214
215   @decorators.Cache
216   def CanCaptureVideo(self):
217     return self.GetOSVersionName() >= 'K'
218
219   def StartVideoCapture(self, min_bitrate_mbps):
220     min_bitrate_mbps = max(min_bitrate_mbps, 0.1)
221     if min_bitrate_mbps > 100:
222       raise ValueError('Android video capture cannot capture at %dmbps. '
223                        'Max capture rate is 100mbps.' % min_bitrate_mbps)
224     self._video_output = tempfile.mkstemp()[1]
225     if self._video_recorder:
226       self._video_recorder.Stop()
227     self._video_recorder = screenshot.VideoRecorder(
228         self._adb, self._video_output, megabits_per_second=min_bitrate_mbps)
229     self._video_recorder.Start()
230     util.WaitFor(self._video_recorder.IsStarted, 5)
231
232   def StopVideoCapture(self):
233     assert self._video_recorder, 'Must start video capture first'
234     self._video_recorder.Stop()
235     self._video_output = self._video_recorder.Pull()
236     self._video_recorder = None
237     for frame in self._FramesFromMp4(self._video_output):
238       yield frame
239
240   def CanMonitorPowerAsync(self):
241     return self._powermonitor.CanMonitorPowerAsync()
242
243   def StartMonitoringPowerAsync(self):
244     self._powermonitor.StartMonitoringPowerAsync()
245
246   def StopMonitoringPowerAsync(self):
247     return self._powermonitor.StopMonitoringPowerAsync()
248
249   def _FramesFromMp4(self, mp4_file):
250     if not self.CanLaunchApplication('avconv'):
251       self.InstallApplication('avconv')
252
253     def GetDimensions(video):
254       proc = subprocess.Popen(['avconv', '-i', video], stderr=subprocess.PIPE)
255       dimensions = None
256       output = ''
257       for line in proc.stderr.readlines():
258         output += line
259         if 'Video:' in line:
260           dimensions = line.split(',')[2]
261           dimensions = map(int, dimensions.split()[0].split('x'))
262           break
263       proc.wait()
264       assert dimensions, ('Failed to determine video dimensions. output=%s' %
265                           output)
266       return dimensions
267
268     def GetFrameTimestampMs(stderr):
269       """Returns the frame timestamp in integer milliseconds from the dump log.
270
271       The expected line format is:
272       '  dts=1.715  pts=1.715\n'
273
274       We have to be careful to only read a single timestamp per call to avoid
275       deadlock because avconv interleaves its writes to stdout and stderr.
276       """
277       while True:
278         line = ''
279         next_char = ''
280         while next_char != '\n':
281           next_char = stderr.read(1)
282           line += next_char
283         if 'pts=' in line:
284           return int(1000 * float(line.split('=')[-1]))
285
286     dimensions = GetDimensions(mp4_file)
287     frame_length = dimensions[0] * dimensions[1] * 3
288     frame_data = bytearray(frame_length)
289
290     # Use rawvideo so that we don't need any external library to parse frames.
291     proc = subprocess.Popen(['avconv', '-i', mp4_file, '-vcodec',
292                              'rawvideo', '-pix_fmt', 'rgb24', '-dump',
293                              '-loglevel', 'debug', '-f', 'rawvideo', '-'],
294                             stderr=subprocess.PIPE, stdout=subprocess.PIPE)
295     while True:
296       num_read = proc.stdout.readinto(frame_data)
297       if not num_read:
298         raise StopIteration
299       assert num_read == len(frame_data), 'Unexpected frame size: %d' % num_read
300       yield (GetFrameTimestampMs(proc.stderr),
301              bitmap.Bitmap(3, dimensions[0], dimensions[1], frame_data))
302
303   def _GetFileContents(self, fname):
304     if not self._can_access_protected_file_contents:
305       logging.warning('%s cannot be retrieved on non-rooted device.' % fname)
306       return ''
307     return '\n'.join(
308         self._adb.GetProtectedFileContents(fname, log_result=False))
309
310   def _GetPsOutput(self, columns, pid=None):
311     assert columns == ['pid', 'name'] or columns == ['pid'], \
312         'Only know how to return pid and name. Requested: ' + columns
313     command = 'ps'
314     if pid:
315       command += ' -p %d' % pid
316     ps = self._adb.RunShellCommand(command, log_result=False)[1:]
317     output = []
318     for line in ps:
319       data = line.split()
320       curr_pid = data[1]
321       curr_name = data[-1]
322       if columns == ['pid', 'name']:
323         output.append([curr_pid, curr_name])
324       else:
325         output.append([curr_pid])
326     return output