blob: 109f7696be46af64359bae9295aac34373b09c69 [file] [log] [blame]
#!python
# Copyright 2012 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""A utility script to automate the process of instrumenting Chrome."""
import chrome_utils
import logging
import optparse
import os.path
import runner
import shutil
import sys
# These binaries will always be instrumented.
EXECUTABLES = ['chrome.dll']
# These binaries will only be instrumented if present.
EXECUTABLES_OPTIONAL = ['chrome_child.dll']
_LOGGER = logging.getLogger(__name__)
class InstrumentationError(Exception):
"""Raised on failure in the instrumentation process."""
pass
_MODE_INFO = {
'asan': 'syzyasan_rtl.dll',
'bbentry': 'basic_block_entry_client.dll',
'calltrace': 'call_trace_client.dll',
'coverage': 'coverage_client.dll',
'fuzzing': None,
'profile' : 'profile_client.dll'
}
MODES = _MODE_INFO.keys()
DEFAULT_MODE = 'calltrace'
def InstrumentExecutable(
chrome_dir, output_dir, mode, args, agent_dll, executable, optional):
"""Makes an instrumented copy of the Chrome files in |chrome_dir| in
|output_dir|.
Args:
chrome_dir: the directory containing the input files.
output_dir: the directory where the output will be generated.
mode: the instrumentation mode to use.
args: additional arguments for instrument.exe
agent_dll: the basename of the agent DLL to use.
executable: the basename of the executable to instrument.
optional: if True then this will succeed even if the binary does not
exist; otherwise, this will fail if the binary does not exist.
Raises:
InstrumentationError if instrumentation fails.
"""
src_file = os.path.join(chrome_dir, executable)
if not os.path.isfile(src_file):
if not optional:
raise InstrumentationError('Missing mandatory "%s".' % executable)
_LOGGER.info('Not instrumenting missing optional "%s".', executable)
return
_LOGGER.info('Instrumenting %s "%s".',
"optional" if optional else "mandatory",
executable)
dst_file = os.path.join(output_dir, executable)
# In generating the command-line we place the additional arguments first
# so that we can subsequently override those arguments we explicitly set.
cmd = [runner._GetExePath('instrument.exe')]
cmd += args
cmd += ['--input-image=%s' % src_file,
'--output-image=%s' % dst_file,
'--mode=%s' % mode,
'--overwrite']
if agent_dll:
cmd.append('--agent=%s' % agent_dll)
if mode == 'profile':
cmd.append('--no-interior-refs')
ret = chrome_utils.Subprocess(cmd)
if ret != 0:
raise InstrumentationError('Failed to instrument "%s".' % executable)
# Give us silent access to internal member functions of the runner.
# pylint: disable=W0212
def InstrumentChrome(chrome_dir, output_dir, mode, args):
"""Makes an instrumented copy of the Chrome files in chrome_dir in
output_dir.
Args:
chrome_dir: the directory containing the input files.
output_dir: the directory where the output will be generated.
mode: the instrumentation mode to use.
args: additional arguments for instrument.exe
Raises:
InstrumentationError if instrumentation fails.
"""
if mode not in _MODE_INFO:
raise InstrumentationError("Unrecognized mode: %s" % mode)
_LOGGER.info('Copying chrome files from "%s" to "%s".',
chrome_dir,
output_dir)
chrome_utils.CopyChromeFiles(chrome_dir, output_dir)
# Drop the agent DLL, if any, into the output dir.
agent_dll = _MODE_INFO[mode]
if agent_dll:
shutil.copy2(runner._GetExePath(agent_dll), output_dir)
for path in EXECUTABLES:
InstrumentExecutable(
chrome_dir, output_dir, mode, args, agent_dll, path, False)
for path in EXECUTABLES_OPTIONAL:
InstrumentExecutable(
chrome_dir, output_dir, mode, args, agent_dll, path, True)
_USAGE = """\
%prog [options] [-- additional options for instrument.exe]
Copies the Chrome executables supplied in an input directory to an output
directory and instruments them at the destination. Leaves the instrumented
Chrome instance in the destination directory ready to use.
"""
def _ParseArguments():
parser = optparse.OptionParser(usage=_USAGE)
parser.add_option('--verbose', dest='verbose',
default=False, action='store_true',
help='Verbose logging.')
parser.add_option('--input-dir', dest='input_dir',
help=('The input directory where the original Chrome '
'executables are to be found.'))
parser.add_option('--output-dir', dest='output_dir',
help=('The directory where the optimized Chrome '
'installation will be created. From this location, '
'one can subsequently run benchmarks.'))
parser.add_option('--mode', choices=MODES, default=DEFAULT_MODE,
help='The instrumentation mode. Allowed values are '
' %s (default: %%default).' % ', '.join(MODES))
(opts, args) = parser.parse_args()
# Minimally configure logging.
if opts.verbose:
logging.basicConfig(level=logging.INFO)
else:
logging.basicConfig(level=logging.WARNING)
if not opts.input_dir or not opts.output_dir:
parser.error('You must provide input and output directories.')
opts.input_dir = os.path.abspath(opts.input_dir)
opts.output_dir = os.path.abspath(opts.output_dir)
return (opts, args)
def main():
"""Parses arguments and runs the instrumentation process."""
opts, args = _ParseArguments()
try:
InstrumentChrome(opts.input_dir, opts.output_dir, opts.mode, args)
except Exception:
_LOGGER.exception('Instrumentation failed.')
return 1
return 0
if __name__ == '__main__':
sys.exit(main())