OLD | NEW |
| (Empty) |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """A utility script to help building Syzygy-instrumented Chrome binaries.""" | |
7 | |
8 import glob | |
9 import logging | |
10 import optparse | |
11 import os | |
12 import shutil | |
13 import subprocess | |
14 import sys | |
15 | |
16 | |
17 # The default directory containing the Syzygy toolchain. | |
18 _DEFAULT_SYZYGY_DIR = os.path.abspath(os.path.join( | |
19 os.path.dirname(__file__), '../../../..', | |
20 'third_party/syzygy/binaries/exe/')) | |
21 | |
22 # Basenames of various tools. | |
23 _INSTRUMENT_EXE = 'instrument.exe' | |
24 _GENFILTER_EXE = 'genfilter.exe' | |
25 | |
26 _LOGGER = logging.getLogger() | |
27 | |
28 | |
29 def _Shell(*cmd, **kw): | |
30 """Shells out to "cmd". Returns a tuple of cmd's stdout, stderr.""" | |
31 _LOGGER.info('Running command "%s".', cmd) | |
32 prog = subprocess.Popen(cmd, **kw) | |
33 | |
34 stdout, stderr = prog.communicate() | |
35 if prog.returncode != 0: | |
36 raise RuntimeError('Command "%s" returned %d.' % (cmd, prog.returncode)) | |
37 | |
38 return stdout, stderr | |
39 | |
40 | |
41 def _CompileFilter(syzygy_dir, executable, symbol, filter_file, | |
42 output_filter_file): | |
43 """Compiles the provided filter writing the compiled filter file to | |
44 output_filter_file. | |
45 """ | |
46 cmd = [os.path.abspath(os.path.join(syzygy_dir, _GENFILTER_EXE)), | |
47 '--action=compile', | |
48 '--input-image=%s' % executable, | |
49 '--input-pdb=%s' % symbol, | |
50 '--output-file=%s' % output_filter_file, | |
51 '--overwrite', | |
52 os.path.abspath(filter_file)] | |
53 | |
54 _Shell(*cmd) | |
55 if not os.path.exists(output_filter_file): | |
56 raise RuntimeError('Compiled filter file missing: %s' % output_filter_file) | |
57 return | |
58 | |
59 | |
60 def _InstrumentBinary(syzygy_dir, mode, executable, symbol, dst_dir, | |
61 filter_file): | |
62 """Instruments the executable found in input_dir, and writes the resultant | |
63 instrumented executable and symbol files to dst_dir. | |
64 """ | |
65 cmd = [os.path.abspath(os.path.join(syzygy_dir, _INSTRUMENT_EXE)), | |
66 '--overwrite', | |
67 '--mode=%s' % mode, | |
68 '--debug-friendly', | |
69 '--input-image=%s' % executable, | |
70 '--input-pdb=%s' % symbol, | |
71 '--output-image=%s' % os.path.abspath( | |
72 os.path.join(dst_dir, os.path.basename(executable))), | |
73 '--output-pdb=%s' % os.path.abspath( | |
74 os.path.join(dst_dir, os.path.basename(symbol)))] | |
75 | |
76 if mode == "asan": | |
77 cmd.append('--no-augment-pdb') | |
78 | |
79 # If a filter was specified then pass it on to the instrumenter. | |
80 if filter_file: | |
81 cmd.append('--filter=%s' % os.path.abspath(filter_file)) | |
82 | |
83 return _Shell(*cmd) | |
84 | |
85 | |
86 def main(options): | |
87 # Make sure the destination directory exists. | |
88 if not os.path.isdir(options.destination_dir): | |
89 _LOGGER.info('Creating destination directory "%s".', | |
90 options.destination_dir) | |
91 os.makedirs(options.destination_dir) | |
92 | |
93 # Compile the filter if one was provided. | |
94 if options.filter: | |
95 _CompileFilter(options.syzygy_dir, | |
96 options.input_executable, | |
97 options.input_symbol, | |
98 options.filter, | |
99 options.output_filter_file) | |
100 | |
101 # Instruments the binaries into the destination directory. | |
102 _InstrumentBinary(options.syzygy_dir, | |
103 options.mode, | |
104 options.input_executable, | |
105 options.input_symbol, | |
106 options.destination_dir, | |
107 options.output_filter_file) | |
108 | |
109 | |
110 def _ParseOptions(): | |
111 option_parser = optparse.OptionParser() | |
112 option_parser.add_option('--input_executable', | |
113 help='The path to the input executable.') | |
114 option_parser.add_option('--input_symbol', | |
115 help='The path to the input symbol file.') | |
116 option_parser.add_option('--mode', | |
117 help='Specifies which instrumentation mode is to be used.') | |
118 option_parser.add_option('--syzygy-dir', default=_DEFAULT_SYZYGY_DIR, | |
119 help='Instrumenter executable to use, defaults to "%default".') | |
120 option_parser.add_option('-d', '--destination_dir', | |
121 help='Destination directory for instrumented files.') | |
122 option_parser.add_option('--filter', | |
123 help='An optional filter. This will be compiled and passed to the ' | |
124 'instrumentation executable.') | |
125 option_parser.add_option('--output-filter-file', | |
126 help='The path where the compiled filter will be written. This is ' | |
127 'required if --filter is specified.') | |
128 options, args = option_parser.parse_args() | |
129 | |
130 if not options.mode: | |
131 option_parser.error('You must provide an instrumentation mode.') | |
132 if not options.input_executable: | |
133 option_parser.error('You must provide an input executable.') | |
134 if not options.input_symbol: | |
135 option_parser.error('You must provide an input symbol file.') | |
136 if not options.destination_dir: | |
137 option_parser.error('You must provide a destination directory.') | |
138 if options.filter and not options.output_filter_file: | |
139 option_parser.error('You must provide a filter output file.') | |
140 | |
141 return options | |
142 | |
143 | |
144 if '__main__' == __name__: | |
145 logging.basicConfig(level=logging.INFO) | |
146 sys.exit(main(_ParseOptions())) | |
OLD | NEW |