| # 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) |