• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2
3# Copyright (c) 2011 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Android system-wide tracing utility.
8
9This is a tool for capturing a trace that includes data from both userland and
10the kernel.  It creates an HTML file for visualizing the trace.
11"""
12
13# Make sure we're using a new enough version of Python.
14# The flags= parameter of re.sub() is new in Python 2.7. And Systrace does not
15# support Python 3 yet.
16
17import sys
18
19version = sys.version_info[:2]
20if version != (2, 7):
21  sys.stderr.write('This script does not support Python %d.%d. '
22                   'Please use Python 2.7.\n' % version)
23  sys.exit(1)
24
25
26import optparse
27import os
28import time
29
30_SYSTRACE_DIR = os.path.abspath(
31    os.path.join(os.path.dirname(__file__), os.path.pardir))
32_CATAPULT_DIR = os.path.join(
33    os.path.dirname(os.path.abspath(__file__)), os.path.pardir, os.path.pardir)
34_DEVIL_DIR = os.path.join(_CATAPULT_DIR, 'devil')
35if _DEVIL_DIR not in sys.path:
36  sys.path.insert(0, _DEVIL_DIR)
37if _SYSTRACE_DIR not in sys.path:
38  sys.path.insert(0, _SYSTRACE_DIR)
39
40from devil import devil_env
41from devil.android.sdk import adb_wrapper
42from systrace import systrace_runner
43from systrace import util
44from systrace.tracing_agents import atrace_agent
45from systrace.tracing_agents import atrace_from_file_agent
46from systrace.tracing_agents import battor_trace_agent
47from systrace.tracing_agents import ftrace_agent
48from systrace.tracing_agents import walt_agent
49
50
51ALL_MODULES = [atrace_agent, atrace_from_file_agent,
52               battor_trace_agent, ftrace_agent, walt_agent]
53
54
55def parse_options(argv):
56  """Parses and checks the command-line options.
57
58  Returns:
59    A tuple containing the options structure and a list of categories to
60    be traced.
61  """
62  usage = 'Usage: %prog [options] [category1 [category2 ...]]'
63  desc = 'Example: %prog -b 32768 -t 15 gfx input view sched freq'
64  parser = optparse.OptionParser(usage=usage, description=desc,
65                                 conflict_handler='resolve')
66  parser = util.get_main_options(parser)
67
68  parser.add_option('-l', '--list-categories', dest='list_categories',
69                    default=False, action='store_true',
70                    help='list the available categories and exit')
71
72  # Add the other agent parsing options to the parser. For Systrace on the
73  # command line, all agents are added. For Android, only the compatible agents
74  # will be added.
75  for module in ALL_MODULES:
76    option_group = module.add_options(parser)
77    if option_group:
78      parser.add_option_group(option_group)
79
80  options, categories = parser.parse_args(argv[1:])
81
82  if options.output_file is None:
83    options.output_file = 'trace.json' if options.write_json else 'trace.html'
84
85  if options.link_assets or options.asset_dir != 'trace-viewer':
86    parser.error('--link-assets and --asset-dir are deprecated.')
87
88  if options.trace_time and options.trace_time < 0:
89    parser.error('the trace time must be a non-negative number')
90
91  if (options.trace_buf_size is not None) and (options.trace_buf_size <= 0):
92    parser.error('the trace buffer size must be a positive number')
93
94  return (options, categories)
95
96def find_adb():
97  """Finds adb on the path.
98
99  This method is provided to avoid the issue of diskutils.spawn's
100  find_executable which first searches the current directory before
101  searching $PATH. That behavior results in issues where systrace.py
102  uses a different adb than the one in the path.
103  """
104  paths = os.environ['PATH'].split(os.pathsep)
105  executable = 'adb'
106  if sys.platform == 'win32':
107    executable = executable + '.exe'
108  for p in paths:
109    f = os.path.join(p, executable)
110    if os.path.isfile(f):
111      return f
112  return None
113
114def initialize_devil():
115  """Initialize devil to use adb from $PATH"""
116  adb_path = find_adb()
117  if adb_path is None:
118    print >> sys.stderr, "Unable to find adb, is it in your path?"
119    sys.exit(1)
120  devil_dynamic_config = {
121    'config_type': 'BaseConfig',
122    'dependencies': {
123      'adb': {
124        'file_info': {
125          devil_env.GetPlatform(): {
126            'local_paths': [os.path.abspath(adb_path)]
127          }
128        }
129      }
130    }
131  }
132  devil_env.config.Initialize(configs=[devil_dynamic_config])
133
134
135def main_impl(arguments):
136  # Parse the command line options.
137  options, categories = parse_options(arguments)
138
139  # Override --atrace-categories and --ftrace-categories flags if command-line
140  # categories are provided.
141  if categories:
142    if options.target == 'android':
143      options.atrace_categories = categories
144    elif options.target == 'linux':
145      options.ftrace_categories = categories
146    else:
147      raise RuntimeError('Categories are only valid for atrace/ftrace. Target '
148                         'platform must be either Android or Linux.')
149
150  # Include atrace categories by default in Systrace.
151  if options.target == 'android' and not options.atrace_categories:
152    options.atrace_categories = atrace_agent.DEFAULT_CATEGORIES
153
154  if options.target == 'android' and not options.from_file:
155    initialize_devil()
156    if not options.device_serial_number:
157      devices = [a.GetDeviceSerial() for a in adb_wrapper.AdbWrapper.Devices()]
158      if len(devices) == 0:
159        raise RuntimeError('No ADB devices connected.')
160      elif len(devices) >= 2:
161        raise RuntimeError('Multiple devices connected, serial number required')
162      options.device_serial_number = devices[0]
163
164  # If list_categories is selected, just print the list of categories.
165  # In this case, use of the tracing controller is not necessary.
166  if options.list_categories:
167    if options.target == 'android':
168      atrace_agent.list_categories(options)
169    elif options.target == 'linux':
170      ftrace_agent.list_categories(options)
171    return
172
173  # Set up the systrace runner and start tracing.
174  controller = systrace_runner.SystraceRunner(
175      os.path.dirname(os.path.abspath(__file__)), options)
176  controller.StartTracing()
177
178  # Wait for the given number of seconds or until the user presses enter.
179  # pylint: disable=superfluous-parens
180  # (need the parens so no syntax error if trying to load with Python 3)
181  if options.from_file is not None:
182    print('Reading results from file.')
183  elif options.trace_time:
184    print('Starting tracing (%d seconds)' % options.trace_time)
185    time.sleep(options.trace_time)
186  else:
187    raw_input('Starting tracing (stop with enter)')
188
189  # Stop tracing and collect the output.
190  print('Tracing completed. Collecting output...')
191  controller.StopTracing()
192  print('Outputting Systrace results...')
193  controller.OutputSystraceResults(write_json=options.write_json)
194
195def main():
196  main_impl(sys.argv)
197
198if __name__ == '__main__' and __package__ is None:
199  main()
200