# Copyright 2011 Google Inc. All Rights Reserved.
"""Script to build chrome with FDO and compare performance against no FDO."""
import getpass
import optparse
import os
import sys
import image_chromeos
import setup_chromeos
from cros_utils import command_executer
from cros_utils import misc
from cros_utils import logger
class Patcher(object):
def __init__(self, dir_to_patch, patch_file):
self._dir_to_patch = dir_to_patch
self._patch_file = patch_file
self._base_patch_command = 'patch -p0 %%s < %s' % patch_file
self._ce = command_executer.GetCommandExecuter()
def _RunPatchCommand(self, args):
patch_command = self._base_patch_command % args
command = ('cd %s && %s' % (self._dir_to_patch, patch_command))
return self._ce.RunCommand(command)
def _ApplyPatch(self, args):
full_args = '%s --dry-run' % args
ret = self._RunPatchCommand(full_args)
if ret:
raise RuntimeError('Patch dry run failed!')
ret = self._RunPatchCommand(args)
if ret:
raise RuntimeError('Patch application failed!')
def __enter__(self):
self._ApplyPatch('')
def __exit__(self, type, value, traceback):
self._ApplyPatch('-R')
class FDOComparator(object):
def __init__(self, board, remotes, ebuild_version, plus_pgo, minus_pgo,
update_pgo, chromeos_root):
self._board = board
self._remotes = remotes
self._ebuild_version = ebuild_version
self._remote = remotes.split(',')[0]
self._chromeos_root = chromeos_root
self._profile_dir = 'profile_dir'
self._profile_path = os.path.join(self._chromeos_root, 'src', 'scripts',
os.path.basename(self._profile_dir))
self._plus_pgo = plus_pgo
self._minus_pgo = minus_pgo
self._update_pgo = update_pgo
self._ce = command_executer.GetCommandExecuter()
self._l = logger.GetLogger()
def _CheckoutChromeOS(self):
if not os.path.exists(self._chromeos_root):
setup_chromeos_args = [setup_chromeos.__file__,
'--dir=%s' % self._chromeos_root, '--minilayout']
setup_chromeos.Main(setup_chromeos_args)
def _BuildChromeOSUsingBinaries(self):
image_dir = misc.GetImageDir(self._chromeos_root, self._board)
command = 'equery-%s l chromeos' % self._board
ret = self._ce.ChrootRunCommand(self._chromeos_root, command)
if ret:
command = misc.GetSetupBoardCommand(self._board, usepkg=True)
ret = self._ce.ChrootRunCommand(self._chromeos_root, command)
if ret:
raise RuntimeError("Couldn't run setup_board!")
command = misc.GetBuildPackagesCommand(self._board, True)
ret = self._ce.ChrootRunCommand(self._chromeos_root, command)
if ret:
raise RuntimeError("Couldn't run build_packages!")
def _ReportMismatches(self, build_log):
mismatch_signature = '-Wcoverage-mismatch'
mismatches = build_log.count(mismatch_signature)
self._l.LogOutput('Total mismatches: %s' % mismatches)
stale_files = set([])
for line in build_log.splitlines():
if mismatch_signature in line:
filename = line.split(':')[0]
stale_files.add(filename)
self._l.LogOutput('Total stale files: %s' % len(stale_files))
def _BuildChromeAndImage(self,
ebuild_version='',
env_dict={},
cflags='',
cxxflags='',
ldflags='',
label='',
build_image_args=''):
env_string = misc.GetEnvStringFromDict(env_dict)
if not label:
label = ' '.join([env_string, cflags, cxxflags, ldflags, ebuild_version])
label = label.strip()
label = misc.GetFilenameFromString(label)
if not misc.DoesLabelExist(self._chromeos_root, self._board, label):
build_chrome_browser_args = ['--clean', '--chromeos_root=%s' %
self._chromeos_root, '--board=%s' %
self._board, '--env=%r' % env_string,
'--cflags=%r' % cflags, '--cxxflags=%r' %
cxxflags, '--ldflags=%r' % ldflags,
'--ebuild_version=%s' % ebuild_version,
'--build_image_args=%s' % build_image_args]
build_chrome_browser = os.path.join(
os.path.dirname(__file__), '..', 'build_chrome_browser.py')
command = 'python %s %s' % (build_chrome_browser,
' '.join(build_chrome_browser_args))
ret, out, err = self._ce.RunCommandWOutput(command)
if '-fprofile-use' in cxxflags:
self._ReportMismatches(out)
if ret:
raise RuntimeError("Couldn't build chrome browser!")
misc.LabelLatestImage(self._chromeos_root, self._board, label)
return label
def _TestLabels(self, labels):
experiment_file = 'pgo_experiment.txt'
experiment_header = """
board: %s
remote: %s
""" % (self._board, self._remotes)
experiment_tests = """
benchmark: desktopui_PyAutoPerfTests {
iterations: 1
}
"""
with open(experiment_file, 'w') as f:
print >> f, experiment_header
print >> f, experiment_tests
for label in labels:
# TODO(asharif): Fix crosperf so it accepts labels with symbols
crosperf_label = label
crosperf_label = crosperf_label.replace('-', 'minus')
crosperf_label = crosperf_label.replace('+', 'plus')
experiment_image = """
%s {
chromeos_image: %s
}
""" % (crosperf_label, os.path.join(
misc.GetImageDir(self._chromeos_root, self._board), label,
'chromiumos_test_image.bin'))
print >> f, experiment_image
crosperf = os.path.join(
os.path.dirname(__file__), '..', 'crosperf', 'crosperf')
command = '%s %s' % (crosperf, experiment_file)
ret = self._ce.RunCommand(command)
if ret:
raise RuntimeError("Couldn't run crosperf!")
def _ImageRemote(self, label):
image_path = os.path.join(
misc.GetImageDir(self._chromeos_root,
self._board), label, 'chromiumos_test_image.bin')
image_chromeos_args = [image_chromeos.__file__, '--chromeos_root=%s' %
self._chromeos_root, '--image=%s' % image_path,
'--remote=%s' % self._remote,
'--board=%s' % self._board]
image_chromeos.Main(image_chromeos_args)
def _ProfileRemote(self):
profile_cycler = os.path.join(
os.path.dirname(__file__), 'profile_cycler.py')
profile_cycler_args = ['--chromeos_root=%s' % self._chromeos_root,
'--cycler=all', '--board=%s' % self._board,
'--profile_dir=%s' % self._profile_path,
'--remote=%s' % self._remote]
command = 'python %s %s' % (profile_cycler, ' '.join(profile_cycler_args))
ret = self._ce.RunCommand(command)
if ret:
raise RuntimeError("Couldn't profile cycler!")
def _BuildGenerateImage(self):
# TODO(asharif): add cflags as well.
labels_list = ['fprofile-generate', self._ebuild_version]
label = '_'.join(labels_list)
generate_label = self._BuildChromeAndImage(
env_dict={'USE': 'chrome_internal -pgo pgo_generate'},
label=label,
ebuild_version=self._ebuild_version,
build_image_args='--rootfs_boost_size=400')
return generate_label
def _BuildUseImage(self):
ctarget = misc.GetCtargetFromBoard(self._board, self._chromeos_root)
chroot_profile_dir = os.path.join('/home/%s/trunk' % getpass.getuser(),
'src', 'scripts', self._profile_dir,
ctarget)
cflags = ('-fprofile-use '
'-fprofile-correction '
'-Wno-error '
'-fdump-tree-optimized-blocks-lineno '
'-fdump-ipa-profile-blocks-lineno '
'-fno-vpt '
'-fprofile-dir=%s' % chroot_profile_dir)
labels_list = ['updated_pgo', self._ebuild_version]
label = '_'.join(labels_list)
pgo_use_label = self._BuildChromeAndImage(
env_dict={'USE': 'chrome_internal -pgo'},
cflags=cflags,
cxxflags=cflags,
ldflags=cflags,
label=label,
ebuild_version=self._ebuild_version)
return pgo_use_label
def DoAll(self):
self._CheckoutChromeOS()
self._BuildChromeOSUsingBinaries()
labels = []
if self._minus_pgo:
minus_pgo = self._BuildChromeAndImage(
env_dict={'USE': 'chrome_internal -pgo'},
ebuild_version=self._ebuild_version)
labels.append(minus_pgo)
if self._plus_pgo:
plus_pgo = self._BuildChromeAndImage(
env_dict={'USE': 'chrome_internal pgo'},
ebuild_version=self._ebuild_version)
labels.append(plus_pgo)
if self._update_pgo:
if not os.path.exists(self._profile_path):
# Build Chrome with -fprofile-generate
generate_label = self._BuildGenerateImage()
# Image to the remote box.
self._ImageRemote(generate_label)
# Profile it using all page cyclers.
self._ProfileRemote()
# Use the profile directory to rebuild it.
updated_pgo_label = self._BuildUseImage()
labels.append(updated_pgo_label)
# Run crosperf on all images now.
self._TestLabels(labels)
return 0
def Main(argv):
"""The main function."""
# Common initializations
### command_executer.InitCommandExecuter(True)
command_executer.InitCommandExecuter()
parser = optparse.OptionParser()
parser.add_option('--remote',
dest='remote',
help='Remote machines to run tests on.')
parser.add_option('--board',
dest='board',
default='x86-zgb',
help='The target board.')
parser.add_option('--ebuild_version',
dest='ebuild_version',
default='',
help='The Chrome ebuild version to use.')
parser.add_option('--plus_pgo',
dest='plus_pgo',
action='store_true',
default=False,
help='Build USE=+pgo.')
parser.add_option('--minus_pgo',
dest='minus_pgo',
action='store_true',
default=False,
help='Build USE=-pgo.')
parser.add_option('--update_pgo',
dest='update_pgo',
action='store_true',
default=False,
help='Update pgo and build Chrome with the update.')
parser.add_option('--chromeos_root',
dest='chromeos_root',
default=False,
help='The chromeos root directory')
options, _ = parser.parse_args(argv)
if not options.board:
print 'Please give a board.'
return 1
if not options.remote:
print 'Please give at least one remote machine.'
return 1
if not options.chromeos_root:
print 'Please provide the chromeos root directory.'
return 1
if not any((options.minus_pgo, options.plus_pgo, options.update_pgo)):
print 'Please provide at least one build option.'
return 1
fc = FDOComparator(options.board, options.remote, options.ebuild_version,
options.plus_pgo, options.minus_pgo, options.update_pgo,
os.path.expanduser(options.chromeos_root))
return fc.DoAll()
if __name__ == '__main__':
retval = Main(sys.argv)
sys.exit(retval)