buildbot_test_toolchains.py revision e84ea3d3e9fbe92d9854a85d67ab01fb488781c7
1#!/usr/bin/env python2 2"""Script for running nightly compiler tests on ChromeOS. 3 4This script launches a buildbot to build ChromeOS with the latest compiler on 5a particular board; then it finds and downloads the trybot image and the 6corresponding official image, and runs crosperf performance tests comparing 7the two. It then generates a report, emails it to the c-compiler-chrome, as 8well as copying the images into the seven-day reports directory. 9""" 10 11# Script to test different toolchains against ChromeOS benchmarks. 12 13from __future__ import print_function 14 15import argparse 16import datetime 17import os 18import re 19import sys 20import time 21 22from cros_utils import command_executer 23from cros_utils import logger 24 25from cros_utils import buildbot_utils 26 27# CL that updated GCC ebuilds to use 'next_gcc'. 28USE_NEXT_GCC_PATCH = '230260' 29 30# CL that uses LLVM to build the peppy image. 31USE_LLVM_PATCH = '295217' 32 33CROSTC_ROOT = '/usr/local/google/crostc' 34ROLE_ACCOUNT = 'mobiletc-prebuild' 35TOOLCHAIN_DIR = os.path.dirname(os.path.realpath(__file__)) 36MAIL_PROGRAM = '~/var/bin/mail-sheriff' 37PENDING_ARCHIVES_DIR = os.path.join(CROSTC_ROOT, 'pending_archives') 38NIGHTLY_TESTS_DIR = os.path.join(CROSTC_ROOT, 'nightly_test_reports') 39 40IMAGE_FS = (r'{board}-{image_type}/{chrome_version}-{tip}\.' + 41 r'{branch}\.{branch_branch}') 42TRYBOT_IMAGE_FS = 'trybot-' + IMAGE_FS + '-{build_id}' 43PFQ_IMAGE_FS = IMAGE_FS + '-rc1' 44IMAGE_RE_GROUPS = { 45 'board': r'(?P<board>\S+)', 46 'image_type': r'(?P<image_type>\S+)', 47 'chrome_version': r'(?P<chrome_version>R\d+)', 48 'tip': r'(?P<tip>\d+)', 49 'branch': r'(?P<branch>\d+)', 50 'branch_branch': r'(?P<branch_branch>\d+)', 51 'build_id': r'(?P<build_id>b\d+)' 52} 53TRYBOT_IMAGE_RE = TRYBOT_IMAGE_FS.format(**IMAGE_RE_GROUPS) 54 55 56class ToolchainComparator(object): 57 """Class for doing the nightly tests work.""" 58 59 def __init__(self, 60 board, 61 remotes, 62 chromeos_root, 63 weekday, 64 patches, 65 noschedv2=False): 66 self._board = board 67 self._remotes = remotes 68 self._chromeos_root = chromeos_root 69 self._base_dir = os.getcwd() 70 self._ce = command_executer.GetCommandExecuter() 71 self._l = logger.GetLogger() 72 self._build = '%s-release' % board 73 self._patches = patches.split(',') 74 self._patches_string = '_'.join(str(p) for p in self._patches) 75 self._noschedv2 = noschedv2 76 77 if not weekday: 78 self._weekday = time.strftime('%a') 79 else: 80 self._weekday = weekday 81 timestamp = datetime.datetime.strftime(datetime.datetime.now(), 82 '%Y-%m-%d_%H:%M:%S') 83 self._reports_dir = os.path.join( 84 NIGHTLY_TESTS_DIR, 85 '%s.%s' % (timestamp, board),) 86 87 def _GetVanillaImageName(self, trybot_image): 88 """Given a trybot artifact name, get corresponding vanilla image name. 89 90 Args: 91 trybot_image: artifact name such as 92 'trybot-daisy-release/R40-6394.0.0-b1389' 93 94 Returns: 95 Corresponding official image name, e.g. 'daisy-release/R40-6394.0.0'. 96 """ 97 mo = re.search(TRYBOT_IMAGE_RE, trybot_image) 98 assert mo 99 return IMAGE_FS.replace('\\', '').format(**mo.groupdict()) 100 101 def _GetNonAFDOImageName(self, trybot_image): 102 """Given a trybot artifact name, get corresponding non-AFDO image name. 103 104 We get the non-AFDO image from the PFQ builders. This image 105 is not generated for all the boards and, the closest PFQ image 106 was the one build for the previous ChromeOS version (the chrome 107 used in the current version is the one validated in the previous 108 version). 109 The previous ChromeOS does not always exist either. So, we try 110 a couple of versions before. 111 112 Args: 113 trybot_image: artifact name such as 114 'trybot-daisy-release/R40-6394.0.0-b1389' 115 116 Returns: 117 Corresponding chrome PFQ image name, e.g. 118 'daisy-chrome-pfq/R40-6393.0.0-rc1'. 119 """ 120 mo = re.search(TRYBOT_IMAGE_RE, trybot_image) 121 assert mo 122 image_dict = mo.groupdict() 123 image_dict['image_type'] = 'chrome-pfq' 124 for _ in xrange(2): 125 image_dict['tip'] = str(int(image_dict['tip']) - 1) 126 nonafdo_image = PFQ_IMAGE_FS.replace('\\', '').format(**image_dict) 127 if buildbot_utils.DoesImageExist(self._chromeos_root, nonafdo_image): 128 return nonafdo_image 129 return '' 130 131 def _FinishSetup(self): 132 """Make sure testing_rsa file is properly set up.""" 133 # Fix protections on ssh key 134 command = ('chmod 600 /var/cache/chromeos-cache/distfiles/target' 135 '/chrome-src-internal/src/third_party/chromite/ssh_keys' 136 '/testing_rsa') 137 ret_val = self._ce.ChrootRunCommand(self._chromeos_root, command) 138 if ret_val != 0: 139 raise RuntimeError('chmod for testing_rsa failed') 140 141 def _TestImages(self, trybot_image, vanilla_image, nonafdo_image): 142 """Create crosperf experiment file. 143 144 Given the names of the trybot, vanilla and non-AFDO images, create the 145 appropriate crosperf experiment file and launch crosperf on it. 146 """ 147 experiment_file_dir = os.path.join(self._chromeos_root, '..', self._weekday) 148 experiment_file_name = '%s_toolchain_experiment.txt' % self._board 149 150 compiler_string = 'gcc' 151 if USE_LLVM_PATCH in self._patches_string: 152 experiment_file_name = '%s_llvm_experiment.txt' % self._board 153 compiler_string = 'llvm' 154 155 experiment_file = os.path.join(experiment_file_dir, experiment_file_name) 156 experiment_header = """ 157 board: %s 158 remote: %s 159 retries: 1 160 """ % (self._board, self._remotes) 161 experiment_tests = """ 162 benchmark: all_toolchain_perf { 163 suite: telemetry_Crosperf 164 iterations: 3 165 } 166 167 benchmark: page_cycler_v2.typical_25 { 168 suite: telemetry_Crosperf 169 iterations: 2 170 run_local: False 171 retries: 0 172 } 173 """ 174 175 with open(experiment_file, 'w') as f: 176 f.write(experiment_header) 177 f.write(experiment_tests) 178 179 # Now add vanilla to test file. 180 official_image = """ 181 vanilla_image { 182 chromeos_root: %s 183 build: %s 184 compiler: gcc 185 } 186 """ % (self._chromeos_root, vanilla_image) 187 f.write(official_image) 188 189 # Now add non-AFDO image to test file. 190 if nonafdo_image: 191 official_nonafdo_image = """ 192 nonafdo_image { 193 chromeos_root: %s 194 build: %s 195 compiler: gcc 196 } 197 """ % (self._chromeos_root, nonafdo_image) 198 f.write(official_nonafdo_image) 199 200 label_string = '%s_trybot_image' % compiler_string 201 if USE_NEXT_GCC_PATCH in self._patches: 202 label_string = 'gcc_next_trybot_image' 203 204 experiment_image = """ 205 %s { 206 chromeos_root: %s 207 build: %s 208 compiler: %s 209 } 210 """ % (label_string, self._chromeos_root, trybot_image, 211 compiler_string) 212 f.write(experiment_image) 213 214 crosperf = os.path.join(TOOLCHAIN_DIR, 'crosperf', 'crosperf') 215 noschedv2_opts = '--noschedv2' if self._noschedv2 else '' 216 command = ('{crosperf} --no_email=True --results_dir={r_dir} ' 217 '--json_report=True {noschedv2_opts} {exp_file}').format( 218 crosperf=crosperf, 219 r_dir=self._reports_dir, 220 noschedv2_opts=noschedv2_opts, 221 exp_file=experiment_file) 222 223 ret = self._ce.RunCommand(command) 224 if ret != 0: 225 raise RuntimeError('Crosperf execution error!') 226 else: 227 # Copy json report to pending archives directory. 228 command = 'cp %s/*.json %s/.' % (self._reports_dir, PENDING_ARCHIVES_DIR) 229 ret = self._ce.RunCommand(command) 230 return 231 232 def _SendEmail(self): 233 """Find email message generated by crosperf and send it.""" 234 filename = os.path.join(self._reports_dir, 'msg_body.html') 235 if (os.path.exists(filename) and 236 os.path.exists(os.path.expanduser(MAIL_PROGRAM))): 237 email_title = 'buildbot test results' 238 if self._patches_string == USE_LLVM_PATCH: 239 email_title = 'buildbot llvm test results' 240 command = ('cat %s | %s -s "%s, %s" -team -html' % 241 (filename, MAIL_PROGRAM, email_title, self._board)) 242 self._ce.RunCommand(command) 243 244 def DoAll(self): 245 """Main function inside ToolchainComparator class. 246 247 Launch trybot, get image names, create crosperf experiment file, run 248 crosperf, and copy images into seven-day report directories. 249 """ 250 date_str = datetime.date.today() 251 description = 'master_%s_%s_%s' % (self._patches_string, self._build, 252 date_str) 253 build_id, trybot_image = buildbot_utils.GetTrybotImage( 254 self._chromeos_root, 255 self._build, 256 self._patches, 257 description, 258 other_flags=['--notests'], 259 build_toolchain=True) 260 261 print('trybot_url: \ 262 https://uberchromegw.corp.google.com/i/chromiumos.tryserver/builders/release/builds/%s' \ 263 % build_id) 264 if len(trybot_image) == 0: 265 self._l.LogError('Unable to find trybot_image for %s!' % description) 266 return 1 267 268 vanilla_image = self._GetVanillaImageName(trybot_image) 269 nonafdo_image = self._GetNonAFDOImageName(trybot_image) 270 271 # The trybot image is ready here, in some cases, the vanilla image 272 # is not ready, so we need to make sure vanilla image is available. 273 buildbot_utils.WaitForImage(self._chromeos_root, vanilla_image) 274 print('trybot_image: %s' % trybot_image) 275 print('vanilla_image: %s' % vanilla_image) 276 print('nonafdo_image: %s' % nonafdo_image) 277 278 if os.getlogin() == ROLE_ACCOUNT: 279 self._FinishSetup() 280 281 self._TestImages(trybot_image, vanilla_image, nonafdo_image) 282 self._SendEmail() 283 return 0 284 285 286def Main(argv): 287 """The main function.""" 288 289 # Common initializations 290 command_executer.InitCommandExecuter() 291 parser = argparse.ArgumentParser() 292 parser.add_argument( 293 '--remote', dest='remote', help='Remote machines to run tests on.') 294 parser.add_argument( 295 '--board', dest='board', default='x86-zgb', help='The target board.') 296 parser.add_argument( 297 '--chromeos_root', 298 dest='chromeos_root', 299 help='The chromeos root from which to run tests.') 300 parser.add_argument( 301 '--weekday', 302 default='', 303 dest='weekday', 304 help='The day of the week for which to run tests.') 305 parser.add_argument( 306 '--patch', 307 dest='patches', 308 help='The patches to use for the testing, ' 309 "seprate the patch numbers with ',' " 310 'for more than one patches.') 311 parser.add_argument( 312 '--noschedv2', 313 dest='noschedv2', 314 action='store_true', 315 default=False, 316 help='Pass --noschedv2 to crosperf.') 317 318 options = parser.parse_args(argv[1:]) 319 if not options.board: 320 print('Please give a board.') 321 return 1 322 if not options.remote: 323 print('Please give at least one remote machine.') 324 return 1 325 if not options.chromeos_root: 326 print('Please specify the ChromeOS root directory.') 327 return 1 328 if options.patches: 329 patches = options.patches 330 else: 331 patches = USE_NEXT_GCC_PATCH 332 333 fc = ToolchainComparator(options.board, options.remote, options.chromeos_root, 334 options.weekday, patches, options.noschedv2) 335 return fc.DoAll() 336 337 338if __name__ == '__main__': 339 retval = Main(sys.argv) 340 sys.exit(retval) 341