Home | History | Annotate | Download | only in fdo_scripts
      1 # Copyright 2011 Google Inc. All Rights Reserved.
      2 """Script to build chrome with FDO and compare performance against no FDO."""
      3 
      4 import getpass
      5 import optparse
      6 import os
      7 import sys
      8 
      9 import image_chromeos
     10 import setup_chromeos
     11 from cros_utils import command_executer
     12 from cros_utils import misc
     13 from cros_utils import logger
     14 
     15 
     16 class Patcher(object):
     17 
     18   def __init__(self, dir_to_patch, patch_file):
     19     self._dir_to_patch = dir_to_patch
     20     self._patch_file = patch_file
     21     self._base_patch_command = 'patch -p0 %%s < %s' % patch_file
     22     self._ce = command_executer.GetCommandExecuter()
     23 
     24   def _RunPatchCommand(self, args):
     25     patch_command = self._base_patch_command % args
     26     command = ('cd %s && %s' % (self._dir_to_patch, patch_command))
     27     return self._ce.RunCommand(command)
     28 
     29   def _ApplyPatch(self, args):
     30     full_args = '%s --dry-run' % args
     31     ret = self._RunPatchCommand(full_args)
     32     if ret:
     33       raise RuntimeError('Patch dry run failed!')
     34     ret = self._RunPatchCommand(args)
     35     if ret:
     36       raise RuntimeError('Patch application failed!')
     37 
     38   def __enter__(self):
     39     self._ApplyPatch('')
     40 
     41   def __exit__(self, type, value, traceback):
     42     self._ApplyPatch('-R')
     43 
     44 
     45 class FDOComparator(object):
     46 
     47   def __init__(self, board, remotes, ebuild_version, plus_pgo, minus_pgo,
     48                update_pgo, chromeos_root):
     49     self._board = board
     50     self._remotes = remotes
     51     self._ebuild_version = ebuild_version
     52     self._remote = remotes.split(',')[0]
     53     self._chromeos_root = chromeos_root
     54     self._profile_dir = 'profile_dir'
     55     self._profile_path = os.path.join(self._chromeos_root, 'src', 'scripts',
     56                                       os.path.basename(self._profile_dir))
     57     self._plus_pgo = plus_pgo
     58     self._minus_pgo = minus_pgo
     59     self._update_pgo = update_pgo
     60 
     61     self._ce = command_executer.GetCommandExecuter()
     62     self._l = logger.GetLogger()
     63 
     64   def _CheckoutChromeOS(self):
     65     if not os.path.exists(self._chromeos_root):
     66       setup_chromeos_args = [setup_chromeos.__file__,
     67                              '--dir=%s' % self._chromeos_root, '--minilayout']
     68       setup_chromeos.Main(setup_chromeos_args)
     69 
     70   def _BuildChromeOSUsingBinaries(self):
     71     image_dir = misc.GetImageDir(self._chromeos_root, self._board)
     72     command = 'equery-%s l chromeos' % self._board
     73     ret = self._ce.ChrootRunCommand(self._chromeos_root, command)
     74     if ret:
     75       command = misc.GetSetupBoardCommand(self._board, usepkg=True)
     76       ret = self._ce.ChrootRunCommand(self._chromeos_root, command)
     77       if ret:
     78         raise RuntimeError("Couldn't run setup_board!")
     79       command = misc.GetBuildPackagesCommand(self._board, True)
     80       ret = self._ce.ChrootRunCommand(self._chromeos_root, command)
     81       if ret:
     82         raise RuntimeError("Couldn't run build_packages!")
     83 
     84   def _ReportMismatches(self, build_log):
     85     mismatch_signature = '-Wcoverage-mismatch'
     86     mismatches = build_log.count(mismatch_signature)
     87     self._l.LogOutput('Total mismatches: %s' % mismatches)
     88     stale_files = set([])
     89     for line in build_log.splitlines():
     90       if mismatch_signature in line:
     91         filename = line.split(':')[0]
     92         stale_files.add(filename)
     93     self._l.LogOutput('Total stale files: %s' % len(stale_files))
     94 
     95   def _BuildChromeAndImage(self,
     96                            ebuild_version='',
     97                            env_dict={},
     98                            cflags='',
     99                            cxxflags='',
    100                            ldflags='',
    101                            label='',
    102                            build_image_args=''):
    103     env_string = misc.GetEnvStringFromDict(env_dict)
    104     if not label:
    105       label = ' '.join([env_string, cflags, cxxflags, ldflags, ebuild_version])
    106       label = label.strip()
    107       label = misc.GetFilenameFromString(label)
    108     if not misc.DoesLabelExist(self._chromeos_root, self._board, label):
    109       build_chrome_browser_args = ['--clean', '--chromeos_root=%s' %
    110                                    self._chromeos_root, '--board=%s' %
    111                                    self._board, '--env=%r' % env_string,
    112                                    '--cflags=%r' % cflags, '--cxxflags=%r' %
    113                                    cxxflags, '--ldflags=%r' % ldflags,
    114                                    '--ebuild_version=%s' % ebuild_version,
    115                                    '--build_image_args=%s' % build_image_args]
    116 
    117       build_chrome_browser = os.path.join(
    118           os.path.dirname(__file__), '..', 'build_chrome_browser.py')
    119       command = 'python %s %s' % (build_chrome_browser,
    120                                   ' '.join(build_chrome_browser_args))
    121       ret, out, err = self._ce.RunCommandWOutput(command)
    122       if '-fprofile-use' in cxxflags:
    123         self._ReportMismatches(out)
    124 
    125       if ret:
    126         raise RuntimeError("Couldn't build chrome browser!")
    127       misc.LabelLatestImage(self._chromeos_root, self._board, label)
    128     return label
    129 
    130   def _TestLabels(self, labels):
    131     experiment_file = 'pgo_experiment.txt'
    132     experiment_header = """
    133     board: %s
    134     remote: %s
    135     """ % (self._board, self._remotes)
    136     experiment_tests = """
    137     benchmark: desktopui_PyAutoPerfTests {
    138       iterations: 1
    139     }
    140     """
    141 
    142     with open(experiment_file, 'w') as f:
    143       print >> f, experiment_header
    144       print >> f, experiment_tests
    145       for label in labels:
    146         # TODO(asharif): Fix crosperf so it accepts labels with symbols
    147         crosperf_label = label
    148         crosperf_label = crosperf_label.replace('-', 'minus')
    149         crosperf_label = crosperf_label.replace('+', 'plus')
    150         experiment_image = """
    151         %s {
    152           chromeos_image: %s
    153         }
    154         """ % (crosperf_label, os.path.join(
    155             misc.GetImageDir(self._chromeos_root, self._board), label,
    156             'chromiumos_test_image.bin'))
    157         print >> f, experiment_image
    158     crosperf = os.path.join(
    159         os.path.dirname(__file__), '..', 'crosperf', 'crosperf')
    160     command = '%s %s' % (crosperf, experiment_file)
    161     ret = self._ce.RunCommand(command)
    162     if ret:
    163       raise RuntimeError("Couldn't run crosperf!")
    164 
    165   def _ImageRemote(self, label):
    166     image_path = os.path.join(
    167         misc.GetImageDir(self._chromeos_root,
    168                          self._board), label, 'chromiumos_test_image.bin')
    169     image_chromeos_args = [image_chromeos.__file__, '--chromeos_root=%s' %
    170                            self._chromeos_root, '--image=%s' % image_path,
    171                            '--remote=%s' % self._remote,
    172                            '--board=%s' % self._board]
    173     image_chromeos.Main(image_chromeos_args)
    174 
    175   def _ProfileRemote(self):
    176     profile_cycler = os.path.join(
    177         os.path.dirname(__file__), 'profile_cycler.py')
    178     profile_cycler_args = ['--chromeos_root=%s' % self._chromeos_root,
    179                            '--cycler=all', '--board=%s' % self._board,
    180                            '--profile_dir=%s' % self._profile_path,
    181                            '--remote=%s' % self._remote]
    182     command = 'python %s %s' % (profile_cycler, ' '.join(profile_cycler_args))
    183     ret = self._ce.RunCommand(command)
    184     if ret:
    185       raise RuntimeError("Couldn't profile cycler!")
    186 
    187   def _BuildGenerateImage(self):
    188     # TODO(asharif): add cflags as well.
    189     labels_list = ['fprofile-generate', self._ebuild_version]
    190     label = '_'.join(labels_list)
    191     generate_label = self._BuildChromeAndImage(
    192         env_dict={'USE': 'chrome_internal -pgo pgo_generate'},
    193         label=label,
    194         ebuild_version=self._ebuild_version,
    195         build_image_args='--rootfs_boost_size=400')
    196     return generate_label
    197 
    198   def _BuildUseImage(self):
    199     ctarget = misc.GetCtargetFromBoard(self._board, self._chromeos_root)
    200     chroot_profile_dir = os.path.join('/home/%s/trunk' % getpass.getuser(),
    201                                       'src', 'scripts', self._profile_dir,
    202                                       ctarget)
    203     cflags = ('-fprofile-use '
    204               '-fprofile-correction '
    205               '-Wno-error '
    206               '-fdump-tree-optimized-blocks-lineno '
    207               '-fdump-ipa-profile-blocks-lineno '
    208               '-fno-vpt '
    209               '-fprofile-dir=%s' % chroot_profile_dir)
    210     labels_list = ['updated_pgo', self._ebuild_version]
    211     label = '_'.join(labels_list)
    212     pgo_use_label = self._BuildChromeAndImage(
    213         env_dict={'USE': 'chrome_internal -pgo'},
    214         cflags=cflags,
    215         cxxflags=cflags,
    216         ldflags=cflags,
    217         label=label,
    218         ebuild_version=self._ebuild_version)
    219     return pgo_use_label
    220 
    221   def DoAll(self):
    222     self._CheckoutChromeOS()
    223     self._BuildChromeOSUsingBinaries()
    224     labels = []
    225 
    226     if self._minus_pgo:
    227       minus_pgo = self._BuildChromeAndImage(
    228           env_dict={'USE': 'chrome_internal -pgo'},
    229           ebuild_version=self._ebuild_version)
    230       labels.append(minus_pgo)
    231     if self._plus_pgo:
    232       plus_pgo = self._BuildChromeAndImage(
    233           env_dict={'USE': 'chrome_internal pgo'},
    234           ebuild_version=self._ebuild_version)
    235       labels.append(plus_pgo)
    236 
    237     if self._update_pgo:
    238       if not os.path.exists(self._profile_path):
    239         # Build Chrome with -fprofile-generate
    240         generate_label = self._BuildGenerateImage()
    241         # Image to the remote box.
    242         self._ImageRemote(generate_label)
    243         # Profile it using all page cyclers.
    244         self._ProfileRemote()
    245 
    246       # Use the profile directory to rebuild it.
    247       updated_pgo_label = self._BuildUseImage()
    248       labels.append(updated_pgo_label)
    249 
    250     # Run crosperf on all images now.
    251     self._TestLabels(labels)
    252     return 0
    253 
    254 
    255 def Main(argv):
    256   """The main function."""
    257   # Common initializations
    258   ###  command_executer.InitCommandExecuter(True)
    259   command_executer.InitCommandExecuter()
    260   parser = optparse.OptionParser()
    261   parser.add_option('--remote',
    262                     dest='remote',
    263                     help='Remote machines to run tests on.')
    264   parser.add_option('--board',
    265                     dest='board',
    266                     default='x86-zgb',
    267                     help='The target board.')
    268   parser.add_option('--ebuild_version',
    269                     dest='ebuild_version',
    270                     default='',
    271                     help='The Chrome ebuild version to use.')
    272   parser.add_option('--plus_pgo',
    273                     dest='plus_pgo',
    274                     action='store_true',
    275                     default=False,
    276                     help='Build USE=+pgo.')
    277   parser.add_option('--minus_pgo',
    278                     dest='minus_pgo',
    279                     action='store_true',
    280                     default=False,
    281                     help='Build USE=-pgo.')
    282   parser.add_option('--update_pgo',
    283                     dest='update_pgo',
    284                     action='store_true',
    285                     default=False,
    286                     help='Update pgo and build Chrome with the update.')
    287   parser.add_option('--chromeos_root',
    288                     dest='chromeos_root',
    289                     default=False,
    290                     help='The chromeos root directory')
    291   options, _ = parser.parse_args(argv)
    292   if not options.board:
    293     print 'Please give a board.'
    294     return 1
    295   if not options.remote:
    296     print 'Please give at least one remote machine.'
    297     return 1
    298   if not options.chromeos_root:
    299     print 'Please provide the chromeos root directory.'
    300     return 1
    301   if not any((options.minus_pgo, options.plus_pgo, options.update_pgo)):
    302     print 'Please provide at least one build option.'
    303     return 1
    304   fc = FDOComparator(options.board, options.remote, options.ebuild_version,
    305                      options.plus_pgo, options.minus_pgo, options.update_pgo,
    306                      os.path.expanduser(options.chromeos_root))
    307   return fc.DoAll()
    308 
    309 
    310 if __name__ == '__main__':
    311   retval = Main(sys.argv)
    312   sys.exit(retval)
    313