Jamie Gennis | 9279147 | 2012-03-05 17:33:58 -0800 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | |
Jamie Gennis | 4b56a2b | 2012-04-28 01:06:56 -0700 | [diff] [blame] | 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. |
Jamie Gennis | 9279147 | 2012-03-05 17:33:58 -0800 | [diff] [blame] | 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 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 13 | import sys |
Jamie Gennis | 9279147 | 2012-03-05 17:33:58 -0800 | [diff] [blame] | 14 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 15 | # Make sure we're using a new enough version of Python. |
| 16 | # The flags= parameter of re.sub() is new in Python 2.7. |
| 17 | if sys.version_info[:2] < (2, 7): |
| 18 | print >> sys.stderr, '\nThis script requires Python 2.7 or newer.' |
| 19 | sys.exit(1) |
Jamie Gennis | 2da489c | 2012-09-19 18:06:29 -0700 | [diff] [blame] | 20 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 21 | # pylint: disable=g-bad-import-order,g-import-not-at-top |
| 22 | import imp |
| 23 | import optparse |
| 24 | import os |
Jamie Gennis | 664f21b | 2013-06-03 16:40:54 -0700 | [diff] [blame] | 25 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 26 | import util |
Jamie Gennis | 664f21b | 2013-06-03 16:40:54 -0700 | [diff] [blame] | 27 | |
Jamie Gennis | 664f21b | 2013-06-03 16:40:54 -0700 | [diff] [blame] | 28 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 29 | # The default agent directory. |
| 30 | DEFAULT_AGENT_DIR = 'agents' |
Jamie Gennis | 664f21b | 2013-06-03 16:40:54 -0700 | [diff] [blame] | 31 | |
Jamie Gennis | 664f21b | 2013-06-03 16:40:54 -0700 | [diff] [blame] | 32 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 33 | def parse_options(argv): |
| 34 | """Parses and checks the command-line options. |
Jamie Gennis | 664f21b | 2013-06-03 16:40:54 -0700 | [diff] [blame] | 35 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 36 | Returns: |
| 37 | A tuple containing the options structure and a list of categories to |
| 38 | be traced. |
| 39 | """ |
| 40 | usage = 'Usage: %prog [options] [category1 [category2 ...]]' |
| 41 | desc = 'Example: %prog -b 32768 -t 15 gfx input view sched freq' |
Jamie Gennis | fe4c594 | 2012-11-18 18:15:22 -0800 | [diff] [blame] | 42 | parser = optparse.OptionParser(usage=usage, description=desc) |
Jamie Gennis | 9279147 | 2012-03-05 17:33:58 -0800 | [diff] [blame] | 43 | parser.add_option('-o', dest='output_file', help='write HTML to FILE', |
| 44 | default='trace.html', metavar='FILE') |
| 45 | parser.add_option('-t', '--time', dest='trace_time', type='int', |
| 46 | help='trace for N seconds', metavar='N') |
Jamie Gennis | 98ef97d | 2012-03-07 16:06:53 -0800 | [diff] [blame] | 47 | parser.add_option('-b', '--buf-size', dest='trace_buf_size', type='int', |
| 48 | help='use a trace buffer size of N KB', metavar='N') |
Jamie Gennis | 553ec56 | 2012-11-20 17:45:49 -0800 | [diff] [blame] | 49 | parser.add_option('-k', '--ktrace', dest='kfuncs', action='store', |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 50 | help='specify a comma-separated list of kernel functions ' |
| 51 | 'to trace') |
| 52 | parser.add_option('-l', '--list-categories', dest='list_categories', |
| 53 | default=False, action='store_true', |
| 54 | help='list the available categories and exit') |
Jamie Gennis | b9a5fc8 | 2013-03-27 19:55:09 -0700 | [diff] [blame] | 55 | parser.add_option('-a', '--app', dest='app_name', default=None, type='string', |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 56 | action='store', |
| 57 | help='enable application-level tracing for comma-separated ' |
Jamie Gennis | b9a5fc8 | 2013-03-27 19:55:09 -0700 | [diff] [blame] | 58 | 'list of app cmdlines') |
Jeff Brown | c6e750f | 2014-08-15 16:27:54 -0700 | [diff] [blame] | 59 | parser.add_option('--no-fix-threads', dest='fix_threads', default=True, |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 60 | action='store_false', |
| 61 | help='don\'t fix missing or truncated thread names') |
Adrian Roos | 61c9bc2 | 2015-05-06 19:06:01 -0700 | [diff] [blame] | 62 | parser.add_option('--no-fix-circular', dest='fix_circular', default=True, |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 63 | action='store_false', |
| 64 | help='don\'t fix truncated circular traces') |
| 65 | parser.add_option('--no-compress', dest='compress_trace_data', |
| 66 | default=True, action='store_false', |
| 67 | help='Tell the device not to send the trace data in ' |
| 68 | 'compressed form.') |
Jeff Brown | 595ae1e | 2012-05-22 14:52:13 -0700 | [diff] [blame] | 69 | parser.add_option('--link-assets', dest='link_assets', default=False, |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 70 | action='store_true', |
| 71 | help='(deprecated)') |
Glenn Kasten | a0cfa1d | 2012-10-08 15:40:30 -0700 | [diff] [blame] | 72 | parser.add_option('--from-file', dest='from_file', action='store', |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 73 | help='read the trace from a file (compressed) rather than ' |
| 74 | 'running a live trace') |
Jamie Gennis | 2da489c | 2012-09-19 18:06:29 -0700 | [diff] [blame] | 75 | parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer', |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 76 | type='string', help='(deprecated)') |
Keun young Park | de427be | 2012-08-30 15:17:13 -0700 | [diff] [blame] | 77 | parser.add_option('-e', '--serial', dest='device_serial', type='string', |
| 78 | help='adb device serial number') |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 79 | parser.add_option('--agent-dirs', dest='agent_dirs', type='string', |
| 80 | help='the directories of additional systrace agent modules.' |
| 81 | ' The directories should be comma separated, e.g., ' |
| 82 | '--agent-dirs=dir1,dir2,dir3. Directory |%s| is the default' |
| 83 | ' agent directory and will always be checked.' |
| 84 | % DEFAULT_AGENT_DIR) |
Jamie Gennis | fe4c594 | 2012-11-18 18:15:22 -0800 | [diff] [blame] | 85 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 86 | options, categories = parser.parse_args(argv[1:]) |
Jamie Gennis | 9279147 | 2012-03-05 17:33:58 -0800 | [diff] [blame] | 87 | |
Chris Craik | b122baf | 2015-03-05 13:58:42 -0800 | [diff] [blame] | 88 | if options.link_assets or options.asset_dir != 'trace-viewer': |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 89 | parser.error('--link-assets and --asset-dir are deprecated.') |
Chris Craik | b122baf | 2015-03-05 13:58:42 -0800 | [diff] [blame] | 90 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 91 | if (options.trace_time is not None) and (options.trace_time <= 0): |
| 92 | parser.error('the trace time must be a positive number') |
Jamie Gennis | fe4c594 | 2012-11-18 18:15:22 -0800 | [diff] [blame] | 93 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 94 | if (options.trace_buf_size is not None) and (options.trace_buf_size <= 0): |
| 95 | parser.error('the trace buffer size must be a positive number') |
Jamie Gennis | b9a5fc8 | 2013-03-27 19:55:09 -0700 | [diff] [blame] | 96 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 97 | return (options, categories) |
Jamie Gennis | fe4c594 | 2012-11-18 18:15:22 -0800 | [diff] [blame] | 98 | |
Jamie Gennis | b9a5fc8 | 2013-03-27 19:55:09 -0700 | [diff] [blame] | 99 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 100 | def write_trace_html(html_filename, script_dir, agents): |
| 101 | """Writes out a trace html file. |
Jamie Gennis | 553ec56 | 2012-11-20 17:45:49 -0800 | [diff] [blame] | 102 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 103 | Args: |
| 104 | html_filename: The name of the file to write. |
| 105 | script_dir: The directory containing this script. |
| 106 | agents: The systrace agents. |
| 107 | """ |
| 108 | html_prefix = read_asset(script_dir, 'prefix.html') |
| 109 | html_suffix = read_asset(script_dir, 'suffix.html') |
| 110 | trace_viewer_html = read_asset(script_dir, 'systrace_trace_viewer.html') |
Jamie Gennis | fe4c594 | 2012-11-18 18:15:22 -0800 | [diff] [blame] | 111 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 112 | # Open the file in binary mode to prevent python from changing the |
| 113 | # line endings. |
| 114 | html_file = open(html_filename, 'wb') |
| 115 | html_file.write(html_prefix.replace('{{SYSTRACE_TRACE_VIEWER_HTML}}', |
| 116 | trace_viewer_html)) |
Jeff Brown | c6e750f | 2014-08-15 16:27:54 -0700 | [diff] [blame] | 117 | |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 118 | html_file.write('<!-- BEGIN TRACE -->\n') |
| 119 | for a in agents: |
| 120 | html_file.write(' <script class="') |
| 121 | html_file.write(a.get_class_name()) |
| 122 | html_file.write('" type="application/text">\n') |
| 123 | html_file.write(a.get_trace_data()) |
| 124 | html_file.write(' </script>\n') |
| 125 | html_file.write('<!-- END TRACE -->\n') |
| 126 | |
| 127 | html_file.write(html_suffix) |
| 128 | html_file.close() |
| 129 | print '\n wrote file://%s\n' % os.path.abspath(html_filename) |
| 130 | |
| 131 | |
| 132 | def create_agents(options, categories): |
| 133 | """Create systrace agents. |
| 134 | |
| 135 | This function will search systrace agent modules in agent directories and |
| 136 | create the corresponding systrace agents. |
| 137 | Args: |
| 138 | options: The command-line options. |
| 139 | categories: The trace categories to capture. |
| 140 | Returns: |
| 141 | The list of systrace agents. |
| 142 | """ |
| 143 | agent_dirs = [os.path.join(os.path.dirname(__file__), DEFAULT_AGENT_DIR)] |
| 144 | if options.agent_dirs: |
| 145 | agent_dirs.extend(options.agent_dirs.split(',')) |
| 146 | |
| 147 | agents = [] |
| 148 | for agent_dir in agent_dirs: |
| 149 | if not agent_dir: |
| 150 | continue |
| 151 | for filename in os.listdir(agent_dir): |
| 152 | (module_name, ext) = os.path.splitext(filename) |
| 153 | if ext != '.py' or module_name == '__init__': |
| 154 | continue |
| 155 | (f, pathname, data) = imp.find_module(module_name, [agent_dir]) |
| 156 | try: |
| 157 | module = imp.load_module(module_name, f, pathname, data) |
| 158 | finally: |
| 159 | if f: |
| 160 | f.close() |
| 161 | if module: |
| 162 | agent = module.try_create_agent(options, categories) |
| 163 | if not agent: |
| 164 | continue |
| 165 | agents.append(agent) |
| 166 | return agents |
| 167 | |
| 168 | |
| 169 | def main(): |
| 170 | options, categories = parse_options(sys.argv) |
| 171 | agents = create_agents(options, categories) |
| 172 | |
| 173 | if not agents: |
| 174 | dirs = DEFAULT_AGENT_DIR |
| 175 | if options.agent_dirs: |
| 176 | dirs += ',' + options.agent_dirs |
| 177 | print >> sys.stderr, ('No systrace agent is available in directories |%s|.' |
| 178 | % dirs) |
| 179 | sys.exit(1) |
| 180 | |
| 181 | for a in agents: |
| 182 | a.start() |
| 183 | |
| 184 | for a in agents: |
| 185 | a.collect_result() |
| 186 | if not a.expect_trace(): |
| 187 | # Nothing more to do. |
| 188 | return |
Glenn Kasten | a0cfa1d | 2012-10-08 15:40:30 -0700 | [diff] [blame] | 189 | |
Jamie Gennis | 4b56a2b | 2012-04-28 01:06:56 -0700 | [diff] [blame] | 190 | script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) |
Chris Craik | ad0b04f | 2015-07-21 13:44:22 -0700 | [diff] [blame] | 191 | write_trace_html(options.output_file, script_dir, agents) |
Jeff Brown | 595ae1e | 2012-05-22 14:52:13 -0700 | [diff] [blame] | 192 | |
Jamie Gennis | 9279147 | 2012-03-05 17:33:58 -0800 | [diff] [blame] | 193 | |
Siva Velusamy | 48ea076 | 2013-07-19 11:03:37 -0700 | [diff] [blame] | 194 | def read_asset(src_dir, filename): |
| 195 | return open(os.path.join(src_dir, filename)).read() |
| 196 | |
Jeff Brown | 595ae1e | 2012-05-22 14:52:13 -0700 | [diff] [blame] | 197 | |
Jamie Gennis | 9279147 | 2012-03-05 17:33:58 -0800 | [diff] [blame] | 198 | if __name__ == '__main__': |
| 199 | main() |