New script for using buildbots for nightly tests.
This script could eventually replace the current test_toolchains.py
script. Instead of downloading and building ChromeOS locally, it
launches a buildbot to build ChromeOS with the latest mobile
toolchain compiler. It then downloads the trybot image (and the
corresponding vanilla image), generates the crosperf experiment file,
and launches crosperf.
I have been running this as a second cron job on chrotomation2,
running daisy tests, and it has been working just fine.
BUG=None
TEST=Ran in cron jobs on chrotomation2.
Change-Id: I3b17d0373e2fae359fc8f4e0188dcec2943c7b71
Reviewed-on: https://chrome-internal-review.googlesource.com/187735
Reviewed-by: Caroline Tice <cmtice@google.com>
Trybot-Ready: Caroline Tice <cmtice@google.com>
Tested-by: Caroline Tice <cmtice@google.com>
Commit-Queue: Caroline Tice <cmtice@google.com>
diff --git a/buildbot_test_toolchains.py b/buildbot_test_toolchains.py
new file mode 100644
index 0000000..bc2c056
--- /dev/null
+++ b/buildbot_test_toolchains.py
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+"""
+Script for running nightly compiler tests on ChromeOS.
+
+This script launches a buildbot to build ChromeOS with the latest compiler on
+a particular board; then it finds and downloads the trybot image and the
+corresponding official image, and runs crosperf performance tests comparing
+the two. It then generates a report, emails it to the c-compiler-chrome, as
+well as copying the images into the seven-day reports directory.
+"""
+
+# Script to test different toolchains against ChromeOS benchmarks.
+import optparse
+import os
+import sys
+import time
+import urllib2
+
+from utils import command_executer
+from utils import logger
+
+from utils import buildbot_utils
+
+# CL that updated GCC ebuilds to use 'next_gcc'.
+USE_NEXT_GCC_PATCH ="230260"
+
+WEEKLY_REPORTS_ROOT = "/usr/local/google/crostc/weekly_test_data"
+ROLE_ACCOUNT = "mobiletc-prebuild"
+TOOLCHAIN_DIR = os.path.dirname(os.path.realpath(__file__))
+
+
+class ToolchainComparator():
+ """
+ Class for doing the nightly tests work.
+ """
+
+ def __init__(self, board, remotes, chromeos_root, weekday):
+ self._board = board
+ self._remotes = remotes
+ self._chromeos_root = chromeos_root
+ self._base_dir = os.getcwd()
+ self._ce = command_executer.GetCommandExecuter()
+ self._l = logger.GetLogger()
+ self._build = "%s-release" % board
+ if not weekday:
+ self._weekday = time.strftime("%a")
+ else:
+ self._weekday = weekday
+
+ def _ParseVanillaImage(self, trybot_image):
+ """
+ Parse a trybot artifact name to get corresponding vanilla image.
+
+ This function takes an artifact name, such as
+ 'trybot-daisy-release/R40-6394.0.0-b1389', and returns the
+ corresponding official build name, e.g. 'daisy-release/R40-6394.0.0'.
+ """
+ start_pos = trybot_image.find(self._build)
+ end_pos = trybot_image.rfind("-b")
+ vanilla_image = trybot_image[start_pos:end_pos]
+ return vanilla_image
+
+
+ def _FinishSetup(self):
+ """
+ Make sure testing_rsa file is properly set up.
+ """
+ # Fix protections on ssh key
+ command = ("chmod 600 /var/cache/chromeos-cache/distfiles/target"
+ "/chrome-src-internal/src/third_party/chromite/ssh_keys"
+ "/testing_rsa")
+ ret_val = self._ce.ChrootRunCommand(self._chromeos_root, command)
+ if ret_val:
+ raise Exception("chmod for testing_rsa failed")
+
+ def _TestImages(self, trybot_image, vanilla_image):
+ """
+ Create crosperf experiment file.
+
+ Given the names of the trybot and vanilla images, create the
+ appropriate crosperf experiment file and launch crosperf on it.
+ """
+ experiment_file_dir = os.path.join (self._chromeos_root, "..",
+ self._weekday)
+ experiment_file_name = "%s_toolchain_experiment.txt" % self._board
+ experiment_file = os.path.join (experiment_file_dir,
+ experiment_file_name)
+ experiment_header = """
+ board: %s
+ remote: %s
+ """ % (self._board, self._remotes)
+ experiment_tests = """
+ benchmark: all_perfv2 {
+ suite: telemetry_Crosperf
+ iterations: 3
+ }
+ """
+ with open(experiment_file, "w") as f:
+ print >> f, experiment_header
+ print >> f, experiment_tests
+
+ # Now add vanilla to test file.
+ official_image = """
+ vanilla_image {
+ chromeos_root: %s
+ build: %s
+ }
+ """ % (self._chromeos_root, vanilla_image)
+ print >> f, official_image
+
+ experiment_image = """
+ test_image {
+ chromeos_root: %s
+ build: %s
+ }
+ """ % (self._chromeos_root, trybot_image)
+ print >> f, experiment_image
+
+ crosperf = os.path.join(TOOLCHAIN_DIR,
+ "crosperf",
+ "crosperf")
+ command = "%s --email=c-compiler-chrome %s" % (crosperf, experiment_file)
+ ret = self._ce.RunCommand(command)
+ if ret:
+ raise Exception("Couldn't run crosperf!")
+ return ret
+
+
+ def _CopyWeeklyReportFiles(self, trybot_image, vanilla_image):
+ """
+ Put files in place for running seven-day reports.
+
+ Create tar files of the custom and official images and copy them
+ to the weekly reports directory, so they exist when the weekly report
+ gets generated. IMPORTANT NOTE: This function must run *after*
+ crosperf has been run; otherwise the vanilla images will not be there.
+ """
+
+ dry_run = False
+ if (os.getlogin() != ROLE_ACCOUNT):
+ self._l.LogOutput("Running this from non-role account; not copying "
+ "tar files for weekly reports.")
+ dry_run = True
+
+ images_path = os.path.join(os.path.realpath(self._chromeos_root),
+ "chroot/tmp")
+
+ data_dir = os.path.join(WEEKLY_REPORTS_ROOT, self._board)
+ dest_dir = os.path.join (data_dir, self._weekday)
+ if not os.path.exists(dest_dir):
+ os.makedirs(dest_dir)
+
+ # Make sure dest_dir is empty (clean out last week's data).
+ cmd = "cd %s; rm -Rf %s_*_image*" % (dest_dir, self._weekday)
+ if dry_run:
+ print "CMD: %s" % cmd
+ else:
+ self._ce.RunCommand(cmd)
+
+ # Now create new tar files and copy them over.
+ labels = [ "test", "vanilla" ]
+ for label_name in labels:
+ if label_name == "test":
+ test_path = trybot_image
+ else:
+ test_path = vanilla_image
+ tar_file_name = "%s_%s_image.tar" % (self._weekday, label_name)
+ cmd = "cd %s; tar -cvf %s %s/*; cp %s %s/." % (images_path,
+ tar_file_name,
+ test_path,
+ tar_file_name,
+ dest_dir)
+ if dry_run:
+ print "CMD: %s" % cmd
+ tar_ret = 0
+ else:
+ tar_ret = self._ce.RunCommand(cmd)
+ if tar_ret:
+ self._l.LogOutput("Error while creating/copying test tar file(%s)."
+ % tar_file_name)
+
+
+ def DoAll(self):
+ """
+ Main function inside ToolchainComparator class.
+
+ Launch trybot, get image names, create crosperf experiment file, run
+ crosperf, and copy images into seven-day report directories.
+ """
+
+ description = "master_%s_%s" % (USE_NEXT_GCC_PATCH, self._build)
+ trybot_image = buildbot_utils.GetTrybotImage(self._chromeos_root,
+ self._build,
+ [ USE_NEXT_GCC_PATCH ],
+ description)
+
+ vanilla_image = self._ParseVanillaImage(trybot_image)
+
+ print ("trybot_image: %s" % trybot_image)
+ print ("vanilla_image: %s" % vanilla_image)
+ if os.getlogin() == ROLE_ACCOUNT:
+ self._FinishSetup()
+
+ if not self._TestImages(trybot_image, vanilla_image):
+ # Only try to copy the image files if the test runs ran successfully.
+ self._CopyWeeklyReportFiles(trybot_image, vanilla_image)
+ return 0
+
+
+def Main(argv):
+ """The main function."""
+
+ # Common initializations
+ command_executer.InitCommandExecuter()
+ parser = optparse.OptionParser()
+ parser.add_option("--remote",
+ dest="remote",
+ help="Remote machines to run tests on.")
+ parser.add_option("--board",
+ dest="board",
+ default="x86-zgb",
+ help="The target board.")
+ parser.add_option("--chromeos_root",
+ dest="chromeos_root",
+ help="The chromeos root from which to run tests.")
+ parser.add_option("--weekday", default="",
+ dest="weekday",
+ help="The day of the week for which to run tests.")
+ options, _ = parser.parse_args(argv)
+ if not options.board:
+ print "Please give a board."
+ return 1
+ if not options.remote:
+ print "Please give at least one remote machine."
+ return 1
+ if not options.chromeos_root:
+ print "Please specify the ChromeOS root directory."
+ return 1
+
+ fc = ToolchainComparator(options.board, options.remote,
+ options.chromeos_root, options.weekday)
+ return fc.DoAll()
+
+
+if __name__ == "__main__":
+ retval = Main(sys.argv)
+ sys.exit(retval)
diff --git a/utils/buildbot_utils.py b/utils/buildbot_utils.py
new file mode 100644
index 0000000..e41490f
--- /dev/null
+++ b/utils/buildbot_utils.py
@@ -0,0 +1,199 @@
+#!/usr/bin/python
+
+# Copyright 2014 Google Inc. All Rights Reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import time
+import urllib2
+
+from utils import command_executer
+from utils import logger
+from utils import buildbot_json
+
+SLEEP_TIME = 600 # 10 minutes; time between polling of buildbot.
+TIME_OUT = 18000 # Decide the build is dead or will never finish
+ # after this time (5 hours).
+
+"""Utilities for launching and accessing ChromeOS buildbots."""
+
+def ParseReportLog (url, build):
+ """
+ Scrape the trybot image name off the Reports log page.
+
+ This takes the URL for a trybot Reports Stage web page,
+ and a trybot build type, such as 'daisy-release'. It
+ opens the web page and parses it looking for the trybot
+ artifact name (e.g. something like
+ 'trybot-daisy-release/R40-6394.0.0-b1389'). It returns the
+ artifact name, if found.
+ """
+ trybot_image = ""
+ url += "/text"
+ newurl = url.replace ("uberchromegw", "chromegw")
+ webpage = urllib2.urlopen(newurl)
+ data = webpage.read()
+ lines = data.split('\n')
+ for l in lines:
+ if l.find("Artifacts") and l.find("trybot"):
+ trybot_name = "trybot-%s" % build
+ start_pos = l.find(trybot_name)
+ end_pos = l.find("@https://storage")
+ trybot_image = l[start_pos:end_pos]
+
+ return trybot_image
+
+
+def GetBuildData (buildbot_queue, build_id):
+ """
+ Find the Reports stage web page for a trybot build.
+
+ This takes the name of a buildbot_queue, such as 'daisy-release'
+ and a build id (the build number), and uses the json buildbot api to
+ find the Reports stage web page for that build, if it exists.
+ """
+ builder = buildbot_json.Buildbot(
+ "http://chromegw/p/tryserver.chromiumos/").builders[buildbot_queue]
+ build_data = builder.builds[build_id].data
+ logs = build_data["logs"]
+ for l in logs:
+ fname = l[1]
+ if "steps/Report/" in fname:
+ return fname
+
+ return ""
+
+
+def FindBuildRecordFromLog(description, log_info):
+ """
+ Find the right build record in the build logs.
+
+ Get the first build record from build log with a reason field
+ that matches 'description'. ('description' is a special tag we
+ created when we launched the buildbot, so we could find it at this
+ point.)
+ """
+
+ current_line = 1
+ while current_line < len(log_info):
+ my_dict = {}
+ # Read all the lines from one "Build" to the next into my_dict
+ while True:
+ key = log_info[current_line].split(":")[0].strip()
+ value = log_info[current_line].split(":", 1)[1].strip()
+ my_dict[key] = value
+ current_line += 1
+ if "Build" in key or current_line == len(log_info):
+ break
+ try:
+ # Check to see of the build record is the right one.
+ if str(description) in my_dict["reason"]:
+ # We found a match; we're done.
+ return my_dict
+ except:
+ print "reason is not in dictionary: '%s'" % repr(my_dict)
+ else:
+ # Keep going.
+ continue
+
+ # We hit the bottom of the log without a match.
+ return {}
+
+
+def GetBuildInfo(file_dir):
+ """
+ Get all the build records for the trybot builds.
+
+ file_dir is the toolchain_utils directory.
+ """
+ ce = command_executer.GetCommandExecuter()
+ commands = ("{0}/utils/buildbot_json.py builds "
+ "http://chromegw/p/tryserver.chromiumos/"
+ .format(file_dir))
+
+ _, buildinfo, _ = ce.RunCommand(commands, return_output=True,
+ print_to_console=False)
+ build_log = buildinfo.splitlines()
+ return build_log
+
+
+def GetTrybotImage(chromeos_root, buildbot_name, patch_list, build_tag):
+ """
+ Launch buildbot and get resulting trybot artifact name.
+
+ This function launches a buildbot with the appropriate flags to
+ build the test ChromeOS image, with the current ToT mobile compiler. It
+ checks every 10 minutes to see if the trybot has finished. When the trybot
+ has finished, it parses the resulting report logs to find the trybot
+ artifact (if one was created), and returns that artifact name.
+
+ chromeos_root is the path to the ChromeOS root, needed for finding chromite
+ and launching the buildbot.
+
+ buildbot_name is the name of the buildbot queue, such as lumpy-release or
+ daisy-paladin.
+
+ patch_list a python list of the patches, if any, for the buildbot to use.
+
+ build_tag is a (unique) string to be used to look up the buildbot results
+ from among all the build records.
+ """
+ ce = command_executer.GetCommandExecuter()
+ cbuildbot_path = os.path.join(chromeos_root, "chromite/cbuildbot")
+ base_dir = os.getcwd()
+ patch_arg = ""
+ if patch_list:
+ patch_arg = "-g "
+ for p in patch_list:
+ patch_arg = patch_arg + " " + repr(p)
+ branch = "master"
+ os.chdir(cbuildbot_path)
+
+ # Launch buildbot with appropriate flags.
+ build = buildbot_name
+ description = build_tag
+ command = ("./cbuildbot --remote --nochromesdk --notests %s %s"
+ " --remote-description=%s"
+ " --chrome_rev=tot" % (patch_arg, build, description))
+ ce.RunCommand(command)
+ os.chdir(base_dir)
+
+ build_id = 0
+ # Wait for buildbot to finish running (check every 10 minutes)
+ done = False
+ running_time = 0
+ while not done:
+ done = True
+ build_info = GetBuildInfo(base_dir)
+ if not build_info:
+ logger.GetLogger().LogFatal("Unable to get build logs for target %s"
+ % build)
+
+ data_dict = FindBuildRecordFromLog(description, build_info)
+ if not data_dict:
+ logger.GetLogger().LogFatal("Unable to find build record for trybot %s"
+ % description)
+
+ if "True" in data_dict["completed"]:
+ build_id = data_dict["number"]
+ else:
+ done = False
+
+ if not done:
+ logger.GetLogger().LogOutput("{0} minutes passed.".format(
+ running_time / 60))
+ logger.GetLogger().LogOutput("Sleeping {0} seconds.".format(SLEEP_TIME))
+ time.sleep(SLEEP_TIME)
+ running_time += SLEEP_TIME
+ if running_time > TIME_OUT:
+ done = True
+
+ trybot_image = ""
+ # Buildbot has finished. Look for the log and the trybot image.
+ if build_id:
+ log_name = GetBuildData(build, build_id)
+ if log_name:
+ trybot_image = ParseReportLog(log_name, build)
+
+ return trybot_image