• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
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.TimelineModel(linuxPerfData);
240  var timelineViewEl = document.querySelector('.view');
241  base.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()
281