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