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, allocation_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 # Disable some of the new SysyASAN features. We're seeing an increase in |
| 79 # crash rates and are wondering if they are to blame. |
| 80 cmd.append( |
| 81 '--asan-rtl-options="--enable_feature_randomization ' |
| 82 '--prevent_duplicate_corruption_crashes"') |
| 83 |
| 84 # If any filters were specified then pass them on to the instrumenter. |
| 85 if filter_file: |
| 86 cmd.append('--filter=%s' % os.path.abspath(filter_file)) |
| 87 if allocation_filter_file: |
| 88 cmd.append('--allocation-filter-config-file=%s' % |
| 89 os.path.abspath(allocation_filter_file)) |
| 90 |
| 91 return _Shell(*cmd) |
| 92 |
| 93 |
| 94 def main(options): |
| 95 # Make sure the destination directory exists. |
| 96 if not os.path.isdir(options.destination_dir): |
| 97 _LOGGER.info('Creating destination directory "%s".', |
| 98 options.destination_dir) |
| 99 os.makedirs(options.destination_dir) |
| 100 |
| 101 # Compile the filter if one was provided. |
| 102 if options.filter: |
| 103 _CompileFilter(options.syzygy_dir, |
| 104 options.input_executable, |
| 105 options.input_symbol, |
| 106 options.filter, |
| 107 options.output_filter_file) |
| 108 |
| 109 # Instruments the binaries into the destination directory. |
| 110 _InstrumentBinary(options.syzygy_dir, |
| 111 options.mode, |
| 112 options.input_executable, |
| 113 options.input_symbol, |
| 114 options.destination_dir, |
| 115 options.output_filter_file, |
| 116 options.allocation_filter_file) |
| 117 |
| 118 |
| 119 def _ParseOptions(): |
| 120 option_parser = optparse.OptionParser() |
| 121 option_parser.add_option('--input_executable', |
| 122 help='The path to the input executable.') |
| 123 option_parser.add_option('--input_symbol', |
| 124 help='The path to the input symbol file.') |
| 125 option_parser.add_option('--mode', |
| 126 help='Specifies which instrumentation mode is to be used.') |
| 127 option_parser.add_option('--syzygy-dir', default=_DEFAULT_SYZYGY_DIR, |
| 128 help='Instrumenter executable to use, defaults to "%default".') |
| 129 option_parser.add_option('-d', '--destination_dir', |
| 130 help='Destination directory for instrumented files.') |
| 131 option_parser.add_option('--filter', |
| 132 help='An optional filter. This will be compiled and passed to the ' |
| 133 'instrumentation executable.') |
| 134 option_parser.add_option('--output-filter-file', |
| 135 help='The path where the compiled filter will be written. This is ' |
| 136 'required if --filter is specified.') |
| 137 option_parser.add_option('--allocation-filter-file', |
| 138 help='The path to the SyzyASAN allocation filter to use.') |
| 139 options, args = option_parser.parse_args() |
| 140 |
| 141 if not options.mode: |
| 142 option_parser.error('You must provide an instrumentation mode.') |
| 143 if not options.input_executable: |
| 144 option_parser.error('You must provide an input executable.') |
| 145 if not options.input_symbol: |
| 146 option_parser.error('You must provide an input symbol file.') |
| 147 if not options.destination_dir: |
| 148 option_parser.error('You must provide a destination directory.') |
| 149 if options.filter and not options.output_filter_file: |
| 150 option_parser.error('You must provide a filter output file.') |
| 151 |
| 152 return options |
| 153 |
| 154 |
| 155 if '__main__' == __name__: |
| 156 logging.basicConfig(level=logging.INFO) |
| 157 sys.exit(main(_ParseOptions())) |
OLD | NEW |