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