test_toolchains.py revision 4536ef6c9da0f8c0c544552798c0bcf516519f73
1#!/usr/bin/python 2 3# Script to test different toolchains against ChromeOS benchmarks. 4import optparse 5import os 6import sys 7import build_chromeos 8import setup_chromeos 9import time 10from utils import command_executer 11from utils import misc 12from utils import logger 13 14 15WEEKLY_REPORTS_ROOT="/usr/local/google/crostc/weekly_test_data" 16 17class GCCConfig(object): 18 def __init__(self, githash): 19 self.githash = githash 20 21 22class ToolchainConfig: 23 def __init__(self, gcc_config=None, binutils_config=None): 24 self.gcc_config = gcc_config 25 26 27class ChromeOSCheckout(object): 28 def __init__(self, board, chromeos_root): 29 self._board = board 30 self._chromeos_root = chromeos_root 31 self._ce = command_executer.GetCommandExecuter() 32 self._l = logger.GetLogger() 33 self._build_num = None 34 35 def _DeleteChroot(self): 36 command = "cd %s; cros_sdk --delete" % self._chromeos_root 37 return self._ce.RunCommand(command) 38 39 def _DeleteCcahe(self): 40 # crosbug.com/34956 41 command = "sudo rm -rf %s" % os.path.join(self._chromeos_root, ".cache") 42 return self._ce.RunCommand(command) 43 44 def _GetBuildNumber(self): 45 """ This function assumes a ChromeOS image has been built in the chroot. 46 It translates the 'latest' symlink in the 47 <chroot>/src/build/images/<board> directory, to find the actual 48 ChromeOS build number for the image that was built. For example, if 49 src/build/image/lumpy/latest -> R37-5982.0.2014_06_23_0454-a1, then 50 This function would parse it out and assign 'R37-5982' to self._build_num. 51 This is used to determine the official, vanilla build to use for 52 comparison tests. 53 """ 54 # Get the path to 'latest' 55 sym_path = os.path.join (misc.GetImageDir(self._chromeos_root, 56 self._board), 57 "latest") 58 # Translate the symlink to its 'real' path. 59 real_path = os.path.realpath(sym_path) 60 # Break up the path and get the last piece 61 # (e.g. 'R37-5982.0.2014_06_23_0454-a1" 62 path_pieces = real_path.split("/") 63 last_piece = path_pieces[-1] 64 # Break this piece into the image number + other pieces, and get the 65 # image number [ 'R37-5982', '0', '2014_06_23_0454-a1'] 66 image_parts = last_piece.split(".") 67 self._build_num = image_parts[0] 68 69 def _BuildAndImage(self, label=""): 70 if (not label or 71 not misc.DoesLabelExist(self._chromeos_root, self._board, label)): 72 build_chromeos_args = [build_chromeos.__file__, 73 "--chromeos_root=%s" % self._chromeos_root, 74 "--board=%s" % self._board, 75 "--rebuild"] 76 if self._public: 77 build_chromeos_args.append("--env=USE=-chrome_internal") 78 79 ret = build_chromeos.Main(build_chromeos_args) 80 if ret: 81 raise Exception("Couldn't build ChromeOS!") 82 83 if not self._build_num: 84 self._GetBuildNumber() 85 # Check to see if we need to create the symbolic link for the vanilla 86 # image, and do so if appropriate. 87 if not misc.DoesLabelExist(self._chromeos_root, self._board, "vanilla"): 88 build_name = "%s-release/%s.0.0" % (self._board, self._build_num) 89 full_vanilla_path = os.path.join (os.getcwd(), self._chromeos_root, 90 'chroot/tmp', build_name) 91 misc.LabelLatestImage(self._chromeos_root, self._board, label, 92 full_vanilla_path) 93 else: 94 misc.LabelLatestImage(self._chromeos_root, self._board, label) 95 return label 96 97 def _SetupBoard(self, env_dict, usepkg_flag, clobber_flag): 98 env_string = misc.GetEnvStringFromDict(env_dict) 99 command = ("%s %s" % 100 (env_string, 101 misc.GetSetupBoardCommand(self._board, 102 usepkg=usepkg_flag, 103 force=clobber_flag))) 104 ret = self._ce.ChrootRunCommand(self._chromeos_root, 105 command) 106 error_str = "Could not setup board: '%s'" % command 107 assert ret == 0, error_str 108 109 def _UnInstallToolchain(self): 110 command = ("sudo CLEAN_DELAY=0 emerge -C cross-%s/gcc" % 111 misc.GetCtargetFromBoard(self._board, 112 self._chromeos_root)) 113 ret = self._ce.ChrootRunCommand(self._chromeos_root, 114 command) 115 if ret: 116 raise Exception("Couldn't uninstall the toolchain!") 117 118 def _CheckoutChromeOS(self): 119 # TODO(asharif): Setup a fixed ChromeOS version (quarterly snapshot). 120 if not os.path.exists(self._chromeos_root): 121 setup_chromeos_args = [setup_chromeos.__file__, 122 "--dir=%s" % self._chromeos_root] 123 if self._public: 124 setup_chromeos_args.append("--public") 125 ret = setup_chromeos.Main(setup_chromeos_args) 126 if ret: 127 raise Exception("Couldn't run setup_chromeos!") 128 129 130 def _BuildToolchain(self, config): 131 # Call setup_board for basic, vanilla setup. 132 self._SetupBoard({}, usepkg_flag=True, clobber_flag=False) 133 # Now uninstall the vanilla compiler and setup/build our custom 134 # compiler. 135 self._UnInstallToolchain() 136 envdict = {"USE": "git_gcc", 137 "GCC_GITHASH": config.gcc_config.githash, 138 "EMERGE_DEFAULT_OPTS": "--exclude=gcc"} 139 self._SetupBoard(envdict, usepkg_flag=False, clobber_flag=False) 140 141 142class ToolchainComparator(ChromeOSCheckout): 143 def __init__(self, board, remotes, configs, clean, public, force_mismatch): 144 self._board = board 145 self._remotes = remotes 146 self._chromeos_root = "chromeos" 147 self._configs = configs 148 self._clean = clean 149 self._public = public 150 self._force_mismatch = force_mismatch 151 self._ce = command_executer.GetCommandExecuter() 152 self._l = logger.GetLogger() 153 ChromeOSCheckout.__init__(self, board, self._chromeos_root) 154 155 156 def _FinishSetup(self): 157 # Get correct .boto file 158 current_dir = os.getcwd() 159 src = "/home/mobiletc-prebuild/.boto" 160 dest = os.path.join(current_dir, self._chromeos_root, 161 "src/private-overlays/chromeos-overlay/" 162 "googlestorage_account.boto") 163 # Copy the file to the correct place 164 copy_cmd = "cp %s %s" % (src, dest) 165 retval = self._ce.RunCommand(copy_cmd) 166 if retval: 167 raise Exception("Couldn't copy .boto file for google storage.") 168 169 # Fix protections on ssh key 170 command = ("chmod 600 /var/cache/chromeos-cache/distfiles/target" 171 "/chrome-src-internal/src/third_party/chromite/ssh_keys" 172 "/testing_rsa") 173 retval = self._ce.ChrootRunCommand(self._chromeos_root, command) 174 if retval: 175 raise Exception("chmod for testing_rsa failed") 176 177 def _TestLabels(self, labels): 178 experiment_file = "toolchain_experiment.txt" 179 image_args = "" 180 if self._force_mismatch: 181 image_args = "--force-mismatch" 182 experiment_header = """ 183 board: %s 184 remote: %s 185 """ % (self._board, self._remotes) 186 experiment_tests = """ 187 benchmark: all_perfv2 { 188 suite: telemetry_Crosperf 189 iterations: 3 190 } 191 """ 192 with open(experiment_file, "w") as f: 193 print >>f, experiment_header 194 print >>f, experiment_tests 195 for label in labels: 196 # TODO(asharif): Fix crosperf so it accepts labels with symbols 197 crosperf_label = label 198 crosperf_label = crosperf_label.replace("-", "minus") 199 crosperf_label = crosperf_label.replace("+", "plus") 200 crosperf_label = crosperf_label.replace(".", "") 201 202 # Use the official build instead of building vanilla ourselves. 203 if label == "vanilla": 204 build_name = '%s-release/%s.0.0' % (self._board, self._build_num) 205 206 # Now add 'official build' to test file. 207 official_image = """ 208 official_image { 209 chromeos_root: %s 210 build: %s 211 } 212 """ % (self._chromeos_root, build_name) 213 print >>f, official_image 214 215 else: 216 experiment_image = """ 217 %s { 218 chromeos_image: %s 219 image_args: %s 220 } 221 """ % (crosperf_label, 222 os.path.join(misc.GetImageDir(self._chromeos_root, 223 self._board), 224 label, "chromiumos_test_image.bin"), 225 image_args) 226 print >>f, experiment_image 227 228 crosperf = os.path.join(os.path.dirname(__file__), 229 "crosperf", 230 "crosperf") 231 command = "%s --email=c-compiler-chrome %s" % (crosperf, experiment_file) 232 ret = self._ce.RunCommand(command) 233 if ret: 234 raise Exception("Couldn't run crosperf!") 235 return ret 236 237 238 def _CopyWeeklyReportFiles(self, labels): 239 """Create tar files of the custom and official images and copy them 240 to the weekly reports directory, so they exist when the weekly report 241 gets generated. IMPORTANT NOTE: This function must run *after* 242 crosperf has been run; otherwise the vanilla images will not be there. 243 """ 244 images_path = os.path.join(os.path.realpath(self._chromeos_root), 245 "src/build/images", self._board) 246 weekday = time.strftime("%a") 247 data_dir = os.path.join(WEEKLY_REPORTS_ROOT, self._board) 248 dest_dir = os.path.join (data_dir, weekday) 249 if not os.path.exists(dest_dir): 250 os.makedirs(dest_dir) 251 # Make sure dest_dir is empty (clean out last week's data). 252 cmd = "cd %s; rm -Rf %s_*_image*" % (dest_dir, weekday) 253 self._ce.RunCommand(cmd) 254 # Now create new tar files and copy them over. 255 for l in labels: 256 test_path = os.path.join(images_path, l) 257 if os.path.exists(test_path): 258 if l != "vanilla": 259 label_name = "test" 260 else: 261 label_name = "vanilla" 262 tar_file_name = "%s_%s_image.tar" % (weekday, label_name) 263 cmd = "cd %s; tar -cvf %s %s/*; cp %s %s/." % (images_path, 264 tar_file_name, 265 l, tar_file_name, 266 dest_dir) 267 tar_ret = self._ce.RunCommand(cmd) 268 if tar_ret: 269 self._l.LogOutput("Error while creating/copying test tar file(%s)." 270 % tar_file_name) 271 272 273 def DoAll(self): 274 self._CheckoutChromeOS() 275 labels = [] 276 labels.append("vanilla") 277 for config in self._configs: 278 label = misc.GetFilenameFromString(config.gcc_config.githash) 279 if (not misc.DoesLabelExist(self._chromeos_root, 280 self._board, 281 label)): 282 self._BuildToolchain(config) 283 label = self._BuildAndImage(label) 284 labels.append(label) 285 self._FinishSetup() 286 if not self._TestLabels(labels): 287 # Only try to copy the image files if the test runs ran successfully. 288 self._CopyWeeklyReportFiles(labels) 289 if self._clean: 290 ret = self._DeleteChroot() 291 if ret: return ret 292 ret = self._DeleteCcahe() 293 if ret: return ret 294 return 0 295 296 297def Main(argv): 298 """The main function.""" 299 # Common initializations 300### command_executer.InitCommandExecuter(True) 301 command_executer.InitCommandExecuter() 302 parser = optparse.OptionParser() 303 parser.add_option("--remote", 304 dest="remote", 305 help="Remote machines to run tests on.") 306 parser.add_option("--board", 307 dest="board", 308 default="x86-zgb", 309 help="The target board.") 310 parser.add_option("--githashes", 311 dest="githashes", 312 default="master", 313 help="The gcc githashes to test.") 314 parser.add_option("--clean", 315 dest="clean", 316 default=False, 317 action="store_true", 318 help="Clean the chroot after testing.") 319 parser.add_option("--public", 320 dest="public", 321 default=False, 322 action="store_true", 323 help="Use the public checkout/build.") 324 parser.add_option("--force-mismatch", 325 dest="force_mismatch", 326 default="", 327 help="Force the image regardless of board mismatch") 328 options, _ = parser.parse_args(argv) 329 if not options.board: 330 print "Please give a board." 331 return 1 332 if not options.remote: 333 print "Please give at least one remote machine." 334 return 1 335 toolchain_configs = [] 336 for githash in options.githashes.split(","): 337 gcc_config = GCCConfig(githash=githash) 338 toolchain_config = ToolchainConfig(gcc_config=gcc_config) 339 toolchain_configs.append(toolchain_config) 340 fc = ToolchainComparator(options.board, options.remote, toolchain_configs, 341 options.clean, options.public, 342 options.force_mismatch) 343 return fc.DoAll() 344 345 346if __name__ == "__main__": 347 retval = Main(sys.argv) 348 sys.exit(retval) 349