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