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 errno, optparse, os, select, subprocess, sys, time, zlib, config
     14 
     15 # This list is based on the tags in frameworks/native/include/utils/Trace.h.
     16 trace_tag_bits = {
     17   'gfx':      1<<1,
     18   'input':    1<<2,
     19   'view':     1<<3,
     20   'webview':  1<<4,
     21   'wm':       1<<5,
     22   'am':       1<<6,
     23   'sync':     1<<7,
     24   'audio':    1<<8,
     25   'video':    1<<9,
     26 }
     27 
     28 def main():
     29   parser = optparse.OptionParser()
     30   parser.add_option('-o', dest='output_file', help='write HTML to FILE',
     31                     default='trace.html', metavar='FILE')
     32   parser.add_option('-t', '--time', dest='trace_time', type='int',
     33                     help='trace for N seconds', metavar='N')
     34   parser.add_option('-b', '--buf-size', dest='trace_buf_size', type='int',
     35                     help='use a trace buffer size of N KB', metavar='N')
     36   parser.add_option('-d', '--disk', dest='trace_disk', default=False,
     37                     action='store_true', help='trace disk I/O (requires root)')
     38   parser.add_option('-f', '--cpu-freq', dest='trace_cpu_freq', default=False,
     39                     action='store_true', help='trace CPU frequency changes')
     40   parser.add_option('-i', '--cpu-idle', dest='trace_cpu_idle', default=False,
     41                     action='store_true', help='trace CPU idle events')
     42   parser.add_option('-l', '--cpu-load', dest='trace_cpu_load', default=False,
     43                     action='store_true', help='trace CPU load')
     44   parser.add_option('-s', '--no-cpu-sched', dest='trace_cpu_sched', default=True,
     45                     action='store_false', help='inhibit tracing CPU ' +
     46                     'scheduler (allows longer trace times by reducing data ' +
     47                     'rate into buffer)')
     48   parser.add_option('-w', '--workqueue', dest='trace_workqueue', default=False,
     49                     action='store_true', help='trace the kernel workqueues ' +
     50                     '(requires root)')
     51   parser.add_option('--set-tags', dest='set_tags', action='store',
     52                     help='set the enabled trace tags and exit; set to a ' +
     53                     'comma separated list of: ' +
     54                     ', '.join(trace_tag_bits.iterkeys()))
     55   parser.add_option('--link-assets', dest='link_assets', default=False,
     56                     action='store_true', help='link to original CSS or JS resources '
     57                     'instead of embedding them')
     58   options, args = parser.parse_args()
     59 
     60   if options.set_tags:
     61     flags = 0
     62     tags = options.set_tags.split(',')
     63     for tag in tags:
     64       try:
     65         flags |= trace_tag_bits[tag]
     66       except KeyError:
     67         parser.error('unrecognized tag: %s\nknown tags are: %s' %
     68                      (tag, ', '.join(trace_tag_bits.iterkeys())))
     69     atrace_args = ['adb', 'shell', 'setprop', 'debug.atrace.tags.enableflags', hex(flags)]
     70     try:
     71       subprocess.check_call(atrace_args)
     72     except subprocess.CalledProcessError, e:
     73       print >> sys.stderr, 'unable to set tags: %s' % e
     74     print '\nSet enabled tags to: %s\n' % ', '.join(tags)
     75     print ('You will likely need to restart the Android framework for this to ' +
     76           'take effect:\n\n    adb shell stop\n    adb shell ' +
     77           'start\n')
     78     return
     79 
     80   atrace_args = ['adb', 'shell', 'atrace', '-z']
     81   if options.trace_disk:
     82     atrace_args.append('-d')
     83   if options.trace_cpu_freq:
     84     atrace_args.append('-f')
     85   if options.trace_cpu_idle:
     86     atrace_args.append('-i')
     87   if options.trace_cpu_load:
     88     atrace_args.append('-l')
     89   if options.trace_cpu_sched:
     90     atrace_args.append('-s')
     91   if options.trace_workqueue:
     92     atrace_args.append('-w')
     93   if options.trace_time is not None:
     94     if options.trace_time > 0:
     95       atrace_args.extend(['-t', str(options.trace_time)])
     96     else:
     97       parser.error('the trace time must be a positive number')
     98   if options.trace_buf_size is not None:
     99     if options.trace_buf_size > 0:
    100       atrace_args.extend(['-b', str(options.trace_buf_size)])
    101     else:
    102       parser.error('the trace buffer size must be a positive number')
    103 
    104   script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
    105 
    106   if options.link_assets:
    107     css = '\n'.join(linked_css_tag % (os.path.join(script_dir, f)) for f in config.css_in_files)
    108     js = '\n'.join(linked_js_tag % (os.path.join(script_dir, f)) for f in config.js_in_files)
    109   else:
    110     css_filename = os.path.join(script_dir, config.css_out_file)
    111     js_filename = os.path.join(script_dir, config.js_out_file)
    112     css = compiled_css_tag % (open(css_filename).read())
    113     js = compiled_js_tag % (open(js_filename).read())
    114 
    115   html_filename = options.output_file
    116 
    117   trace_started = False
    118   leftovers = ''
    119   adb = subprocess.Popen(atrace_args, stdout=subprocess.PIPE,
    120                          stderr=subprocess.PIPE)
    121   dec = zlib.decompressobj()
    122   while True:
    123     ready = select.select([adb.stdout, adb.stderr], [], [adb.stdout, adb.stderr])
    124     if adb.stderr in ready[0]:
    125       err = os.read(adb.stderr.fileno(), 4096)
    126       sys.stderr.write(err)
    127       sys.stderr.flush()
    128     if adb.stdout in ready[0]:
    129       out = leftovers + os.read(adb.stdout.fileno(), 4096)
    130       out = out.replace('\r\n', '\n')
    131       if out.endswith('\r'):
    132         out = out[:-1]
    133         leftovers = '\r'
    134       else:
    135         leftovers = ''
    136       if not trace_started:
    137         lines = out.splitlines(True)
    138         out = ''
    139         for i, line in enumerate(lines):
    140           if line == 'TRACE:\n':
    141             sys.stdout.write("downloading trace...")
    142             sys.stdout.flush()
    143             out = ''.join(lines[i+1:])
    144             html_file = open(html_filename, 'w')
    145             html_file.write(html_prefix % (css, js))
    146             trace_started = True
    147             break
    148           elif 'TRACE:'.startswith(line) and i == len(lines) - 1:
    149             leftovers = line + leftovers
    150           else:
    151             sys.stdout.write(line)
    152             sys.stdout.flush()
    153       if len(out) > 0:
    154         out = dec.decompress(out)
    155       html_out = out.replace('\n', '\\n\\\n')
    156       if len(html_out) > 0:
    157         html_file.write(html_out)
    158     result = adb.poll()
    159     if result is not None:
    160       break
    161   if result != 0:
    162     print >> sys.stderr, 'adb returned error code %d' % result
    163   elif trace_started:
    164     html_out = dec.flush().replace('\n', '\\n\\\n').replace('\r', '')
    165     if len(html_out) > 0:
    166       html_file.write(html_out)
    167     html_file.write(html_suffix)
    168     html_file.close()
    169     print " done\n\n    wrote file://%s/%s\n" % (os.getcwd(), options.output_file)
    170   else:
    171     print >> sys.stderr, ('An error occured while capturing the trace.  Output ' +
    172       'file was not written.')
    173 
    174 html_prefix = """<!DOCTYPE HTML>
    175 <html>
    176 <head i18n-values="dir:textdirection;">
    177 <title>Android System Trace</title>
    178 %s
    179 %s
    180 <style>
    181   .view {
    182     overflow: hidden;
    183     position: absolute;
    184     top: 0;
    185     bottom: 0;
    186     left: 0;
    187     right: 0;
    188   }
    189 </style>
    190 </head>
    191 <body>
    192   <div class="view">
    193   </div>
    194   <script>
    195   var linuxPerfData = "\\
    196 """
    197 
    198 html_suffix = """           dummy-0000  [000] 0.0: 0: trace_event_clock_sync: parent_ts=0.0\\n";
    199   </script>
    200 </body>
    201 </html>
    202 """
    203 
    204 compiled_css_tag = """<style type="text/css">%s</style>"""
    205 compiled_js_tag = """<script language="javascript">%s</script>"""
    206 
    207 linked_css_tag = """<link rel="stylesheet" href="%s"></link>"""
    208 linked_js_tag = """<script language="javascript" src="%s"></script>"""
    209 
    210 if __name__ == '__main__':
    211   main()
    212