buildbot_test_toolchains.py revision f2a3ef46f75d2196a93d3ed27f4d1fcf22b54fbe
1#!/usr/bin/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 datetime 16import optparse 17import os 18import sys 19import time 20 21from utils import command_executer 22from utils import logger 23 24from utils import buildbot_utils 25 26# CL that updated GCC ebuilds to use 'next_gcc'. 27USE_NEXT_GCC_PATCH = '230260' 28 29# CL that uses LLVM to build the peppy image. 30USE_LLVM_PATCH = '295217' 31 32# The boards on which we run weekly reports 33WEEKLY_REPORT_BOARDS = ['lumpy'] 34 35CROSTC_ROOT = '/usr/local/google/crostc' 36ROLE_ACCOUNT = 'mobiletc-prebuild' 37TOOLCHAIN_DIR = os.path.dirname(os.path.realpath(__file__)) 38MAIL_PROGRAM = '~/var/bin/mail-sheriff' 39WEEKLY_REPORTS_ROOT = os.path.join(CROSTC_ROOT, 'weekly_test_data') 40PENDING_ARCHIVES_DIR = os.path.join(CROSTC_ROOT, 'pending_archives') 41NIGHTLY_TESTS_DIR = os.path.join(CROSTC_ROOT, 'nightly_test_reports') 42 43 44class ToolchainComparator(object): 45 """Class for doing the nightly tests work.""" 46 47 def __init__(self, 48 board, 49 remotes, 50 chromeos_root, 51 weekday, 52 patches, 53 noschedv2=False): 54 self._board = board 55 self._remotes = remotes 56 self._chromeos_root = chromeos_root 57 self._base_dir = os.getcwd() 58 self._ce = command_executer.GetCommandExecuter() 59 self._l = logger.GetLogger() 60 self._build = '%s-release' % board 61 self._patches = patches.split(',') 62 self._patches_string = '_'.join(str(p) for p in self._patches) 63 self._noschedv2 = noschedv2 64 65 if not weekday: 66 self._weekday = time.strftime('%a') 67 else: 68 self._weekday = weekday 69 timestamp = datetime.datetime.strftime(datetime.datetime.now(), 70 '%Y-%m-%d_%H:%M:%S') 71 self._reports_dir = os.path.join(NIGHTLY_TESTS_DIR, 72 '%s.%s' % (timestamp, board),) 73 74 def _ParseVanillaImage(self, trybot_image): 75 """Parse a trybot artifact name to get corresponding vanilla image. 76 77 This function takes an artifact name, such as 78 'trybot-daisy-release/R40-6394.0.0-b1389', and returns the 79 corresponding official build name, e.g. 'daisy-release/R40-6394.0.0'. 80 """ 81 start_pos = trybot_image.find(self._build) 82 end_pos = trybot_image.rfind('-b') 83 vanilla_image = trybot_image[start_pos:end_pos] 84 return vanilla_image 85 86 def _FinishSetup(self): 87 """Make sure testing_rsa file is properly set up.""" 88 # Fix protections on ssh key 89 command = ('chmod 600 /var/cache/chromeos-cache/distfiles/target' 90 '/chrome-src-internal/src/third_party/chromite/ssh_keys' 91 '/testing_rsa') 92 ret_val = self._ce.ChrootRunCommand(self._chromeos_root, command) 93 if ret_val != 0: 94 raise RuntimeError('chmod for testing_rsa failed') 95 96 def _TestImages(self, trybot_image, vanilla_image): 97 """Create crosperf experiment file. 98 99 Given the names of the trybot and vanilla images, create the 100 appropriate crosperf experiment file and launch crosperf on it. 101 """ 102 experiment_file_dir = os.path.join(self._chromeos_root, '..', self._weekday) 103 experiment_file_name = '%s_toolchain_experiment.txt' % self._board 104 105 compiler_string = 'gcc' 106 if USE_LLVM_PATCH in self._patches_string: 107 experiment_file_name = '%s_llvm_experiment.txt' % self._board 108 compiler_string = 'llvm' 109 110 experiment_file = os.path.join(experiment_file_dir, experiment_file_name) 111 experiment_header = """ 112 board: %s 113 remote: %s 114 retries: 1 115 """ % (self._board, self._remotes) 116 experiment_tests = """ 117 benchmark: all_toolchain_perf { 118 suite: telemetry_Crosperf 119 iterations: 3 120 } 121 """ 122 123 with open(experiment_file, 'w') as f: 124 f.write(experiment_header) 125 f.write(experiment_tests) 126 127 # Now add vanilla to test file. 128 official_image = """ 129 vanilla_image { 130 chromeos_root: %s 131 build: %s 132 compiler: gcc 133 } 134 """ % (self._chromeos_root, vanilla_image) 135 f.write(official_image) 136 137 label_string = '%s_trybot_image' % compiler_string 138 if USE_NEXT_GCC_PATCH in self._patches: 139 label_string = 'gcc_next_trybot_image' 140 141 experiment_image = """ 142 %s { 143 chromeos_root: %s 144 build: %s 145 compiler: %s 146 } 147 """ % (label_string, self._chromeos_root, trybot_image, 148 compiler_string) 149 f.write(experiment_image) 150 151 crosperf = os.path.join(TOOLCHAIN_DIR, 'crosperf', 'crosperf') 152 noschedv2_opts = '--noschedv2' if self._noschedv2 else '' 153 command = ('{crosperf} --no_email=True --results_dir={r_dir} ' 154 '--json_report=True {noschedv2_opts} {exp_file}').format( 155 crosperf=crosperf, 156 r_dir=self._reports_dir, 157 noschedv2_opts=noschedv2_opts, 158 exp_file=experiment_file) 159 160 ret = self._ce.RunCommand(command) 161 if ret != 0: 162 raise RuntimeError("Couldn't run crosperf!") 163 else: 164 # Copy json report to pending archives directory. 165 command = 'cp %s/*.json %s/.' % (self._reports_dir, PENDING_ARCHIVES_DIR) 166 ret = self._ce.RunCommand(command) 167 return 168 169 def _CopyWeeklyReportFiles(self, trybot_image, vanilla_image): 170 """Put files in place for running seven-day reports. 171 172 Create tar files of the custom and official images and copy them 173 to the weekly reports directory, so they exist when the weekly report 174 gets generated. IMPORTANT NOTE: This function must run *after* 175 crosperf has been run; otherwise the vanilla images will not be there. 176 """ 177 178 dry_run = False 179 if os.getlogin() != ROLE_ACCOUNT: 180 self._l.LogOutput('Running this from non-role account; not copying ' 181 'tar files for weekly reports.') 182 dry_run = True 183 184 images_path = os.path.join( 185 os.path.realpath(self._chromeos_root), 'chroot/tmp') 186 187 data_dir = os.path.join(WEEKLY_REPORTS_ROOT, self._board) 188 dest_dir = os.path.join(data_dir, self._weekday) 189 if not os.path.exists(dest_dir): 190 os.makedirs(dest_dir) 191 192 # Make sure dest_dir is empty (clean out last week's data). 193 cmd = 'cd %s; rm -Rf %s_*_image*' % (dest_dir, self._weekday) 194 if dry_run: 195 print('CMD: %s' % cmd) 196 else: 197 self._ce.RunCommand(cmd) 198 199 # Now create new tar files and copy them over. 200 labels = ['test', 'vanilla'] 201 for label_name in labels: 202 if label_name == 'test': 203 test_path = trybot_image 204 else: 205 test_path = vanilla_image 206 tar_file_name = '%s_%s_image.tar' % (self._weekday, label_name) 207 cmd = ('cd %s; tar -cvf %s %s/chromiumos_test_image.bin; ' 208 'cp %s %s/.') % (images_path, tar_file_name, test_path, 209 tar_file_name, dest_dir) 210 if dry_run: 211 print('CMD: %s' % cmd) 212 tar_ret = 0 213 else: 214 tar_ret = self._ce.RunCommand(cmd) 215 if tar_ret: 216 self._l.LogOutput('Error while creating/copying test tar file(%s).' % 217 tar_file_name) 218 219 def _SendEmail(self): 220 """Find email message generated by crosperf and send it.""" 221 filename = os.path.join(self._reports_dir, 'msg_body.html') 222 if (os.path.exists(filename) and 223 os.path.exists(os.path.expanduser(MAIL_PROGRAM))): 224 email_title = 'buildbot test results' 225 if self._patches_string == USE_LLVM_PATCH: 226 email_title = 'buildbot llvm test results' 227 command = ('cat %s | %s -s "%s, %s" -team -html' % 228 (filename, MAIL_PROGRAM, email_title, self._board)) 229 self._ce.RunCommand(command) 230 231 def DoAll(self): 232 """Main function inside ToolchainComparator class. 233 234 Launch trybot, get image names, create crosperf experiment file, run 235 crosperf, and copy images into seven-day report directories. 236 """ 237 date_str = datetime.date.today() 238 description = 'master_%s_%s_%s' % (self._patches_string, self._build, 239 date_str) 240 trybot_image = buildbot_utils.GetTrybotImage(self._chromeos_root, 241 self._build, 242 self._patches, 243 description, 244 build_toolchain=True) 245 246 vanilla_image = self._ParseVanillaImage(trybot_image) 247 248 print('trybot_image: %s' % trybot_image) 249 print('vanilla_image: %s' % vanilla_image) 250 if len(trybot_image) == 0: 251 self._l.LogError('Unable to find trybot_image for %s!' % description) 252 return 1 253 if len(vanilla_image) == 0: 254 self._l.LogError('Unable to find vanilla image for %s!' % description) 255 return 1 256 if os.getlogin() == ROLE_ACCOUNT: 257 self._FinishSetup() 258 259 self._TestImages(trybot_image, vanilla_image) 260 self._SendEmail() 261 if (self._patches_string == USE_NEXT_GCC_PATCH and 262 self._board in WEEKLY_REPORT_BOARDS): 263 # Only try to copy the image files if the test runs ran successfully. 264 self._CopyWeeklyReportFiles(trybot_image, vanilla_image) 265 return 0 266 267 268def Main(argv): 269 """The main function.""" 270 271 # Common initializations 272 command_executer.InitCommandExecuter() 273 parser = optparse.OptionParser() 274 parser.add_option('--remote', 275 dest='remote', 276 help='Remote machines to run tests on.') 277 parser.add_option('--board', 278 dest='board', 279 default='x86-zgb', 280 help='The target board.') 281 parser.add_option('--chromeos_root', 282 dest='chromeos_root', 283 help='The chromeos root from which to run tests.') 284 parser.add_option('--weekday', 285 default='', 286 dest='weekday', 287 help='The day of the week for which to run tests.') 288 parser.add_option('--patch', 289 dest='patches', 290 help='The patches to use for the testing, ' 291 "seprate the patch numbers with ',' " 292 'for more than one patches.') 293 parser.add_option('--noschedv2', 294 dest='noschedv2', 295 action='store_true', 296 default=False, 297 help='Pass --noschedv2 to crosperf.') 298 299 options, _ = parser.parse_args(argv) 300 if not options.board: 301 print('Please give a board.') 302 return 1 303 if not options.remote: 304 print('Please give at least one remote machine.') 305 return 1 306 if not options.chromeos_root: 307 print('Please specify the ChromeOS root directory.') 308 return 1 309 if options.patches: 310 patches = options.patches 311 else: 312 patches = USE_NEXT_GCC_PATCH 313 314 fc = ToolchainComparator(options.board, options.remote, options.chromeos_root, 315 options.weekday, patches, options.noschedv2) 316 return fc.DoAll() 317 318 319if __name__ == '__main__': 320 retval = Main(sys.argv) 321 sys.exit(retval) 322