Home | History | Annotate | Download | only in chromium-trace
      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 
      9 This is a tool for capturing a trace that includes data from both userland and
     10 the kernel.  It creates an HTML file for visualizing the trace.
     11 """
     12 
     13 import 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.
     17 if 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
     22 import imp
     23 import optparse
     24 import os
     25 
     26 import util
     27 
     28 
     29 # The default agent directory.
     30 DEFAULT_AGENT_DIR = 'agents'
     31 
     32 
     33 def 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 
    100 def 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 
    132 def 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 
    169 def 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 
    194 def read_asset(src_dir, filename):
    195   return open(os.path.join(src_dir, filename)).read()
    196 
    197 
    198 if __name__ == '__main__':
    199   main()
    200