1# -*- coding: utf-8 -*- 2# Copyright 2013 The ChromiumOS Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""A module to generate experiments.""" 7 8 9import os 10import re 11import socket 12import sys 13 14from benchmark import Benchmark 15from cros_utils import command_executer 16from cros_utils import logger 17from experiment import Experiment 18import file_lock_machine 19from label import Label 20from label import MockLabel 21from results_cache import CacheConditions 22import test_flag 23 24import config 25 26 27# Users may want to run Telemetry tests either individually, or in 28# specified sets. Here we define sets of tests that users may want 29# to run together. 30 31telemetry_perfv2_tests = [ 32 "kraken", 33 "octane", 34] 35 36telemetry_pagecycler_tests = [ 37 "page_cycler_v2.intl_ar_fa_he", 38 "page_cycler_v2.intl_es_fr_pt-BR", 39 "page_cycler_v2.intl_hi_ru", 40 "page_cycler_v2.intl_ja_zh", 41 "page_cycler_v2.intl_ko_th_vi", 42 "page_cycler_v2.typical_25", 43] 44 45telemetry_toolchain_old_perf_tests = [ 46 "page_cycler_v2.intl_es_fr_pt-BR", 47 "page_cycler_v2.intl_hi_ru", 48 "page_cycler_v2.intl_ja_zh", 49 "page_cycler_v2.intl_ko_th_vi", 50 "page_cycler_v2.netsim.top_10", 51 "page_cycler_v2.typical_25", 52 "spaceport", 53 "tab_switching.top_10", 54] 55telemetry_toolchain_perf_tests = [ 56 "octane", 57 "kraken", 58 "speedometer", 59 "speedometer2", 60 "jetstream2", 61] 62graphics_perf_tests = [ 63 "graphics_GLBench", 64 "graphics_GLMark2", 65 "graphics_SanAngeles", 66 "graphics_WebGLAquarium", 67 "graphics_WebGLPerformance", 68] 69# TODO: disable rendering.desktop by default as the benchmark is 70# currently in a bad state 71# page_cycler_v2.typical_25 is deprecated and the recommend replacement is 72# loading.desktop@@typical (crbug.com/916340) 73telemetry_crosbolt_perf_tests = [ 74 "octane", 75 "kraken", 76 "speedometer2", 77 "jetstream", 78 "loading.desktop", 79 # 'rendering.desktop', 80] 81 82crosbolt_perf_tests = [ 83 "graphics_WebGLAquarium", 84 "tast.video.PlaybackPerfVP91080P30FPS", 85] 86 87# 'cheets_AntutuTest', 88# 'cheets_PerfBootServer', 89# 'cheets_CandyCrushTest', 90# 'cheets_LinpackTest', 91# ] 92 93dso_list = [ 94 "all", 95 "chrome", 96 "kallsyms", 97] 98 99 100class ExperimentFactory(object): 101 """Factory class for building an Experiment, given an ExperimentFile as input. 102 103 This factory is currently hardcoded to produce an experiment for running 104 ChromeOS benchmarks, but the idea is that in the future, other types 105 of experiments could be produced. 106 """ 107 108 def AppendBenchmarkSet( 109 self, 110 benchmarks, 111 benchmark_list, 112 test_args, 113 iterations, 114 rm_chroot_tmp, 115 perf_args, 116 suite, 117 show_all_results, 118 retries, 119 run_local, 120 cwp_dso, 121 weight, 122 ): 123 """Add all the tests in a set to the benchmarks list.""" 124 for test_name in benchmark_list: 125 telemetry_benchmark = Benchmark( 126 test_name, 127 test_name, 128 test_args, 129 iterations, 130 rm_chroot_tmp, 131 perf_args, 132 suite, 133 show_all_results, 134 retries, 135 run_local, 136 cwp_dso, 137 weight, 138 ) 139 benchmarks.append(telemetry_benchmark) 140 141 def GetExperiment(self, experiment_file, working_directory, log_dir): 142 """Construct an experiment from an experiment file.""" 143 global_settings = experiment_file.GetGlobalSettings() 144 experiment_name = global_settings.GetField("name") 145 board = global_settings.GetField("board") 146 chromeos_root = global_settings.GetField("chromeos_root") 147 log_level = global_settings.GetField("logging_level") 148 if log_level not in ("quiet", "average", "verbose"): 149 log_level = "verbose" 150 151 crosfleet = global_settings.GetField("crosfleet") 152 no_lock = bool(global_settings.GetField("no_lock")) 153 # Check whether crosfleet tool is installed correctly for crosfleet mode. 154 if crosfleet and not self.CheckCrosfleetTool(chromeos_root, log_level): 155 sys.exit(0) 156 157 remote = global_settings.GetField("remote") 158 # This is used to remove the ",' from the remote if user 159 # add them to the remote string. 160 new_remote = [] 161 if remote: 162 for i in remote: 163 c = re.sub("[\"']", "", i) 164 new_remote.append(c) 165 remote = new_remote 166 rm_chroot_tmp = global_settings.GetField("rm_chroot_tmp") 167 perf_args = global_settings.GetField("perf_args") 168 download_debug = global_settings.GetField("download_debug") 169 # Do not download debug symbols when perf_args is not specified. 170 if not perf_args and download_debug: 171 download_debug = False 172 acquire_timeout = global_settings.GetField("acquire_timeout") 173 cache_dir = global_settings.GetField("cache_dir") 174 cache_only = global_settings.GetField("cache_only") 175 config.AddConfig("no_email", global_settings.GetField("no_email")) 176 share_cache = global_settings.GetField("share_cache") 177 results_dir = global_settings.GetField("results_dir") 178 compress_results = global_settings.GetField("compress_results") 179 # Warn user that option use_file_locks is deprecated. 180 use_file_locks = global_settings.GetField("use_file_locks") 181 if use_file_locks: 182 l = logger.GetLogger() 183 l.LogWarning( 184 "Option use_file_locks is deprecated, please remove it " 185 "from your experiment settings." 186 ) 187 locks_dir = global_settings.GetField("locks_dir") 188 # If not specified, set the locks dir to the default locks dir in 189 # file_lock_machine. 190 if not locks_dir: 191 locks_dir = file_lock_machine.Machine.LOCKS_DIR 192 if not os.path.exists(locks_dir): 193 raise RuntimeError( 194 "Cannot access default lock directory. " 195 "Please run prodaccess or specify a local directory" 196 ) 197 chrome_src = global_settings.GetField("chrome_src") 198 show_all_results = global_settings.GetField("show_all_results") 199 cwp_dso = global_settings.GetField("cwp_dso") 200 if cwp_dso and not cwp_dso in dso_list: 201 raise RuntimeError("The DSO specified is not supported") 202 ignore_min_max = global_settings.GetField("ignore_min_max") 203 dut_config = { 204 "enable_aslr": global_settings.GetField("enable_aslr"), 205 "intel_pstate": global_settings.GetField("intel_pstate"), 206 "cooldown_time": global_settings.GetField("cooldown_time"), 207 "cooldown_temp": global_settings.GetField("cooldown_temp"), 208 "governor": global_settings.GetField("governor"), 209 "cpu_usage": global_settings.GetField("cpu_usage"), 210 "cpu_freq_pct": global_settings.GetField("cpu_freq_pct"), 211 "turbostat": global_settings.GetField("turbostat"), 212 "top_interval": global_settings.GetField("top_interval"), 213 } 214 215 # Default cache hit conditions. The image checksum in the cache and the 216 # computed checksum of the image must match. Also a cache file must exist. 217 cache_conditions = [ 218 CacheConditions.CACHE_FILE_EXISTS, 219 CacheConditions.CHECKSUMS_MATCH, 220 ] 221 if global_settings.GetField("rerun_if_failed"): 222 cache_conditions.append(CacheConditions.RUN_SUCCEEDED) 223 if global_settings.GetField("rerun"): 224 cache_conditions.append(CacheConditions.FALSE) 225 if global_settings.GetField("same_machine"): 226 cache_conditions.append(CacheConditions.SAME_MACHINE_MATCH) 227 if global_settings.GetField("same_specs"): 228 cache_conditions.append(CacheConditions.MACHINES_MATCH) 229 230 # Construct benchmarks. 231 # Some fields are common with global settings. The values are 232 # inherited and/or merged with the global settings values. 233 benchmarks = [] 234 all_benchmark_settings = experiment_file.GetSettings("benchmark") 235 236 # Check if there is duplicated benchmark name 237 benchmark_names = {} 238 # Check if in cwp_dso mode, all benchmarks should have same iterations 239 cwp_dso_iterations = 0 240 241 for benchmark_settings in all_benchmark_settings: 242 benchmark_name = benchmark_settings.name 243 test_name = benchmark_settings.GetField("test_name") 244 if not test_name: 245 test_name = benchmark_name 246 test_args = benchmark_settings.GetField("test_args") 247 248 # Rename benchmark name if 'story-filter' or 'story-tag-filter' specified 249 # in test_args. Make sure these two tags only appear once. 250 story_count = 0 251 for arg in test_args.split(): 252 if "--story-filter=" in arg or "--story-tag-filter=" in arg: 253 story_count += 1 254 if story_count > 1: 255 raise RuntimeError( 256 "Only one story or story-tag filter allowed in " 257 "a single benchmark run" 258 ) 259 # Rename benchmark name with an extension of 'story'-option 260 benchmark_name = "%s@@%s" % ( 261 benchmark_name, 262 arg.split("=")[-1], 263 ) 264 265 # Check for duplicated benchmark name after renaming 266 if not benchmark_name in benchmark_names: 267 benchmark_names[benchmark_name] = True 268 else: 269 raise SyntaxError( 270 "Duplicate benchmark name: '%s'." % benchmark_name 271 ) 272 273 iterations = benchmark_settings.GetField("iterations") 274 if cwp_dso: 275 if cwp_dso_iterations not in (0, iterations): 276 raise RuntimeError( 277 "Iterations of each benchmark run are not the " "same" 278 ) 279 cwp_dso_iterations = iterations 280 281 suite = benchmark_settings.GetField("suite") 282 retries = benchmark_settings.GetField("retries") 283 run_local = benchmark_settings.GetField("run_local") 284 weight = benchmark_settings.GetField("weight") 285 if weight: 286 if not cwp_dso: 287 raise RuntimeError( 288 "Weight can only be set when DSO specified" 289 ) 290 if suite != "telemetry_Crosperf": 291 raise RuntimeError( 292 "CWP approximation weight only works with " 293 "telemetry_Crosperf suite" 294 ) 295 if run_local: 296 raise RuntimeError( 297 "run_local must be set to False to use CWP " 298 "approximation" 299 ) 300 if weight < 0: 301 raise RuntimeError("Weight should be a float >=0") 302 elif cwp_dso: 303 raise RuntimeError( 304 "With DSO specified, each benchmark should have a " "weight" 305 ) 306 307 if suite == "telemetry_Crosperf": 308 if test_name == "all_perfv2": 309 self.AppendBenchmarkSet( 310 benchmarks, 311 telemetry_perfv2_tests, 312 test_args, 313 iterations, 314 rm_chroot_tmp, 315 perf_args, 316 suite, 317 show_all_results, 318 retries, 319 run_local, 320 cwp_dso, 321 weight, 322 ) 323 elif test_name == "all_pagecyclers": 324 self.AppendBenchmarkSet( 325 benchmarks, 326 telemetry_pagecycler_tests, 327 test_args, 328 iterations, 329 rm_chroot_tmp, 330 perf_args, 331 suite, 332 show_all_results, 333 retries, 334 run_local, 335 cwp_dso, 336 weight, 337 ) 338 elif test_name == "all_crosbolt_perf": 339 self.AppendBenchmarkSet( 340 benchmarks, 341 telemetry_crosbolt_perf_tests, 342 test_args, 343 iterations, 344 rm_chroot_tmp, 345 perf_args, 346 "telemetry_Crosperf", 347 show_all_results, 348 retries, 349 run_local, 350 cwp_dso, 351 weight, 352 ) 353 self.AppendBenchmarkSet( 354 benchmarks, 355 crosbolt_perf_tests, 356 "", 357 iterations, 358 rm_chroot_tmp, 359 perf_args, 360 "", 361 show_all_results, 362 retries, 363 run_local=False, 364 cwp_dso=cwp_dso, 365 weight=weight, 366 ) 367 elif test_name == "all_toolchain_perf": 368 self.AppendBenchmarkSet( 369 benchmarks, 370 telemetry_toolchain_perf_tests, 371 test_args, 372 iterations, 373 rm_chroot_tmp, 374 perf_args, 375 suite, 376 show_all_results, 377 retries, 378 run_local, 379 cwp_dso, 380 weight, 381 ) 382 # Add non-telemetry toolchain-perf benchmarks: 383 384 # Tast test platform.ReportDiskUsage for image size. 385 benchmarks.append( 386 Benchmark( 387 "platform.ReportDiskUsage", 388 "platform.ReportDiskUsage", 389 "", 390 1, # This is not a performance benchmark, only run once. 391 rm_chroot_tmp, 392 "", 393 "tast", # Specify the suite to be 'tast' 394 show_all_results, 395 retries, 396 ) 397 ) 398 399 # TODO: crbug.com/1057755 Do not enable graphics_WebGLAquarium until 400 # it gets fixed. 401 # 402 # benchmarks.append( 403 # Benchmark( 404 # 'graphics_WebGLAquarium', 405 # 'graphics_WebGLAquarium', 406 # '', 407 # iterations, 408 # rm_chroot_tmp, 409 # perf_args, 410 # 'crosperf_Wrapper', # Use client wrapper in Autotest 411 # show_all_results, 412 # retries, 413 # run_local=False, 414 # cwp_dso=cwp_dso, 415 # weight=weight)) 416 elif test_name == "all_toolchain_perf_old": 417 self.AppendBenchmarkSet( 418 benchmarks, 419 telemetry_toolchain_old_perf_tests, 420 test_args, 421 iterations, 422 rm_chroot_tmp, 423 perf_args, 424 suite, 425 show_all_results, 426 retries, 427 run_local, 428 cwp_dso, 429 weight, 430 ) 431 else: 432 benchmark = Benchmark( 433 benchmark_name, 434 test_name, 435 test_args, 436 iterations, 437 rm_chroot_tmp, 438 perf_args, 439 suite, 440 show_all_results, 441 retries, 442 run_local, 443 cwp_dso, 444 weight, 445 ) 446 benchmarks.append(benchmark) 447 else: 448 if test_name == "all_graphics_perf": 449 self.AppendBenchmarkSet( 450 benchmarks, 451 graphics_perf_tests, 452 "", 453 iterations, 454 rm_chroot_tmp, 455 perf_args, 456 "", 457 show_all_results, 458 retries, 459 run_local=False, 460 cwp_dso=cwp_dso, 461 weight=weight, 462 ) 463 else: 464 # Add the single benchmark. 465 benchmark = Benchmark( 466 benchmark_name, 467 test_name, 468 test_args, 469 iterations, 470 rm_chroot_tmp, 471 perf_args, 472 suite, 473 show_all_results, 474 retries, 475 run_local=False, 476 cwp_dso=cwp_dso, 477 weight=weight, 478 ) 479 benchmarks.append(benchmark) 480 481 if not benchmarks: 482 raise RuntimeError("No benchmarks specified") 483 484 # Construct labels. 485 # Some fields are common with global settings. The values are 486 # inherited and/or merged with the global settings values. 487 labels = [] 488 all_label_settings = experiment_file.GetSettings("label") 489 all_remote = list(remote) 490 for label_settings in all_label_settings: 491 label_name = label_settings.name 492 image = label_settings.GetField("chromeos_image") 493 build = label_settings.GetField("build") 494 autotest_path = label_settings.GetField("autotest_path") 495 debug_path = label_settings.GetField("debug_path") 496 chromeos_root = label_settings.GetField("chromeos_root") 497 my_remote = label_settings.GetField("remote") 498 compiler = label_settings.GetField("compiler") 499 new_remote = [] 500 if my_remote: 501 for i in my_remote: 502 c = re.sub("[\"']", "", i) 503 new_remote.append(c) 504 my_remote = new_remote 505 506 if image: 507 if crosfleet: 508 raise RuntimeError( 509 "In crosfleet mode, local image should not be used." 510 ) 511 if build: 512 raise RuntimeError( 513 "Image path and build are provided at the same " 514 "time, please use only one of them." 515 ) 516 else: 517 if not build: 518 raise RuntimeError("Can not have empty 'build' field!") 519 image, autotest_path, debug_path = label_settings.GetXbuddyPath( 520 build, 521 autotest_path, 522 debug_path, 523 board, 524 chromeos_root, 525 log_level, 526 download_debug, 527 ) 528 529 cache_dir = label_settings.GetField("cache_dir") 530 chrome_src = label_settings.GetField("chrome_src") 531 532 # TODO(yunlian): We should consolidate code in machine_manager.py 533 # to derermine whether we are running from within google or not 534 if ( 535 "corp.google.com" in socket.gethostname() 536 and not my_remote 537 and not crosfleet 538 ): 539 my_remote = self.GetDefaultRemotes(board) 540 if global_settings.GetField("same_machine") and len(my_remote) > 1: 541 raise RuntimeError( 542 "Only one remote is allowed when same_machine " 543 "is turned on" 544 ) 545 all_remote += my_remote 546 image_args = label_settings.GetField("image_args") 547 if test_flag.GetTestMode(): 548 # pylint: disable=too-many-function-args 549 label = MockLabel( 550 label_name, 551 build, 552 image, 553 autotest_path, 554 debug_path, 555 chromeos_root, 556 board, 557 my_remote, 558 image_args, 559 cache_dir, 560 cache_only, 561 log_level, 562 compiler, 563 crosfleet, 564 chrome_src, 565 ) 566 else: 567 label = Label( 568 label_name, 569 build, 570 image, 571 autotest_path, 572 debug_path, 573 chromeos_root, 574 board, 575 my_remote, 576 image_args, 577 cache_dir, 578 cache_only, 579 log_level, 580 compiler, 581 crosfleet, 582 chrome_src, 583 ) 584 labels.append(label) 585 586 if not labels: 587 raise RuntimeError("No labels specified") 588 589 email = global_settings.GetField("email") 590 all_remote += list(set(my_remote)) 591 all_remote = list(set(all_remote)) 592 if crosfleet: 593 for remote in all_remote: 594 self.CheckRemotesInCrosfleet(remote) 595 experiment = Experiment( 596 experiment_name, 597 all_remote, 598 working_directory, 599 chromeos_root, 600 cache_conditions, 601 labels, 602 benchmarks, 603 experiment_file.Canonicalize(), 604 email, 605 acquire_timeout, 606 log_dir, 607 log_level, 608 share_cache, 609 results_dir, 610 compress_results, 611 locks_dir, 612 cwp_dso, 613 ignore_min_max, 614 crosfleet, 615 dut_config, 616 no_lock=no_lock, 617 ) 618 619 return experiment 620 621 def GetDefaultRemotes(self, board): 622 default_remotes_file = os.path.join( 623 os.path.dirname(__file__), "default_remotes" 624 ) 625 try: 626 with open(default_remotes_file) as f: 627 for line in f: 628 key, v = line.split(":") 629 if key.strip() == board: 630 remotes = v.strip().split() 631 if remotes: 632 return remotes 633 else: 634 raise RuntimeError( 635 "There is no remote for {0}".format(board) 636 ) 637 except IOError: 638 # TODO: rethrow instead of throwing different exception. 639 raise RuntimeError( 640 "IOError while reading file {0}".format(default_remotes_file) 641 ) 642 else: 643 raise RuntimeError("There is no remote for {0}".format(board)) 644 645 def CheckRemotesInCrosfleet(self, remote): 646 # TODO: (AI:zhizhouy) need to check whether a remote is a local or lab 647 # machine. If not lab machine, raise an error. 648 pass 649 650 def CheckCrosfleetTool(self, chromeos_root, log_level): 651 CROSFLEET_PATH = "crosfleet" 652 if os.path.exists(CROSFLEET_PATH): 653 return True 654 l = logger.GetLogger() 655 l.LogOutput("Crosfleet tool not installed, trying to install it.") 656 ce = command_executer.GetCommandExecuter(l, log_level=log_level) 657 setup_lab_tools = os.path.join( 658 chromeos_root, "chromeos-admin", "lab-tools", "setup_lab_tools" 659 ) 660 cmd = "%s" % setup_lab_tools 661 status = ce.RunCommand(cmd) 662 if status != 0: 663 raise RuntimeError( 664 "Crosfleet tool not installed correctly, please try to " 665 "manually install it from %s" % setup_lab_tools 666 ) 667 l.LogOutput( 668 "Crosfleet is installed at %s, please login before first use. " 669 'Login by running "crosfleet login" and follow instructions.' 670 % CROSFLEET_PATH 671 ) 672 return False 673