blob: 6c6ce63e3258a0c94dc66287e10c2026ad7626f5 [file] [log] [blame]
asharif96f59692013-02-16 03:13:36 +00001#!/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
cmtice94bc4702014-05-29 16:29:04 -07009import time
asharif96f59692013-02-16 03:13:36 +000010from utils import command_executer
11from utils import misc
12from utils import logger
13
14
cmtice94bc4702014-05-29 16:29:04 -070015WEEKLY_REPORTS_ROOT="/usr/local/google/crostc/weekly_test_data"
cmtice23130bd2014-07-15 11:19:29 -070016AFDO_BOARDS = ["butterfly", "lumpy", "stumpy", "stout", "parrot", "parrot_ivb"]
cmtice7f3190b2015-05-22 14:14:51 -070017MAIL_PROGRAM = "~/var/bin/mail-sheriff"
cmtice94bc4702014-05-29 16:29:04 -070018
asharif96f59692013-02-16 03:13:36 +000019class GCCConfig(object):
20 def __init__(self, githash):
21 self.githash = githash
22
23
24class ToolchainConfig:
25 def __init__(self, gcc_config=None, binutils_config=None):
26 self.gcc_config = gcc_config
27
28
29class ChromeOSCheckout(object):
30 def __init__(self, board, chromeos_root):
31 self._board = board
32 self._chromeos_root = chromeos_root
33 self._ce = command_executer.GetCommandExecuter()
34 self._l = logger.GetLogger()
cmtice56fb7162014-06-18 11:32:15 -070035 self._build_num = None
asharif96f59692013-02-16 03:13:36 +000036
asharif3e38de02013-02-19 19:34:59 +000037 def _DeleteChroot(self):
asharif87414272013-02-19 19:58:45 +000038 command = "cd %s; cros_sdk --delete" % self._chromeos_root
asharif3e38de02013-02-19 19:34:59 +000039 return self._ce.RunCommand(command)
40
asharif67973582013-02-19 20:19:40 +000041 def _DeleteCcahe(self):
42 # crosbug.com/34956
43 command = "sudo rm -rf %s" % os.path.join(self._chromeos_root, ".cache")
44 return self._ce.RunCommand(command)
45
cmtice56fb7162014-06-18 11:32:15 -070046 def _GetBuildNumber(self):
47 """ This function assumes a ChromeOS image has been built in the chroot.
48 It translates the 'latest' symlink in the
49 <chroot>/src/build/images/<board> directory, to find the actual
50 ChromeOS build number for the image that was built. For example, if
51 src/build/image/lumpy/latest -> R37-5982.0.2014_06_23_0454-a1, then
52 This function would parse it out and assign 'R37-5982' to self._build_num.
53 This is used to determine the official, vanilla build to use for
54 comparison tests.
55 """
56 # Get the path to 'latest'
57 sym_path = os.path.join (misc.GetImageDir(self._chromeos_root,
58 self._board),
59 "latest")
60 # Translate the symlink to its 'real' path.
61 real_path = os.path.realpath(sym_path)
62 # Break up the path and get the last piece
63 # (e.g. 'R37-5982.0.2014_06_23_0454-a1"
64 path_pieces = real_path.split("/")
65 last_piece = path_pieces[-1]
66 # Break this piece into the image number + other pieces, and get the
67 # image number [ 'R37-5982', '0', '2014_06_23_0454-a1']
68 image_parts = last_piece.split(".")
69 self._build_num = image_parts[0]
70
asharif96f59692013-02-16 03:13:36 +000071 def _BuildAndImage(self, label=""):
72 if (not label or
73 not misc.DoesLabelExist(self._chromeos_root, self._board, label)):
74 build_chromeos_args = [build_chromeos.__file__,
75 "--chromeos_root=%s" % self._chromeos_root,
76 "--board=%s" % self._board,
77 "--rebuild"]
asharife6b72fe2013-02-19 19:58:18 +000078 if self._public:
79 build_chromeos_args.append("--env=USE=-chrome_internal")
cmtice56fb7162014-06-18 11:32:15 -070080
cmtice23130bd2014-07-15 11:19:29 -070081 if self._board in AFDO_BOARDS:
82 build_chromeos_args.append("--env=USE=afdo_use")
83
asharif96f59692013-02-16 03:13:36 +000084 ret = build_chromeos.Main(build_chromeos_args)
cmtice7f3190b2015-05-22 14:14:51 -070085 if ret != 0:
86 raise RuntimeError("Couldn't build ChromeOS!")
cmtice56fb7162014-06-18 11:32:15 -070087
88 if not self._build_num:
89 self._GetBuildNumber()
90 # Check to see if we need to create the symbolic link for the vanilla
91 # image, and do so if appropriate.
92 if not misc.DoesLabelExist(self._chromeos_root, self._board, "vanilla"):
93 build_name = "%s-release/%s.0.0" % (self._board, self._build_num)
94 full_vanilla_path = os.path.join (os.getcwd(), self._chromeos_root,
95 'chroot/tmp', build_name)
96 misc.LabelLatestImage(self._chromeos_root, self._board, label,
97 full_vanilla_path)
98 else:
asharif96f59692013-02-16 03:13:36 +000099 misc.LabelLatestImage(self._chromeos_root, self._board, label)
100 return label
101
cmtice56fb7162014-06-18 11:32:15 -0700102 def _SetupBoard(self, env_dict, usepkg_flag, clobber_flag):
asharif96f59692013-02-16 03:13:36 +0000103 env_string = misc.GetEnvStringFromDict(env_dict)
104 command = ("%s %s" %
105 (env_string,
106 misc.GetSetupBoardCommand(self._board,
cmtice56fb7162014-06-18 11:32:15 -0700107 usepkg=usepkg_flag,
108 force=clobber_flag)))
asharif96f59692013-02-16 03:13:36 +0000109 ret = self._ce.ChrootRunCommand(self._chromeos_root,
110 command)
cmtice56fb7162014-06-18 11:32:15 -0700111 error_str = "Could not setup board: '%s'" % command
112 assert ret == 0, error_str
asharif96f59692013-02-16 03:13:36 +0000113
114 def _UnInstallToolchain(self):
115 command = ("sudo CLEAN_DELAY=0 emerge -C cross-%s/gcc" %
116 misc.GetCtargetFromBoard(self._board,
117 self._chromeos_root))
118 ret = self._ce.ChrootRunCommand(self._chromeos_root,
119 command)
cmtice7f3190b2015-05-22 14:14:51 -0700120 if ret != 0:
121 raise RuntimeError("Couldn't uninstall the toolchain!")
asharif96f59692013-02-16 03:13:36 +0000122
123 def _CheckoutChromeOS(self):
124 # TODO(asharif): Setup a fixed ChromeOS version (quarterly snapshot).
125 if not os.path.exists(self._chromeos_root):
126 setup_chromeos_args = [setup_chromeos.__file__,
Caroline Ticec35909e2013-10-02 17:13:13 -0700127 "--dir=%s" % self._chromeos_root]
asharife6b72fe2013-02-19 19:58:18 +0000128 if self._public:
129 setup_chromeos_args.append("--public")
asharif5fe40e22013-02-19 19:58:50 +0000130 ret = setup_chromeos.Main(setup_chromeos_args)
cmtice7f3190b2015-05-22 14:14:51 -0700131 if ret != 0:
132 raise RuntimeError("Couldn't run setup_chromeos!")
asharif96f59692013-02-16 03:13:36 +0000133
Caroline Ticec35909e2013-10-02 17:13:13 -0700134
asharif96f59692013-02-16 03:13:36 +0000135 def _BuildToolchain(self, config):
cmtice56fb7162014-06-18 11:32:15 -0700136 # Call setup_board for basic, vanilla setup.
137 self._SetupBoard({}, usepkg_flag=True, clobber_flag=False)
138 # Now uninstall the vanilla compiler and setup/build our custom
139 # compiler.
asharif96f59692013-02-16 03:13:36 +0000140 self._UnInstallToolchain()
cmtice56fb7162014-06-18 11:32:15 -0700141 envdict = {"USE": "git_gcc",
142 "GCC_GITHASH": config.gcc_config.githash,
143 "EMERGE_DEFAULT_OPTS": "--exclude=gcc"}
144 self._SetupBoard(envdict, usepkg_flag=False, clobber_flag=False)
asharif96f59692013-02-16 03:13:36 +0000145
146
147class ToolchainComparator(ChromeOSCheckout):
asharif58a8c9f2013-02-19 20:42:43 +0000148 def __init__(self, board, remotes, configs, clean, public, force_mismatch):
asharif96f59692013-02-16 03:13:36 +0000149 self._board = board
150 self._remotes = remotes
151 self._chromeos_root = "chromeos"
152 self._configs = configs
asharif3e38de02013-02-19 19:34:59 +0000153 self._clean = clean
asharife6b72fe2013-02-19 19:58:18 +0000154 self._public = public
asharif58a8c9f2013-02-19 20:42:43 +0000155 self._force_mismatch = force_mismatch
asharif96f59692013-02-16 03:13:36 +0000156 self._ce = command_executer.GetCommandExecuter()
157 self._l = logger.GetLogger()
cmtice7f3190b2015-05-22 14:14:51 -0700158 timestamp = datetime.datetime.strftime(datetime.datetime.now(),
159 "%Y-%m-%d_%H:%M:%S")
160 self._reports_dir = os.path.join(
161 os.path.expanduser("~/nightly_test_reports"),
162 "%s.%s" % (timestamp, board),
163 )
asharif96f59692013-02-16 03:13:36 +0000164 ChromeOSCheckout.__init__(self, board, self._chromeos_root)
165
cmticea6255d02014-01-10 10:27:22 -0800166
167 def _FinishSetup(self):
168 # Get correct .boto file
169 current_dir = os.getcwd()
170 src = "/home/mobiletc-prebuild/.boto"
171 dest = os.path.join(current_dir, self._chromeos_root,
172 "src/private-overlays/chromeos-overlay/"
173 "googlestorage_account.boto")
174 # Copy the file to the correct place
175 copy_cmd = "cp %s %s" % (src, dest)
176 retval = self._ce.RunCommand(copy_cmd)
cmtice7f3190b2015-05-22 14:14:51 -0700177 if retval != 0:
178 raise RuntimeError("Couldn't copy .boto file for google storage.")
cmticea6255d02014-01-10 10:27:22 -0800179
180 # Fix protections on ssh key
181 command = ("chmod 600 /var/cache/chromeos-cache/distfiles/target"
182 "/chrome-src-internal/src/third_party/chromite/ssh_keys"
183 "/testing_rsa")
184 retval = self._ce.ChrootRunCommand(self._chromeos_root, command)
cmtice7f3190b2015-05-22 14:14:51 -0700185 if retval != 0:
186 raise RuntimeError("chmod for testing_rsa failed")
cmticea6255d02014-01-10 10:27:22 -0800187
asharif96f59692013-02-16 03:13:36 +0000188 def _TestLabels(self, labels):
189 experiment_file = "toolchain_experiment.txt"
asharif58a8c9f2013-02-19 20:42:43 +0000190 image_args = ""
191 if self._force_mismatch:
asharif0b5d5c82013-02-19 20:42:56 +0000192 image_args = "--force-mismatch"
asharif96f59692013-02-16 03:13:36 +0000193 experiment_header = """
194 board: %s
195 remote: %s
196 """ % (self._board, self._remotes)
197 experiment_tests = """
cmtice04403882013-11-04 16:38:37 -0500198 benchmark: all_perfv2 {
199 suite: telemetry_Crosperf
cmtice6de7f8f2014-03-14 14:08:21 -0700200 iterations: 3
asharif96f59692013-02-16 03:13:36 +0000201 }
202 """
203 with open(experiment_file, "w") as f:
204 print >>f, experiment_header
205 print >>f, experiment_tests
206 for label in labels:
207 # TODO(asharif): Fix crosperf so it accepts labels with symbols
208 crosperf_label = label
209 crosperf_label = crosperf_label.replace("-", "minus")
210 crosperf_label = crosperf_label.replace("+", "plus")
asharife4a5a8f2013-02-19 20:19:33 +0000211 crosperf_label = crosperf_label.replace(".", "")
cmtice56fb7162014-06-18 11:32:15 -0700212
213 # Use the official build instead of building vanilla ourselves.
214 if label == "vanilla":
215 build_name = '%s-release/%s.0.0' % (self._board, self._build_num)
216
217 # Now add 'official build' to test file.
218 official_image = """
219 official_image {
220 chromeos_root: %s
221 build: %s
222 }
223 """ % (self._chromeos_root, build_name)
224 print >>f, official_image
225
cmtice94bc4702014-05-29 16:29:04 -0700226 else:
cmtice56fb7162014-06-18 11:32:15 -0700227 experiment_image = """
228 %s {
229 chromeos_image: %s
230 image_args: %s
231 }
232 """ % (crosperf_label,
233 os.path.join(misc.GetImageDir(self._chromeos_root,
234 self._board),
235 label, "chromiumos_test_image.bin"),
236 image_args)
237 print >>f, experiment_image
238
asharif96f59692013-02-16 03:13:36 +0000239 crosperf = os.path.join(os.path.dirname(__file__),
240 "crosperf",
241 "crosperf")
cmtice7f3190b2015-05-22 14:14:51 -0700242 command = "%s --no_email=True --results_dir=%s %s" % (crosperf, self._reports_dir,
243 experiment_file)
asharif96f59692013-02-16 03:13:36 +0000244 ret = self._ce.RunCommand(command)
cmtice7f3190b2015-05-22 14:14:51 -0700245 if ret != 0:
246 raise RuntimeError("Couldn't run crosperf!")
247 return
cmtice56fb7162014-06-18 11:32:15 -0700248
249
250 def _CopyWeeklyReportFiles(self, labels):
251 """Create tar files of the custom and official images and copy them
252 to the weekly reports directory, so they exist when the weekly report
253 gets generated. IMPORTANT NOTE: This function must run *after*
254 crosperf has been run; otherwise the vanilla images will not be there.
255 """
256 images_path = os.path.join(os.path.realpath(self._chromeos_root),
257 "src/build/images", self._board)
258 weekday = time.strftime("%a")
259 data_dir = os.path.join(WEEKLY_REPORTS_ROOT, self._board)
260 dest_dir = os.path.join (data_dir, weekday)
261 if not os.path.exists(dest_dir):
262 os.makedirs(dest_dir)
cmtice4536ef62014-07-08 11:17:21 -0700263 # Make sure dest_dir is empty (clean out last week's data).
264 cmd = "cd %s; rm -Rf %s_*_image*" % (dest_dir, weekday)
265 self._ce.RunCommand(cmd)
266 # Now create new tar files and copy them over.
cmtice56fb7162014-06-18 11:32:15 -0700267 for l in labels:
268 test_path = os.path.join(images_path, l)
269 if os.path.exists(test_path):
270 if l != "vanilla":
271 label_name = "test"
272 else:
273 label_name = "vanilla"
274 tar_file_name = "%s_%s_image.tar" % (weekday, label_name)
Han Shenfe054f12015-02-18 15:00:13 -0800275 cmd = ("cd %s; tar -cvf %s %s/chromiumos_test_image.bin; "
276 "cp %s %s/.") % (images_path,
277 tar_file_name,
278 l, tar_file_name,
279 dest_dir)
cmtice56fb7162014-06-18 11:32:15 -0700280 tar_ret = self._ce.RunCommand(cmd)
cmtice7f3190b2015-05-22 14:14:51 -0700281 if tar_ret != 0:
cmtice56fb7162014-06-18 11:32:15 -0700282 self._l.LogOutput("Error while creating/copying test tar file(%s)."
283 % tar_file_name)
284
cmtice7f3190b2015-05-22 14:14:51 -0700285 def _SendEmail(self):
286 """Find email msesage generated by crosperf and send it."""
287 filename = os.path.join(self._reports_dir, "msg_body.html")
288 if (os.path.exists(filename) and
289 os.path.exists(os.path.expanduser(MAIL_PROGRAM))):
290 commmand = ('cat %s | %s -s "Nightly test results, %s" -team -html'
291 % (filename, MAIL_PROGRAM, self._board))
292 self._ce.RunCommand(command)
asharif96f59692013-02-16 03:13:36 +0000293
294 def DoAll(self):
295 self._CheckoutChromeOS()
asharif96f59692013-02-16 03:13:36 +0000296 labels = []
cmtice56fb7162014-06-18 11:32:15 -0700297 labels.append("vanilla")
asharif96f59692013-02-16 03:13:36 +0000298 for config in self._configs:
299 label = misc.GetFilenameFromString(config.gcc_config.githash)
300 if (not misc.DoesLabelExist(self._chromeos_root,
301 self._board,
302 label)):
303 self._BuildToolchain(config)
304 label = self._BuildAndImage(label)
305 labels.append(label)
cmticea6255d02014-01-10 10:27:22 -0800306 self._FinishSetup()
cmtice7f3190b2015-05-22 14:14:51 -0700307 self._TestLabels(labels):
308 self._SendEmail()
309 # Only try to copy the image files if the test runs ran successfully.
310 self._CopyWeeklyReportFiles(labels)
asharif3e38de02013-02-19 19:34:59 +0000311 if self._clean:
312 ret = self._DeleteChroot()
cmtice7f3190b2015-05-22 14:14:51 -0700313 if ret != 0:
314 return ret
asharif67973582013-02-19 20:19:40 +0000315 ret = self._DeleteCcahe()
cmtice7f3190b2015-05-22 14:14:51 -0700316 if ret != 0:
317 return ret
asharif96f59692013-02-16 03:13:36 +0000318 return 0
319
320
321def Main(argv):
322 """The main function."""
323 # Common initializations
324### command_executer.InitCommandExecuter(True)
325 command_executer.InitCommandExecuter()
326 parser = optparse.OptionParser()
327 parser.add_option("--remote",
328 dest="remote",
329 help="Remote machines to run tests on.")
330 parser.add_option("--board",
331 dest="board",
332 default="x86-zgb",
333 help="The target board.")
334 parser.add_option("--githashes",
335 dest="githashes",
336 default="master",
337 help="The gcc githashes to test.")
asharif3e38de02013-02-19 19:34:59 +0000338 parser.add_option("--clean",
339 dest="clean",
340 default=False,
341 action="store_true",
342 help="Clean the chroot after testing.")
asharife6b72fe2013-02-19 19:58:18 +0000343 parser.add_option("--public",
344 dest="public",
345 default=False,
346 action="store_true",
347 help="Use the public checkout/build.")
asharif58a8c9f2013-02-19 20:42:43 +0000348 parser.add_option("--force-mismatch",
349 dest="force_mismatch",
350 default="",
351 help="Force the image regardless of board mismatch")
asharif96f59692013-02-16 03:13:36 +0000352 options, _ = parser.parse_args(argv)
353 if not options.board:
354 print "Please give a board."
355 return 1
356 if not options.remote:
357 print "Please give at least one remote machine."
358 return 1
359 toolchain_configs = []
360 for githash in options.githashes.split(","):
361 gcc_config = GCCConfig(githash=githash)
362 toolchain_config = ToolchainConfig(gcc_config=gcc_config)
363 toolchain_configs.append(toolchain_config)
asharif3e38de02013-02-19 19:34:59 +0000364 fc = ToolchainComparator(options.board, options.remote, toolchain_configs,
asharif58a8c9f2013-02-19 20:42:43 +0000365 options.clean, options.public,
366 options.force_mismatch)
asharif96f59692013-02-16 03:13:36 +0000367 return fc.DoAll()
368
369
370if __name__ == "__main__":
371 retval = Main(sys.argv)
372 sys.exit(retval)