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 13import sys 14 15# Make sure we're using a new enough version of Python. 16# The flags= parameter of re.sub() is new in Python 2.7. 17if sys.version_info[:2] < (2, 7): 18 print >> sys.stderr, '\nThis script requires Python 2.7 or newer.' 19 sys.exit(1) 20 21# pylint: disable=g-bad-import-order,g-import-not-at-top 22import imp 23import optparse 24import os 25 26import util 27 28 29# The default agent directory. 30DEFAULT_AGENT_DIR = 'agents' 31 32 33def parse_options(argv): 34 """Parses and checks the command-line options. 35 36 Returns: 37 A tuple containing the options structure and a list of categories to 38 be traced. 39 """ 40 usage = 'Usage: %prog [options] [category1 [category2 ...]]' 41 desc = 'Example: %prog -b 32768 -t 15 gfx input view sched freq' 42 parser = optparse.OptionParser(usage=usage, description=desc) 43 parser.add_option('-o', dest='output_file', help='write HTML to FILE', 44 default='trace.html', metavar='FILE') 45 parser.add_option('-t', '--time', dest='trace_time', type='int', 46 help='trace for N seconds', metavar='N') 47 parser.add_option('-b', '--buf-size', dest='trace_buf_size', type='int', 48 help='use a trace buffer size of N KB', metavar='N') 49 parser.add_option('-k', '--ktrace', dest='kfuncs', action='store', 50 help='specify a comma-separated list of kernel functions ' 51 'to trace') 52 parser.add_option('-l', '--list-categories', dest='list_categories', 53 default=False, action='store_true', 54 help='list the available categories and exit') 55 parser.add_option('-a', '--app', dest='app_name', default=None, type='string', 56 action='store', 57 help='enable application-level tracing for comma-separated ' 58 'list of app cmdlines') 59 parser.add_option('--no-fix-threads', dest='fix_threads', default=True, 60 action='store_false', 61 help='don\'t fix missing or truncated thread names') 62 parser.add_option('--no-fix-circular', dest='fix_circular', default=True, 63 action='store_false', 64 help='don\'t fix truncated circular traces') 65 parser.add_option('--no-compress', dest='compress_trace_data', 66 default=True, action='store_false', 67 help='Tell the device not to send the trace data in ' 68 'compressed form.') 69 parser.add_option('--link-assets', dest='link_assets', default=False, 70 action='store_true', 71 help='(deprecated)') 72 parser.add_option('--from-file', dest='from_file', action='store', 73 help='read the trace from a file (compressed) rather than ' 74 'running a live trace') 75 parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer', 76 type='string', help='(deprecated)') 77 parser.add_option('-e', '--serial', dest='device_serial', type='string', 78 help='adb device serial number') 79 parser.add_option('--agent-dirs', dest='agent_dirs', type='string', 80 help='the directories of additional systrace agent modules.' 81 ' The directories should be comma separated, e.g., ' 82 '--agent-dirs=dir1,dir2,dir3. Directory |%s| is the default' 83 ' agent directory and will always be checked.' 84 % DEFAULT_AGENT_DIR) 85 86 options, categories = parser.parse_args(argv[1:]) 87 88 if options.link_assets or options.asset_dir != 'trace-viewer': 89 parser.error('--link-assets and --asset-dir are deprecated.') 90 91 if (options.trace_time is not None) and (options.trace_time <= 0): 92 parser.error('the trace time must be a positive number') 93 94 if (options.trace_buf_size is not None) and (options.trace_buf_size <= 0): 95 parser.error('the trace buffer size must be a positive number') 96 97 return (options, categories) 98 99 100def write_trace_html(html_filename, script_dir, agents): 101 """Writes out a trace html file. 102 103 Args: 104 html_filename: The name of the file to write. 105 script_dir: The directory containing this script. 106 agents: The systrace agents. 107 """ 108 html_prefix = read_asset(script_dir, 'prefix.html') 109 html_suffix = read_asset(script_dir, 'suffix.html') 110 trace_viewer_html = read_asset(script_dir, 'systrace_trace_viewer.html') 111 112 # Open the file in binary mode to prevent python from changing the 113 # line endings. 114 html_file = open(html_filename, 'wb') 115 html_file.write(html_prefix.replace('{{SYSTRACE_TRACE_VIEWER_HTML}}', 116 trace_viewer_html)) 117 118 html_file.write('<!-- BEGIN TRACE -->\n') 119 for a in agents: 120 html_file.write(' <script class="') 121 html_file.write(a.get_class_name()) 122 html_file.write('" type="application/text">\n') 123 html_file.write(a.get_trace_data()) 124 html_file.write(' </script>\n') 125 html_file.write('<!-- END TRACE -->\n') 126 127 html_file.write(html_suffix) 128 html_file.close() 129 print '\n wrote file://%s\n' % os.path.abspath(html_filename) 130 131 132def create_agents(options, categories): 133 """Create systrace agents. 134 135 This function will search systrace agent modules in agent directories and 136 create the corresponding systrace agents. 137 Args: 138 options: The command-line options. 139 categories: The trace categories to capture. 140 Returns: 141 The list of systrace agents. 142 """ 143 agent_dirs = [os.path.join(os.path.dirname(__file__), DEFAULT_AGENT_DIR)] 144 if options.agent_dirs: 145 agent_dirs.extend(options.agent_dirs.split(',')) 146 147 agents = [] 148 for agent_dir in agent_dirs: 149 if not agent_dir: 150 continue 151 for filename in os.listdir(agent_dir): 152 (module_name, ext) = os.path.splitext(filename) 153 if ext != '.py' or module_name == '__init__': 154 continue 155 (f, pathname, data) = imp.find_module(module_name, [agent_dir]) 156 try: 157 module = imp.load_module(module_name, f, pathname, data) 158 finally: 159 if f: 160 f.close() 161 if module: 162 agent = module.try_create_agent(options, categories) 163 if not agent: 164 continue 165 agents.append(agent) 166 return agents 167 168 169def main(): 170 options, categories = parse_options(sys.argv) 171 agents = create_agents(options, categories) 172 173 if not agents: 174 dirs = DEFAULT_AGENT_DIR 175 if options.agent_dirs: 176 dirs += ',' + options.agent_dirs 177 print >> sys.stderr, ('No systrace agent is available in directories |%s|.' 178 % dirs) 179 sys.exit(1) 180 181 for a in agents: 182 a.start() 183 184 for a in agents: 185 a.collect_result() 186 if not a.expect_trace(): 187 # Nothing more to do. 188 return 189 190 script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) 191 write_trace_html(options.output_file, script_dir, agents) 192 193 194def read_asset(src_dir, filename): 195 return open(os.path.join(src_dir, filename)).read() 196 197 198if __name__ == '__main__': 199 main() 200