普通文本  |  313行  |  11.45 KB

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