blob: 98eeb87711f251a04b70cf89ded97ce34103fb53 [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
136 js_files, js_flattenizer, css_files = get_assets(src_dir, build_dir)
137
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)
141 else:
142 css_filename = os.path.join(script_dir, flattened_css_file)
143 js_filename = os.path.join(script_dir, flattened_js_file)
144 css = compiled_css_tag % (open(css_filename).read())
145 js = compiled_js_tag % (open(js_filename).read())
146
147 html_filename = options.output_file
148
149 trace_started = False
150 leftovers = ''
151 adb = subprocess.Popen(atrace_args, stdout=subprocess.PIPE,
152 stderr=subprocess.PIPE)
153 dec = zlib.decompressobj()
154 while True:
155 ready = select.select([adb.stdout, adb.stderr], [], [adb.stdout, adb.stderr])
156 if adb.stderr in ready[0]:
157 err = os.read(adb.stderr.fileno(), 4096)
158 sys.stderr.write(err)
159 sys.stderr.flush()
160 if adb.stdout in ready[0]:
161 out = leftovers + os.read(adb.stdout.fileno(), 4096)
162 if options.from_file is None:
163 out = out.replace('\r\n', '\n')
164 if out.endswith('\r'):
165 out = out[:-1]
166 leftovers = '\r'
167 else:
168 leftovers = ''
169 if not trace_started:
170 lines = out.splitlines(True)
171 out = ''
172 for i, line in enumerate(lines):
173 if line == 'TRACE:\n':
174 sys.stdout.write("downloading trace...")
175 sys.stdout.flush()
176 out = ''.join(lines[i+1:])
177 html_file = open(html_filename, 'w')
178 html_file.write(html_prefix % (css, js))
179 trace_started = True
180 break
181 elif 'TRACE:'.startswith(line) and i == len(lines) - 1:
182 leftovers = line + leftovers
183 else:
184 sys.stdout.write(line)
185 sys.stdout.flush()
186 if len(out) > 0:
187 out = dec.decompress(out)
188 html_out = out.replace('\n', '\\n\\\n')
189 if len(html_out) > 0:
190 html_file.write(html_out)
191 result = adb.poll()
192 if result is not None:
193 break
194 if result != 0:
195 print >> sys.stderr, 'adb returned error code %d' % result
196 elif trace_started:
197 html_out = dec.flush().replace('\n', '\\n\\\n').replace('\r', '')
198 if len(html_out) > 0:
199 html_file.write(html_out)
200 html_file.write(html_suffix)
201 html_file.close()
202 print " done\n\n wrote file://%s/%s\n" % (os.getcwd(), options.output_file)
203 else:
204 print >> sys.stderr, ('An error occured while capturing the trace. Output ' +
205 'file was not written.')
206
207def get_assets(src_dir, build_dir):
208 sys.path.append(build_dir)
209 gen = __import__('generate_standalone_timeline_view', {}, {})
210 parse_deps = __import__('parse_deps', {}, {})
211 filenames = gen._get_input_filenames()
212 load_sequence = parse_deps.calc_load_sequence(filenames)
213
214 js_files = []
215 js_flattenizer = "window.FLATTENED = {};\n"
216 css_files = []
217
218 for module in load_sequence:
219 js_files.append(os.path.relpath(module.filename, src_dir))
220 js_flattenizer += "window.FLATTENED['%s'] = true;\n" % module.name
221 for style_sheet in module.style_sheets:
222 css_files.append(os.path.relpath(style_sheet.filename, src_dir))
223
224 sys.path.pop()
225
226 return (js_files, js_flattenizer, css_files)
227
228html_prefix = """<!DOCTYPE HTML>
229<html>
230<head i18n-values="dir:textdirection;">
231<title>Android System Trace</title>
232%s
233%s
234<script language="javascript">
235document.addEventListener('DOMContentLoaded', function() {
236 if (!linuxPerfData)
237 return;
238
239 var m = new tracing.Model(linuxPerfData);
240 var timelineViewEl = document.querySelector('.view');
241 tracing.ui.decorate(timelineViewEl, tracing.TimelineView);
242 timelineViewEl.model = m;
243 timelineViewEl.tabIndex = 1;
244 timelineViewEl.timeline.focusElement = timelineViewEl;
245});
246</script>
247<style>
248 .view {
249 overflow: hidden;
250 position: absolute;
251 top: 0;
252 bottom: 0;
253 left: 0;
254 right: 0;
255 }
256</style>
257</head>
258<body>
259 <div class="view">
260 </div>
261<!-- BEGIN TRACE -->
262 <script>
263 var linuxPerfData = "\\
264"""
265
266html_suffix = """\\n";
267 </script>
268<!-- END TRACE -->
269</body>
270</html>
271"""
272
273compiled_css_tag = """<style type="text/css">%s</style>"""
274compiled_js_tag = """<script language="javascript">%s</script>"""
275
276linked_css_tag = """<link rel="stylesheet" href="%s"></link>"""
277linked_js_tag = """<script language="javascript" src="%s"></script>"""
278
279if __name__ == '__main__':
280 main()