1# Copyright 2014 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 os
7
8from telemetry.core import util
9from telemetry.internal.backends.chrome import android_browser_finder
10from telemetry.internal.platform import profiler
11
12try:
13  from devil.android import device_errors  # pylint: disable=import-error
14except ImportError:
15  device_errors = None
16
17
18class AndroidTraceviewProfiler(profiler.Profiler):
19  """Collects a Traceview on Android."""
20
21  _DEFAULT_DEVICE_DIR = '/data/local/tmp/traceview'
22
23  def __init__(self, browser_backend, platform_backend, output_path, state):
24    super(AndroidTraceviewProfiler, self).__init__(
25        browser_backend, platform_backend, output_path, state)
26
27    if self._browser_backend.device.FileExists(self._DEFAULT_DEVICE_DIR):
28      self._browser_backend.device.RunShellCommand(
29          'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*'))
30    else:
31      self._browser_backend.device.RunShellCommand(
32          'mkdir -p ' + self._DEFAULT_DEVICE_DIR)
33      self._browser_backend.device.RunShellCommand(
34          'chmod 777 ' + self._DEFAULT_DEVICE_DIR)
35
36    self._trace_files = []
37    for pid in self._GetProcessOutputFileMap().iterkeys():
38      device_dump_file = '%s/%s.trace' % (self._DEFAULT_DEVICE_DIR, pid)
39      self._trace_files.append((pid, device_dump_file))
40      self._browser_backend.device.RunShellCommand('am profile %s start %s' %
41                                                   (pid, device_dump_file))
42
43
44  @classmethod
45  def name(cls):
46    return 'android-traceview'
47
48  @classmethod
49  def is_supported(cls, browser_type):
50    if browser_type == 'any':
51      return android_browser_finder.CanFindAvailableBrowsers()
52    return browser_type.startswith('android')
53
54  def CollectProfile(self):
55    output_files = []
56    for pid, trace_file in self._trace_files:
57      self._browser_backend.device.RunShellCommand('am profile %s stop' % pid)
58      # pylint: disable=cell-var-from-loop
59      util.WaitFor(lambda: self._FileSize(trace_file) > 0, timeout=10)
60      output_files.append(trace_file)
61    try:
62      self._browser_backend.device.PullFile(
63          self._DEFAULT_DEVICE_DIR, self._output_path)
64    except:
65      logging.exception('New exception caused by DeviceUtils conversion')
66      raise
67    self._browser_backend.device.RunShellCommand(
68        'rm ' + os.path.join(self._DEFAULT_DEVICE_DIR, '*'))
69    print 'Traceview profiles available in ', self._output_path
70    print 'Use third_party/android_tools/sdk/tools/monitor '
71    print 'then use "File->Open File" to visualize them.'
72    return output_files
73
74  def _FileSize(self, file_name):
75    try:
76      return self._browser_backend.device.Stat(file_name).st_size
77    except device_errors.CommandFailedError:
78      return 0
79