blob: a20f6b5766e9ba0f70e2a27556cdbe40c49230d6 [file] [log] [blame]
Jamie Gennis664f21b2013-06-03 16:40:54 -07001#!/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 errno, optparse, os, select, subprocess, sys, time, zlib
14
15# This list is based on the tags in frameworks/native/include/utils/Trace.h.
16trace_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 'camera': 1<<10,
27}
28
29flattened_css_file = 'style.css'
30flattened_js_file = 'script.js'
31
32def add_adb_serial(command, serial):
33 if serial != None:
34 command.insert(1, serial)
35 command.insert(1, '-s')
36
37def main():
38 parser = optparse.OptionParser()
39 parser.add_option('-o', dest='output_file', help='write HTML to FILE',
40 default='trace.html', metavar='FILE')
41 parser.add_option('-t', '--time', dest='trace_time', type='int',
42 help='trace for N seconds', metavar='N')
43 parser.add_option('-b', '--buf-size', dest='trace_buf_size', type='int',
44 help='use a trace buffer size of N KB', metavar='N')
45 parser.add_option('-d', '--disk', dest='trace_disk', default=False,
46 action='store_true', help='trace disk I/O (requires root)')
47 parser.add_option('-f', '--cpu-freq', dest='trace_cpu_freq', default=False,
48 action='store_true', help='trace CPU frequency changes')
49 parser.add_option('-i', '--cpu-idle', dest='trace_cpu_idle', default=False,
50 action='store_true', help='trace CPU idle events')
51 parser.add_option('-l', '--cpu-load', dest='trace_cpu_load', default=False,
52 action='store_true', help='trace CPU load')
53 parser.add_option('-s', '--no-cpu-sched', dest='trace_cpu_sched', default=True,
54 action='store_false', help='inhibit tracing CPU ' +
55 'scheduler (allows longer trace times by reducing data ' +
56 'rate into buffer)')
57 parser.add_option('-u', '--bus-utilization', dest='trace_bus_utilization',
58 default=False, action='store_true',
59 help='trace bus utilization (requires root)')
60 parser.add_option('-w', '--workqueue', dest='trace_workqueue', default=False,
61 action='store_true', help='trace the kernel workqueues ' +
62 '(requires root)')
63 parser.add_option('--set-tags', dest='set_tags', action='store',
64 help='set the enabled trace tags and exit; set to a ' +
65 'comma separated list of: ' +
66 ', '.join(trace_tag_bits.iterkeys()))
67 parser.add_option('--link-assets', dest='link_assets', default=False,
68 action='store_true', help='link to original CSS or JS resources '
69 'instead of embedding them')
70 parser.add_option('--from-file', dest='from_file', action='store',
71 help='read the trace from a file rather than running a live trace')
72 parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer',
73 type='string', help='')
74 parser.add_option('-e', '--serial', dest='device_serial', type='string',
75 help='adb device serial number')
76 options, args = parser.parse_args()
77
78 if options.set_tags:
79 flags = 0
80 tags = options.set_tags.split(',')
81 for tag in tags:
82 try:
83 flags |= trace_tag_bits[tag]
84 except KeyError:
85 parser.error('unrecognized tag: %s\nknown tags are: %s' %
86 (tag, ', '.join(trace_tag_bits.iterkeys())))
87 atrace_args = ['adb', 'shell', 'setprop', 'debug.atrace.tags.enableflags', hex(flags)]
88 add_adb_serial(atrace_args, options.device_serial)
89 try:
90 subprocess.check_call(atrace_args)
91 except subprocess.CalledProcessError, e:
92 print >> sys.stderr, 'unable to set tags: %s' % e
93 print '\nSet enabled tags to: %s\n' % ', '.join(tags)
94 print ('You will likely need to restart the Android framework for this to ' +
95 'take effect:\n\n adb shell stop\n adb shell ' +
96 'start\n')
97 return
98
99 atrace_args = ['adb', 'shell', 'atrace', '-z']
100 add_adb_serial(atrace_args, options.device_serial)
101
102 if options.trace_disk:
103 atrace_args.append('-d')
104 if options.trace_cpu_freq:
105 atrace_args.append('-f')
106 if options.trace_cpu_idle:
107 atrace_args.append('-i')
108 if options.trace_cpu_load:
109 atrace_args.append('-l')
110 if options.trace_cpu_sched:
111 atrace_args.append('-s')
112 if options.trace_bus_utilization:
113 atrace_args.append('-u')
114 if options.trace_workqueue:
115 atrace_args.append('-w')
116 if options.trace_time is not None:
117 if options.trace_time > 0:
118 atrace_args.extend(['-t', str(options.trace_time)])
119 else:
120 parser.error('the trace time must be a positive number')
121 if options.trace_buf_size is not None:
122 if options.trace_buf_size > 0:
123 atrace_args.extend(['-b', str(options.trace_buf_size)])
124 else:
125 parser.error('the trace buffer size must be a positive number')
126
127 if options.from_file is not None:
128 atrace_args = ['cat', options.from_file]
129
130 script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
131
132 if options.link_assets:
133 src_dir = os.path.join(script_dir, options.asset_dir, 'src')
134 build_dir = os.path.join(script_dir, options.asset_dir, 'build')
135
Jamie Gennis66a37682013-07-15 18:29:18 -0700136 js_files, js_flattenizer, css_files, templates = get_assets(src_dir, build_dir)
Jamie Gennis664f21b2013-06-03 16:40:54 -0700137
138 css = '\n'.join(linked_css_tag % (os.path.join(src_dir, f)) for f in css_files)
139 js = '<script language="javascript">\n%s</script>\n' % js_flattenizer
140 js += '\n'.join(linked_js_tag % (os.path.join(src_dir, f)) for f in js_files)
Jamie Gennis66a37682013-07-15 18:29:18 -0700141
Jamie Gennis664f21b2013-06-03 16:40:54 -0700142 else:
143 css_filename = os.path.join(script_dir, flattened_css_file)
144 js_filename = os.path.join(script_dir, flattened_js_file)
145 css = compiled_css_tag % (open(css_filename).read())
146 js = compiled_js_tag % (open(js_filename).read())
Jamie Gennis66a37682013-07-15 18:29:18 -0700147 templates = ''
Jamie Gennis664f21b2013-06-03 16:40:54 -0700148
149 html_filename = options.output_file
150
151 trace_started = False
152 leftovers = ''
153 adb = subprocess.Popen(atrace_args, stdout=subprocess.PIPE,
154 stderr=subprocess.PIPE)
155 dec = zlib.decompressobj()
156 while True:
157 ready = select.select([adb.stdout, adb.stderr], [], [adb.stdout, adb.stderr])
158 if adb.stderr in ready[0]:
159 err = os.read(adb.stderr.fileno(), 4096)
160 sys.stderr.write(err)
161 sys.stderr.flush()
162 if adb.stdout in ready[0]:
163 out = leftovers + os.read(adb.stdout.fileno(), 4096)
164 if options.from_file is None:
165 out = out.replace('\r\n', '\n')
166 if out.endswith('\r'):
167 out = out[:-1]
168 leftovers = '\r'
169 else:
170 leftovers = ''
171 if not trace_started:
172 lines = out.splitlines(True)
173 out = ''
174 for i, line in enumerate(lines):
175 if line == 'TRACE:\n':
176 sys.stdout.write("downloading trace...")
177 sys.stdout.flush()
178 out = ''.join(lines[i+1:])
Siva Velusamy9c39c342013-07-19 14:10:13 -0700179 html_prefix = read_asset(script_dir, 'prefix.html')
Jamie Gennis664f21b2013-06-03 16:40:54 -0700180 html_file = open(html_filename, 'w')
Jamie Gennis66a37682013-07-15 18:29:18 -0700181 html_file.write(html_prefix % (css, js, templates))
Jamie Gennis664f21b2013-06-03 16:40:54 -0700182 trace_started = True
183 break
184 elif 'TRACE:'.startswith(line) and i == len(lines) - 1:
185 leftovers = line + leftovers
186 else:
187 sys.stdout.write(line)
188 sys.stdout.flush()
189 if len(out) > 0:
190 out = dec.decompress(out)
191 html_out = out.replace('\n', '\\n\\\n')
192 if len(html_out) > 0:
193 html_file.write(html_out)
194 result = adb.poll()
195 if result is not None:
196 break
197 if result != 0:
198 print >> sys.stderr, 'adb returned error code %d' % result
199 elif trace_started:
200 html_out = dec.flush().replace('\n', '\\n\\\n').replace('\r', '')
201 if len(html_out) > 0:
202 html_file.write(html_out)
Siva Velusamy9c39c342013-07-19 14:10:13 -0700203 html_suffix = read_asset(script_dir, 'suffix.html')
Jamie Gennis664f21b2013-06-03 16:40:54 -0700204 html_file.write(html_suffix)
205 html_file.close()
Jamie Gennis66a37682013-07-15 18:29:18 -0700206 print " done\n\n wrote file://%s\n" % (os.path.abspath(options.output_file))
Jamie Gennis664f21b2013-06-03 16:40:54 -0700207 else:
208 print >> sys.stderr, ('An error occured while capturing the trace. Output ' +
209 'file was not written.')
210
Siva Velusamy9c39c342013-07-19 14:10:13 -0700211def read_asset(src_dir, filename):
212 return open(os.path.join(src_dir, filename)).read()
213
Jamie Gennis664f21b2013-06-03 16:40:54 -0700214def get_assets(src_dir, build_dir):
215 sys.path.append(build_dir)
216 gen = __import__('generate_standalone_timeline_view', {}, {})
217 parse_deps = __import__('parse_deps', {}, {})
Jamie Gennis66a37682013-07-15 18:29:18 -0700218 gen_templates = __import__('generate_template_contents', {}, {})
Jamie Gennis664f21b2013-06-03 16:40:54 -0700219 filenames = gen._get_input_filenames()
Jamie Gennis66a37682013-07-15 18:29:18 -0700220 load_sequence = parse_deps.calc_load_sequence(filenames, src_dir)
Jamie Gennis664f21b2013-06-03 16:40:54 -0700221
222 js_files = []
223 js_flattenizer = "window.FLATTENED = {};\n"
Jamie Gennis66a37682013-07-15 18:29:18 -0700224 js_flattenizer += "window.FLATTENED_RAW_SCRIPTS = {};\n"
Jamie Gennis664f21b2013-06-03 16:40:54 -0700225 css_files = []
226
227 for module in load_sequence:
228 js_files.append(os.path.relpath(module.filename, src_dir))
229 js_flattenizer += "window.FLATTENED['%s'] = true;\n" % module.name
Jamie Gennis66a37682013-07-15 18:29:18 -0700230 for dependent_raw_script_name in module.dependent_raw_script_names:
231 js_flattenizer += (
232 "window.FLATTENED_RAW_SCRIPTS['%s'] = true;\n" %
233 dependent_raw_script_name)
234
Jamie Gennis664f21b2013-06-03 16:40:54 -0700235 for style_sheet in module.style_sheets:
236 css_files.append(os.path.relpath(style_sheet.filename, src_dir))
237
Jamie Gennis66a37682013-07-15 18:29:18 -0700238 templates = gen_templates.generate_templates()
239
Jamie Gennis664f21b2013-06-03 16:40:54 -0700240 sys.path.pop()
241
Jamie Gennis66a37682013-07-15 18:29:18 -0700242 return (js_files, js_flattenizer, css_files, templates)
Jamie Gennis664f21b2013-06-03 16:40:54 -0700243
Jamie Gennis664f21b2013-06-03 16:40:54 -0700244compiled_css_tag = """<style type="text/css">%s</style>"""
245compiled_js_tag = """<script language="javascript">%s</script>"""
246
247linked_css_tag = """<link rel="stylesheet" href="%s"></link>"""
248linked_js_tag = """<script language="javascript" src="%s"></script>"""
249
250if __name__ == '__main__':
251 main()