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