You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
312 lines
12 KiB
312 lines
12 KiB
# 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)
|
|
|