blob: e02f6b3fff0e99dabd39b4f4dcbf48bff29a8607 [file] [log] [blame]
#!/usr/bin/python2
"""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.
from __future__ import print_function
import datetime
import optparse
import os
import sys
import time
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'
# CL that uses LLVM to build the peppy image.
USE_LLVM_PATCH = '295217'
# The boards on which we run weekly reports
WEEKLY_REPORT_BOARDS = ['lumpy']
CROSTC_ROOT = '/usr/local/google/crostc'
ROLE_ACCOUNT = 'mobiletc-prebuild'
TOOLCHAIN_DIR = os.path.dirname(os.path.realpath(__file__))
MAIL_PROGRAM = '~/var/bin/mail-sheriff'
WEEKLY_REPORTS_ROOT = os.path.join(CROSTC_ROOT, 'weekly_test_data')
PENDING_ARCHIVES_DIR = os.path.join(CROSTC_ROOT, 'pending_archives')
NIGHTLY_TESTS_DIR = os.path.join(CROSTC_ROOT, 'nightly_test_reports')
class ToolchainComparator(object):
"""Class for doing the nightly tests work."""
def __init__(self,
board,
remotes,
chromeos_root,
weekday,
patches,
noschedv2=False):
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
self._patches = patches.split(',')
self._patches_string = '_'.join(str(p) for p in self._patches)
self._noschedv2 = noschedv2
if not weekday:
self._weekday = time.strftime('%a')
else:
self._weekday = weekday
timestamp = datetime.datetime.strftime(datetime.datetime.now(),
'%Y-%m-%d_%H:%M:%S')
self._reports_dir = os.path.join(NIGHTLY_TESTS_DIR,
'%s.%s' % (timestamp, board),)
def _ParseVanillaImage(self, trybot_image):
"""Parse a trybot artifact name to get corresponding vanilla image.
Args:
trybot_image: artifact name such as
'trybot-daisy-release/R40-6394.0.0-b1389'
Returns:
Corresponding official image name, e.g. 'daisy-release/R40-6394.0.0'.
"""
start_pos = trybot_image.find(self._build)
assert start_pos != -1
end_pos = trybot_image.rfind('-b')
assert end_pos != -1
vanilla_image = trybot_image[start_pos:end_pos]
return vanilla_image
def _ParseNonAFDOImage(self, trybot_image):
"""Parse a trybot artifact name to get corresponding non-AFDO image.
Args:
trybot_image: artifact name such as
'trybot-daisy-release/R40-6394.0.0-b1389'
Returns:
Corresponding chrome PFQ image name, e.g.
'daisy-chrome-pfq/R40-6394.0.0-rc1'.
"""
start_pos = trybot_image.find(self._build)
assert start_pos != -1
end_pos = trybot_image.rfind('-b')
assert end_pos != -1
nonafdo_image = trybot_image[start_pos:end_pos]
pfq_suffix = '-chrome-pfq'
nonafdo_image = nonafdo_image.replace('-release', pfq_suffix) + '-rc1'
assert nonafdo_image.find(pfq_suffix) != -1
return nonafdo_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 != 0:
raise RuntimeError('chmod for testing_rsa failed')
def _TestImages(self, trybot_image, vanilla_image, nonafdo_image):
"""Create crosperf experiment file.
Given the names of the trybot, vanilla and non-AFDO 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
compiler_string = 'gcc'
if USE_LLVM_PATCH in self._patches_string:
experiment_file_name = '%s_llvm_experiment.txt' % self._board
compiler_string = 'llvm'
experiment_file = os.path.join(experiment_file_dir, experiment_file_name)
experiment_header = """
board: %s
remote: %s
retries: 1
""" % (self._board, self._remotes)
experiment_tests = """
benchmark: all_toolchain_perf {
suite: telemetry_Crosperf
iterations: 3
}
"""
with open(experiment_file, 'w') as f:
f.write(experiment_header)
f.write(experiment_tests)
# Now add vanilla to test file.
official_image = """
vanilla_image {
chromeos_root: %s
build: %s
compiler: gcc
}
""" % (self._chromeos_root, vanilla_image)
f.write(official_image)
# Now add non-AFDO image to test file.
if nonafdo_image:
official_nonafdo_image = """
nonafdo_image {
chromeos_root: %s
build: %s
compiler: gcc
}
""" % (self._chromeos_root, nonafdo_image)
f.write(official_nonafdo_image)
label_string = '%s_trybot_image' % compiler_string
if USE_NEXT_GCC_PATCH in self._patches:
label_string = 'gcc_next_trybot_image'
experiment_image = """
%s {
chromeos_root: %s
build: %s
compiler: %s
}
""" % (label_string, self._chromeos_root, trybot_image,
compiler_string)
f.write(experiment_image)
crosperf = os.path.join(TOOLCHAIN_DIR, 'crosperf', 'crosperf')
noschedv2_opts = '--noschedv2' if self._noschedv2 else ''
command = ('{crosperf} --no_email=True --results_dir={r_dir} '
'--json_report=True {noschedv2_opts} {exp_file}').format(
crosperf=crosperf,
r_dir=self._reports_dir,
noschedv2_opts=noschedv2_opts,
exp_file=experiment_file)
ret = self._ce.RunCommand(command)
if ret != 0:
raise RuntimeError("Couldn't run crosperf!")
else:
# Copy json report to pending archives directory.
command = 'cp %s/*.json %s/.' % (self._reports_dir, PENDING_ARCHIVES_DIR)
ret = self._ce.RunCommand(command)
return
def _CopyWeeklyReportFiles(self, trybot_image, vanilla_image, nonafdo_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': trybot_image, 'vanilla': vanilla_image}
if nonafdo_image:
labels['nonafdo'] = nonafdo_image
for label_name, test_path in labels.iteritems():
tar_file_name = '%s_%s_image.tar' % (self._weekday, label_name)
cmd = ('cd %s; tar -cvf %s %s/chromiumos_test_image.bin; '
'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 _SendEmail(self):
"""Find email message generated by crosperf and send it."""
filename = os.path.join(self._reports_dir, 'msg_body.html')
if (os.path.exists(filename) and
os.path.exists(os.path.expanduser(MAIL_PROGRAM))):
email_title = 'buildbot test results'
if self._patches_string == USE_LLVM_PATCH:
email_title = 'buildbot llvm test results'
command = ('cat %s | %s -s "%s, %s" -team -html' %
(filename, MAIL_PROGRAM, email_title, self._board))
self._ce.RunCommand(command)
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.
"""
date_str = datetime.date.today()
description = 'master_%s_%s_%s' % (self._patches_string, self._build,
date_str)
trybot_image = buildbot_utils.GetTrybotImage(self._chromeos_root,
self._build,
self._patches,
description,
build_toolchain=True)
if len(trybot_image) == 0:
self._l.LogError('Unable to find trybot_image for %s!' % description)
return 1
vanilla_image = self._ParseVanillaImage(trybot_image)
nonafdo_image = self._ParseNonAFDOImage(trybot_image)
if not buildbot_utils.DoesImageExist(self._chromeos_root, nonafdo_image):
nonafdo_image = ''
# The trybot image is ready here, in some cases, the vanilla image
# is not ready, so we need to make sure vanilla image is available.
buildbot_utils.WaitForImage(self._chromeos_root, vanilla_image)
print('trybot_image: %s' % trybot_image)
print('vanilla_image: %s' % vanilla_image)
print('nonafdo_image: %s' % nonafdo_image)
if os.getlogin() == ROLE_ACCOUNT:
self._FinishSetup()
self._TestImages(trybot_image, vanilla_image, nonafdo_image)
self._SendEmail()
if (self._patches_string == USE_NEXT_GCC_PATCH and
self._board in WEEKLY_REPORT_BOARDS):
# Only try to copy the image files if the test runs ran successfully.
self._CopyWeeklyReportFiles(trybot_image, vanilla_image, nonafdo_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.')
parser.add_option('--patch',
dest='patches',
help='The patches to use for the testing, '
"seprate the patch numbers with ',' "
'for more than one patches.')
parser.add_option('--noschedv2',
dest='noschedv2',
action='store_true',
default=False,
help='Pass --noschedv2 to crosperf.')
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
if options.patches:
patches = options.patches
else:
patches = USE_NEXT_GCC_PATCH
fc = ToolchainComparator(options.board, options.remote, options.chromeos_root,
options.weekday, patches, options.noschedv2)
return fc.DoAll()
if __name__ == '__main__':
retval = Main(sys.argv)
sys.exit(retval)