| #!/usr/bin/env python |
| |
| # Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Android system-wide tracing utility. |
| |
| This is a tool for capturing a trace that includes data from both userland and |
| the kernel. It creates an HTML file for visualizing the trace. |
| """ |
| |
| import sys |
| |
| # Make sure we're using a new enough version of Python. |
| # The flags= parameter of re.sub() is new in Python 2.7. |
| if sys.version_info[:2] < (2, 7): |
| print >> sys.stderr, '\nThis script requires Python 2.7 or newer.' |
| sys.exit(1) |
| |
| # pylint: disable=g-bad-import-order,g-import-not-at-top |
| import imp |
| import optparse |
| import os |
| |
| import util |
| |
| |
| # The default agent directory. |
| DEFAULT_AGENT_DIR = 'agents' |
| |
| |
| def parse_options(argv): |
| """Parses and checks the command-line options. |
| |
| Returns: |
| A tuple containing the options structure and a list of categories to |
| be traced. |
| """ |
| usage = 'Usage: %prog [options] [category1 [category2 ...]]' |
| desc = 'Example: %prog -b 32768 -t 15 gfx input view sched freq' |
| parser = optparse.OptionParser(usage=usage, description=desc) |
| parser.add_option('-o', dest='output_file', help='write HTML to FILE', |
| default='trace.html', metavar='FILE') |
| parser.add_option('-t', '--time', dest='trace_time', type='int', |
| help='trace for N seconds', metavar='N') |
| parser.add_option('-b', '--buf-size', dest='trace_buf_size', type='int', |
| help='use a trace buffer size of N KB', metavar='N') |
| parser.add_option('-k', '--ktrace', dest='kfuncs', action='store', |
| help='specify a comma-separated list of kernel functions ' |
| 'to trace') |
| parser.add_option('-l', '--list-categories', dest='list_categories', |
| default=False, action='store_true', |
| help='list the available categories and exit') |
| parser.add_option('-a', '--app', dest='app_name', default=None, type='string', |
| action='store', |
| help='enable application-level tracing for comma-separated ' |
| 'list of app cmdlines') |
| parser.add_option('--no-fix-threads', dest='fix_threads', default=True, |
| action='store_false', |
| help='don\'t fix missing or truncated thread names') |
| parser.add_option('--no-fix-circular', dest='fix_circular', default=True, |
| action='store_false', |
| help='don\'t fix truncated circular traces') |
| parser.add_option('--no-compress', dest='compress_trace_data', |
| default=True, action='store_false', |
| help='Tell the device not to send the trace data in ' |
| 'compressed form.') |
| parser.add_option('--link-assets', dest='link_assets', default=False, |
| action='store_true', |
| help='(deprecated)') |
| parser.add_option('--from-file', dest='from_file', action='store', |
| help='read the trace from a file (compressed) rather than ' |
| 'running a live trace') |
| parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer', |
| type='string', help='(deprecated)') |
| parser.add_option('-e', '--serial', dest='device_serial', type='string', |
| help='adb device serial number') |
| parser.add_option('--agent-dirs', dest='agent_dirs', type='string', |
| help='the directories of additional systrace agent modules.' |
| ' The directories should be comma separated, e.g., ' |
| '--agent-dirs=dir1,dir2,dir3. Directory |%s| is the default' |
| ' agent directory and will always be checked.' |
| % DEFAULT_AGENT_DIR) |
| |
| options, categories = parser.parse_args(argv[1:]) |
| |
| if options.link_assets or options.asset_dir != 'trace-viewer': |
| parser.error('--link-assets and --asset-dir are deprecated.') |
| |
| if (options.trace_time is not None) and (options.trace_time <= 0): |
| parser.error('the trace time must be a positive number') |
| |
| if (options.trace_buf_size is not None) and (options.trace_buf_size <= 0): |
| parser.error('the trace buffer size must be a positive number') |
| |
| return (options, categories) |
| |
| |
| def write_trace_html(html_filename, script_dir, agents): |
| """Writes out a trace html file. |
| |
| Args: |
| html_filename: The name of the file to write. |
| script_dir: The directory containing this script. |
| agents: The systrace agents. |
| """ |
| html_prefix = read_asset(script_dir, 'prefix.html') |
| html_suffix = read_asset(script_dir, 'suffix.html') |
| trace_viewer_html = read_asset(script_dir, 'systrace_trace_viewer.html') |
| |
| # Open the file in binary mode to prevent python from changing the |
| # line endings. |
| html_file = open(html_filename, 'wb') |
| html_file.write(html_prefix.replace('{{SYSTRACE_TRACE_VIEWER_HTML}}', |
| trace_viewer_html)) |
| |
| html_file.write('<!-- BEGIN TRACE -->\n') |
| for a in agents: |
| html_file.write(' <script class="') |
| html_file.write(a.get_class_name()) |
| html_file.write('" type="application/text">\n') |
| html_file.write(a.get_trace_data()) |
| html_file.write(' </script>\n') |
| html_file.write('<!-- END TRACE -->\n') |
| |
| html_file.write(html_suffix) |
| html_file.close() |
| print '\n wrote file://%s\n' % os.path.abspath(html_filename) |
| |
| |
| def create_agents(options, categories): |
| """Create systrace agents. |
| |
| This function will search systrace agent modules in agent directories and |
| create the corresponding systrace agents. |
| Args: |
| options: The command-line options. |
| categories: The trace categories to capture. |
| Returns: |
| The list of systrace agents. |
| """ |
| agent_dirs = [os.path.join(os.path.dirname(__file__), DEFAULT_AGENT_DIR)] |
| if options.agent_dirs: |
| agent_dirs.extend(options.agent_dirs.split(',')) |
| |
| agents = [] |
| for agent_dir in agent_dirs: |
| if not agent_dir: |
| continue |
| for filename in os.listdir(agent_dir): |
| (module_name, ext) = os.path.splitext(filename) |
| if ext != '.py' or module_name == '__init__': |
| continue |
| (f, pathname, data) = imp.find_module(module_name, [agent_dir]) |
| try: |
| module = imp.load_module(module_name, f, pathname, data) |
| finally: |
| if f: |
| f.close() |
| if module: |
| agent = module.try_create_agent(options, categories) |
| if not agent: |
| continue |
| agents.append(agent) |
| return agents |
| |
| |
| def main(): |
| options, categories = parse_options(sys.argv) |
| agents = create_agents(options, categories) |
| |
| if not agents: |
| dirs = DEFAULT_AGENT_DIR |
| if options.agent_dirs: |
| dirs += ',' + options.agent_dirs |
| print >> sys.stderr, ('No systrace agent is available in directories |%s|.' |
| % dirs) |
| sys.exit(1) |
| |
| for a in agents: |
| a.start() |
| |
| for a in agents: |
| a.collect_result() |
| if not a.expect_trace(): |
| # Nothing more to do. |
| return |
| |
| script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) |
| write_trace_html(options.output_file, script_dir, agents) |
| |
| |
| def read_asset(src_dir, filename): |
| return open(os.path.join(src_dir, filename)).read() |
| |
| |
| if __name__ == '__main__': |
| main() |