Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
|
Zhen Wang
2016/03/14 17:11:27
+ccraik, do we need to keep the old file name (sys
alexandermont
2016/03/14 22:19:28
FYI, the reason that I changed the name systrace.p
Zhen Wang
2016/03/16 16:54:29
If you change it to run_systrace.py, you will also
| |
| 2 | 2 |
| 3 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 3 # Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 """Android system-wide tracing utility. | 7 """Android system-wide tracing utility. |
| 8 | 8 |
| 9 This is a tool for capturing a trace that includes data from both userland and | 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. | 10 the kernel. It creates an HTML file for visualizing the trace. |
| 11 """ | 11 """ |
| 12 | 12 |
| 13 import sys | 13 import sys |
| 14 import time | |
| 15 from devil.utils import cmd_helper | |
| 16 from systrace import systrace_tracing_controller | |
| 14 | 17 |
| 15 # Make sure we're using a new enough version of Python. | 18 # Make sure we're using a new enough version of Python. |
| 16 # The flags= parameter of re.sub() is new in Python 2.7. And Systrace does not | 19 # The flags= parameter of re.sub() is new in Python 2.7. And Systrace does not |
| 17 # support Python 3 yet. | 20 # support Python 3 yet. |
| 18 version = sys.version_info[:2] | 21 version = sys.version_info[:2] |
| 19 if version != (2, 7): | 22 if version != (2, 7): |
| 20 sys.stderr.write('This script does not support Python %d.%d. ' | 23 sys.stderr.write('This script does not support Python %d.%d. ' |
| 21 'Please use Python 2.7.\n' % version) | 24 'Please use Python 2.7.\n' % version) |
| 22 sys.exit(1) | 25 sys.exit(1) |
| 23 | 26 |
| 24 import imp | |
| 25 import optparse | 27 import optparse |
| 26 import os | 28 import os |
| 27 | 29 |
| 28 | |
| 29 # The default agent directory. | |
| 30 DEFAULT_AGENT_DIR = 'agents' | |
| 31 | |
| 32 | |
| 33 def parse_options(argv): | 30 def parse_options(argv): |
| 34 """Parses and checks the command-line options. | 31 """Parses and checks the command-line options. |
| 35 | 32 |
| 36 Returns: | 33 Returns: |
| 37 A tuple containing the options structure and a list of categories to | 34 A tuple containing the options structure and a list of categories to |
| 38 be traced. | 35 be traced. |
| 39 """ | 36 """ |
| 40 usage = 'Usage: %prog [options] [category1 [category2 ...]]' | 37 usage = 'Usage: %prog [options] [category1 [category2 ...]]' |
| 41 desc = 'Example: %prog -b 32768 -t 15 gfx input view sched freq' | 38 desc = 'Example: %prog -b 32768 -t 15 gfx input view sched freq' |
| 42 parser = optparse.OptionParser(usage=usage, description=desc) | 39 parser = optparse.OptionParser(usage=usage, description=desc) |
| 43 parser.add_option('-o', dest='output_file', help='write HTML to FILE', | 40 parser.add_option('-o', dest='output_file', help='write HTML to FILE', |
| 44 default='trace.html', metavar='FILE') | 41 default='trace.html', metavar='FILE') |
| 45 parser.add_option('-t', '--time', dest='trace_time', type='int', | 42 parser.add_option('-t', '--time', dest='trace_time', type='int', |
| 46 help='trace for N seconds', metavar='N') | 43 help='trace for N seconds', metavar='N') |
| 44 parser.add_option('-i', '--interrupt', dest='interrupt', default=False, | |
|
Chris Craik
2016/03/12 02:02:34
How would folks feel about making this the default
Zhen Wang
2016/03/14 17:11:27
+1
alexandermont
2016/03/14 22:19:28
Done
| |
| 45 action='store_true', help='keep tracing until' | |
| 46 ' interrupted by user pressing control-C') | |
| 47 parser.add_option('-b', '--buf-size', dest='trace_buf_size', type='int', | 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') | 48 help='use a trace buffer size of N KB', metavar='N') |
| 49 parser.add_option('-k', '--ktrace', dest='kfuncs', action='store', | 49 parser.add_option('-k', '--ktrace', dest='kfuncs', action='store', |
| 50 help='specify a comma-separated list of kernel functions ' | 50 help='specify a comma-separated list of kernel functions ' |
| 51 'to trace') | 51 'to trace') |
| 52 parser.add_option('-l', '--list-categories', dest='list_categories', | 52 parser.add_option('-l', '--list-categories', dest='list_categories', |
| 53 default=False, action='store_true', | 53 default=False, action='store_true', |
| 54 help='list the available categories and exit') | 54 help='list the available categories and exit') |
| 55 parser.add_option('-a', '--app', dest='app_name', default=None, type='string', | 55 parser.add_option('-a', '--app', dest='app_name', default=None, type='string', |
| 56 action='store', | 56 action='store', |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 82 'running a live trace') | 82 'running a live trace') |
| 83 parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer', | 83 parser.add_option('--asset-dir', dest='asset_dir', default='trace-viewer', |
| 84 type='string', help='(deprecated)') | 84 type='string', help='(deprecated)') |
| 85 parser.add_option('-e', '--serial', dest='device_serial', type='string', | 85 parser.add_option('-e', '--serial', dest='device_serial', type='string', |
| 86 help='adb device serial number') | 86 help='adb device serial number') |
| 87 parser.add_option('--agent-dirs', dest='agent_dirs', type='string', | 87 parser.add_option('--agent-dirs', dest='agent_dirs', type='string', |
| 88 help='the directories of additional systrace agent modules.' | 88 help='the directories of additional systrace agent modules.' |
| 89 ' The directories should be comma separated, e.g., ' | 89 ' The directories should be comma separated, e.g., ' |
| 90 '--agent-dirs=dir1,dir2,dir3. Directory |%s| is the default' | 90 '--agent-dirs=dir1,dir2,dir3. Directory |%s| is the default' |
| 91 ' agent directory and will always be checked.' | 91 ' agent directory and will always be checked.' |
| 92 % DEFAULT_AGENT_DIR) | 92 % systrace_tracing_controller.DEFAULT_AGENT_DIR) |
| 93 parser.add_option('--target', dest='target', default='android', type='string', | 93 parser.add_option('--target', dest='target', default='android', type='string', |
| 94 help='chose tracing target (android or linux)') | 94 help='chose tracing target (android or linux)') |
| 95 | 95 |
| 96 options, categories = parser.parse_args(argv[1:]) | 96 options, categories = parser.parse_args(argv[1:]) |
| 97 | 97 |
| 98 if options.link_assets or options.asset_dir != 'trace-viewer': | 98 if options.link_assets or options.asset_dir != 'trace-viewer': |
| 99 parser.error('--link-assets and --asset-dir are deprecated.') | 99 parser.error('--link-assets and --asset-dir are deprecated.') |
| 100 | 100 |
| 101 if (options.trace_time is not None) and (options.trace_time <= 0): | 101 if (options.trace_time is not None) and (options.trace_time <= 0): |
| 102 parser.error('the trace time must be a positive number') | 102 parser.error('the trace time must be a positive number') |
| 103 | 103 |
| 104 if (options.trace_buf_size is not None) and (options.trace_buf_size <= 0): | 104 if (options.trace_buf_size is not None) and (options.trace_buf_size <= 0): |
| 105 parser.error('the trace buffer size must be a positive number') | 105 parser.error('the trace buffer size must be a positive number') |
| 106 | 106 |
| 107 return (options, categories) | 107 return (options, categories) |
| 108 | 108 |
| 109 | 109 def get_device_serials(): |
| 110 def write_trace_html(html_filename, script_dir, agents): | 110 cmdout = cmd_helper.GetCmdOutput(['adb', 'devices']) |
| 111 """Writes out a trace html file. | 111 lines = [x.split() for x in cmdout.splitlines()[1:-1]] |
| 112 | 112 return [x[0] for x in lines if x[1] == 'device'] |
| 113 Args: | |
| 114 html_filename: The name of the file to write. | |
| 115 script_dir: The directory containing this script. | |
| 116 agents: The systrace agents. | |
| 117 """ | |
| 118 systrace_dir = os.path.abspath(os.path.dirname(__file__)) | |
| 119 html_prefix = read_asset(systrace_dir, 'prefix.html') | |
| 120 html_suffix = read_asset(systrace_dir, 'suffix.html') | |
| 121 trace_viewer_html = read_asset(script_dir, 'systrace_trace_viewer.html') | |
| 122 | |
| 123 # Open the file in binary mode to prevent python from changing the | |
| 124 # line endings. | |
| 125 html_file = open(html_filename, 'wb') | |
| 126 html_file.write(html_prefix.replace('{{SYSTRACE_TRACE_VIEWER_HTML}}', | |
| 127 trace_viewer_html)) | |
| 128 | |
| 129 html_file.write('<!-- BEGIN TRACE -->\n') | |
| 130 for a in agents: | |
| 131 html_file.write(' <script class="') | |
| 132 html_file.write(a.get_class_name()) | |
| 133 html_file.write('" type="application/text">\n') | |
| 134 html_file.write(a.get_trace_data()) | |
| 135 html_file.write(' </script>\n') | |
| 136 html_file.write('<!-- END TRACE -->\n') | |
| 137 | |
| 138 html_file.write(html_suffix) | |
| 139 html_file.close() | |
| 140 print '\n wrote file://%s\n' % os.path.abspath(html_filename) | |
| 141 | |
| 142 | |
| 143 def create_agents(options, categories): | |
| 144 """Create systrace agents. | |
| 145 | |
| 146 This function will search systrace agent modules in agent directories and | |
| 147 create the corresponding systrace agents. | |
| 148 Args: | |
| 149 options: The command-line options. | |
| 150 categories: The trace categories to capture. | |
| 151 Returns: | |
| 152 The list of systrace agents. | |
| 153 """ | |
| 154 agent_dirs = [os.path.join(os.path.dirname(__file__), DEFAULT_AGENT_DIR)] | |
| 155 if options.agent_dirs: | |
| 156 agent_dirs.extend(options.agent_dirs.split(',')) | |
| 157 | |
| 158 agents = [] | |
| 159 for agent_dir in agent_dirs: | |
| 160 if not agent_dir: | |
| 161 continue | |
| 162 for filename in os.listdir(agent_dir): | |
| 163 (module_name, ext) = os.path.splitext(filename) | |
| 164 if (ext != '.py' or module_name == '__init__' | |
| 165 or module_name.endswith('_unittest')): | |
| 166 continue | |
| 167 (f, pathname, data) = imp.find_module(module_name, [agent_dir]) | |
| 168 try: | |
| 169 module = imp.load_module(module_name, f, pathname, data) | |
| 170 finally: | |
| 171 if f: | |
| 172 f.close() | |
| 173 if module: | |
| 174 agent = module.try_create_agent(options, categories) | |
| 175 if not agent: | |
| 176 continue | |
| 177 agents.append(agent) | |
| 178 return agents | |
| 179 | |
| 180 | 113 |
| 181 def main(): | 114 def main(): |
| 182 options, categories = parse_options(sys.argv) | 115 options, categories = parse_options(sys.argv) |
| 183 agents = create_agents(options, categories) | 116 if not options.device_serial: |
| 117 devices = get_device_serials() | |
| 118 if len(devices) == 0: | |
| 119 raise RuntimeError('No ADB devices connected.') | |
| 120 elif len(devices) >= 2: | |
| 121 raise RuntimeError('Multiple devices connected, serial number required') | |
| 122 options.device_serial = devices[0] | |
| 123 script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) | |
| 124 controller = systrace_tracing_controller.SystraceTracingController( | |
| 125 script_dir, options, categories) | |
| 126 controller.StartTracing() | |
| 127 if options.from_file is not None: | |
| 128 print 'Reading results from file.' | |
| 129 elif options.interrupt: | |
| 130 print 'Starting tracing (stop with control-C)' | |
| 131 while True: | |
| 132 try: | |
| 133 time.sleep(0.5) | |
| 134 except KeyboardInterrupt: | |
| 135 break | |
| 136 else: | |
| 137 print 'Starting tracing (%d seconds)' % options.trace_time | |
| 138 time.sleep(options.trace_time) | |
| 184 | 139 |
| 185 if not agents: | 140 print 'Tracing completed. Collecting output...' |
| 186 dirs = DEFAULT_AGENT_DIR | 141 controller.StopTracing() |
| 187 if options.agent_dirs: | 142 print 'Outputting Systrace results...' |
| 188 dirs += ',' + options.agent_dirs | 143 controller.OutputSystraceResults() |
| 189 sys.stderr.write('No systrace agent is available in directories |%s|.\n' % | |
| 190 dirs) | |
| 191 sys.exit(1) | |
| 192 | |
| 193 try: | |
| 194 from . import update_systrace_trace_viewer | |
| 195 except ImportError: | |
| 196 pass | |
| 197 else: | |
| 198 update_systrace_trace_viewer.update() | |
| 199 | |
| 200 for a in agents: | |
| 201 a.start() | |
| 202 | |
| 203 for a in agents: | |
| 204 a.collect_result() | |
| 205 if not a.expect_trace(): | |
| 206 # Nothing more to do. | |
| 207 return | |
| 208 | |
| 209 script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) | |
| 210 write_trace_html(options.output_file, script_dir, agents) | |
| 211 | |
| 212 | |
| 213 def read_asset(src_dir, filename): | |
| 214 return open(os.path.join(src_dir, filename)).read() | |
| 215 | |
| 216 | 144 |
| 217 if __name__ == '__main__' and __package__ is None: | 145 if __name__ == '__main__' and __package__ is None: |
| 218 # Add current package to search path. | 146 # Add current package to search path. |
| 219 _SYSTRACE_DIR = os.path.abspath( | 147 _SYSTRACE_DIR = os.path.abspath( |
| 220 os.path.join(os.path.dirname(__file__), os.path.pardir)) | 148 os.path.join(os.path.dirname(__file__), os.path.pardir)) |
| 221 sys.path.insert(0, _SYSTRACE_DIR) | 149 sys.path.insert(0, _SYSTRACE_DIR) |
| 222 __package__ = "systrace" # pylint: disable=redefined-builtin | 150 __package__ = "systrace" # pylint: disable=redefined-builtin |
| 223 | 151 |
| 224 main() | 152 main() |
| OLD | NEW |