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