blob: 9e6301d03aba6dfb9c294417a16fcf596e87c9e8 [file] [log] [blame]
Mike Frysingerc7f15932013-03-20 13:43:35 -04001#!/usr/bin/python
kbaclawski20082a02013-02-16 02:12:57 +00002#
3# Copyright 2010 Google Inc. All Rights Reserved.
4
asharif265cda32013-02-15 21:57:02 +00005import datetime
asharif265cda32013-02-15 21:57:02 +00006import optparse
7import os
8import smtplib
9import sys
10import time
kbaclawski20082a02013-02-16 02:12:57 +000011from email.mime.text import MIMEText
12
asharif265cda32013-02-15 21:57:02 +000013from autotest_gatherer import AutotestGatherer as AutotestGatherer
14from autotest_run import AutotestRun as AutotestRun
15from machine_manager_singleton import MachineManagerSingleton as MachineManagerSingleton
16from utils import logger
raymes6623db62013-02-15 22:10:10 +000017from utils.file_utils import FileUtils
asharif265cda32013-02-15 21:57:02 +000018
kbaclawski20082a02013-02-16 02:12:57 +000019
asharif265cda32013-02-15 21:57:02 +000020def CanonicalizeChromeOSRoot(chromeos_root):
21 chromeos_root = os.path.expanduser(chromeos_root)
22 if os.path.isfile(os.path.join(chromeos_root,
23 "src/scripts/enter_chroot.sh")):
24 return chromeos_root
25 else:
26 return None
27
28
29class Autotest(object):
30 def __init__(self, autotest_string):
31 self.name = None
32 self.iterations = None
33 self.args = None
34 fields = autotest_string.split(",", 1)
35 self.name = fields[0]
36 if len(fields) > 1:
37 autotest_string = fields[1]
38 fields = autotest_string.split(",", 1)
39 else: return
40 self.iterations = int(fields[0])
41 if len(fields) > 1:
42 self.args = fields[1]
43 else: return
44
45 def __str__(self):
46 return "\n".join([self.name, self.iterations, self.args])
47
48
49def CreateAutotestListFromString(autotest_strings, default_iterations=None):
50 autotest_list = []
51 for autotest_string in autotest_strings.split(":"):
52 autotest = Autotest(autotest_string)
53 if default_iterations and not autotest.iterations:
54 autotest.iterations = default_iterations
55
56 autotest_list.append(autotest)
57 return autotest_list
58
59
60def CreateAutotestRuns(images, autotests, remote, board, exact_remote,
61 rerun, rerun_if_failed, main_chromeos_root=None):
62 autotest_runs = []
63 for image in images:
64 logger.GetLogger().LogOutput("Computing md5sum of: %s" % image)
raymes6623db62013-02-15 22:10:10 +000065 image_checksum = FileUtils().Md5File(image)
asharif265cda32013-02-15 21:57:02 +000066 logger.GetLogger().LogOutput("md5sum %s: %s" % (image, image_checksum))
67### image_checksum = "abcdefghi"
68
69 chromeos_root = main_chromeos_root
70 if not main_chromeos_root:
71 image_chromeos_root = os.path.join(os.path.dirname(image),
72 "../../../../..")
73 chromeos_root = CanonicalizeChromeOSRoot(image_chromeos_root)
74 assert chromeos_root, "chromeos_root: %s invalid" % image_chromeos_root
75 else:
76 chromeos_root = CanonicalizeChromeOSRoot(main_chromeos_root)
77 assert chromeos_root, "chromeos_root: %s invalid" % main_chromeos_root
78
79 # We just need a single ChromeOS root in the MachineManagerSingleton. It is
80 # needed because we can save re-image time by checking the image checksum at
81 # the beginning and assigning autotests to machines appropriately.
82 if not MachineManagerSingleton().chromeos_root:
83 MachineManagerSingleton().chromeos_root = chromeos_root
84
85 for autotest in autotests:
86 for iteration in range(autotest.iterations):
87 autotest_run = AutotestRun(autotest,
88 chromeos_root=chromeos_root,
89 chromeos_image=image,
90 board=board,
91 remote=remote,
92 iteration=iteration,
93 image_checksum=image_checksum,
94 exact_remote=exact_remote,
95 rerun=rerun,
96 rerun_if_failed=rerun_if_failed)
97 autotest_runs.append(autotest_run)
98 return autotest_runs
99
100
101def GetNamesAndIterations(autotest_runs):
102 strings = []
103 for autotest_run in autotest_runs:
104 strings.append("%s:%s" % (autotest_run.autotest.name,
105 autotest_run.iteration))
106 return " %s (%s)" % (len(strings), " ".join(strings))
107
108
109def GetStatusString(autotest_runs):
110 status_bins = {}
111 for autotest_run in autotest_runs:
112 if autotest_run.status not in status_bins:
113 status_bins[autotest_run.status] = []
114 status_bins[autotest_run.status].append(autotest_run)
115
116 status_strings = []
117 for key, val in status_bins.items():
118 status_strings.append("%s: %s" % (key, GetNamesAndIterations(val)))
119 return "Thread Status:\n%s" % "\n".join(status_strings)
120
121
122def GetProgressBar(num_done, num_total):
raymes6623db62013-02-15 22:10:10 +0000123 ret = "Done: %s%%" % int(100.0 * num_done / num_total)
asharif265cda32013-02-15 21:57:02 +0000124 bar_length = 50
125 done_char = ">"
126 undone_char = " "
127 num_done_chars = bar_length * num_done / num_total
128 num_undone_chars = bar_length - num_done_chars
129 ret += " [%s%s]" % (num_done_chars * done_char, num_undone_chars *
130 undone_char)
131 return ret
132
133
134def GetProgressString(start_time, num_remain, num_total):
135 current_time = time.time()
136 elapsed_time = current_time - start_time
137 try:
138 eta_seconds = float(num_remain) * elapsed_time / (num_total - num_remain)
139 eta_seconds = int(eta_seconds)
140 eta = datetime.timedelta(seconds=eta_seconds)
141 except ZeroDivisionError:
142 eta = "Unknown"
143 strings = []
144 strings.append("Current time: %s Elapsed: %s ETA: %s" %
145 (datetime.datetime.now(),
146 datetime.timedelta(seconds=int(elapsed_time)),
147 eta))
148 strings.append(GetProgressBar(num_total - num_remain, num_total))
149 return "\n".join(strings)
150
151
152def RunAutotestRunsInParallel(autotest_runs):
153 start_time = time.time()
154 active_threads = []
155 for autotest_run in autotest_runs:
156 # Set threads to daemon so program exits when ctrl-c is pressed.
157 autotest_run.daemon = True
158 autotest_run.start()
159 active_threads.append(autotest_run)
160
161 print_interval = 30
162 last_printed_time = time.time()
163 while active_threads:
164 try:
165 active_threads = [t for t in active_threads if t is not None
166 and t.isAlive()]
167 for t in active_threads:
168 t.join(1)
169 if time.time() - last_printed_time > print_interval:
170 border = "=============================="
171 logger.GetLogger().LogOutput(border)
172 logger.GetLogger().LogOutput(GetProgressString(
173 start_time,
174 len([t for t in autotest_runs if t.status not in ["SUCCEEDED",
175 "FAILED"]]),
176 len(autotest_runs)))
177 logger.GetLogger().LogOutput(GetStatusString(autotest_runs))
178 logger.GetLogger().LogOutput("%s\n" %
179 MachineManagerSingleton().AsString())
180 logger.GetLogger().LogOutput(border)
181 last_printed_time = time.time()
182 except KeyboardInterrupt:
183 print "C-c received... cleaning up threads."
184 for t in active_threads:
185 t.terminate = True
186 return 1
187 return 0
188
189
190def RunAutotestRunsSerially(autotest_runs):
191 for autotest_run in autotest_runs:
192 retval = autotest_run.Run()
193 if retval: return retval
194
195
196def ProduceTables(autotest_runs, full_table, fit_string):
197 l = logger.GetLogger()
198 ags_dict = {}
199 for autotest_run in autotest_runs:
200 name = autotest_run.full_name
201 if name not in ags_dict:
202 ags_dict[name] = AutotestGatherer()
203 ags_dict[name].runs.append(autotest_run)
204 output = ""
205 for b, ag in ags_dict.items():
206 output += "Benchmark: %s\n" % b
207 output += ag.GetFormattedMainTable(percents_only=not full_table,
208 fit_string=fit_string)
209 output += "\n"
210
211 summary = ""
212 for b, ag in ags_dict.items():
213 summary += "Benchmark Summary Table: %s\n" % b
214 summary += ag.GetFormattedSummaryTable(percents_only=not full_table,
215 fit_string=fit_string)
216 summary += "\n"
217
218 output += summary
219 output += ("Number of re-images performed: %s" %
220 MachineManagerSingleton().num_reimages)
221 l.LogOutput(output)
222
223 if autotest_runs:
224 board = autotest_runs[0].board
225 else:
226 board = ""
227
228 subject = "%s: %s" % (board, ", ".join(ags_dict.keys()))
229
230 if any(autotest_run.run_completed for autotest_run in autotest_runs):
231 SendEmailToUser(subject, summary)
232
233
234def SendEmailToUser(subject, text_to_send):
235 # Email summary to the current user.
236 msg = MIMEText(text_to_send)
237
238 # me == the sender's email address
239 # you == the recipient's email address
240 me = os.path.basename(__file__)
241 you = os.getlogin()
242 msg["Subject"] = "[%s] %s" % (os.path.basename(__file__), subject)
243 msg["From"] = me
244 msg["To"] = you
245
246 # Send the message via our own SMTP server, but don't include the
247 # envelope header.
248 s = smtplib.SMTP("localhost")
249 s.sendmail(me, [you], msg.as_string())
250 s.quit()
251
252
253def Main(argv):
254 """The main function."""
255 # Common initializations
256### command_executer.InitCommandExecuter(True)
257 l = logger.GetLogger()
258
259 parser = optparse.OptionParser()
260 parser.add_option("-t", "--tests", dest="tests",
261 help=("Tests to compare."
262 "Optionally specify per-test iterations by:"
263 "<test>,<iter>:<args>"))
264 parser.add_option("-c", "--chromeos_root", dest="chromeos_root",
265 help="A *single* chromeos_root where scripts can be found.")
266 parser.add_option("-n", "--iterations", dest="iterations",
267 help="Iterations to run per benchmark.",
268 default=1)
269 parser.add_option("-r", "--remote", dest="remote",
270 help="The remote chromeos machine.")
271 parser.add_option("-b", "--board", dest="board",
272 help="The remote board.")
273 parser.add_option("--full_table", dest="full_table",
274 help="Print full tables.",
275 action="store_true",
276 default=True)
277 parser.add_option("--exact_remote",
278 dest="exact_remote",
279 help="Run tests on the exact remote.",
280 action="store_true",
281 default=False)
282 parser.add_option("--fit_string", dest="fit_string",
283 help="Fit strings to fixed sizes.",
284 action="store_true",
285 default=False)
286 parser.add_option("--rerun",
287 dest="rerun",
288 help="Re-run regardless of cache hit.",
289 action="store_true",
290 default=False)
291 parser.add_option("--rerun_if_failed",
292 dest="rerun_if_failed",
293 help="Re-run if previous run was a failure.",
294 action="store_true",
295 default=False)
296 parser.add_option("--no_lock",
297 dest="no_lock",
298 help="Do not lock the machine before running the tests.",
299 action="store_true",
300 default=False)
301 l.LogOutput(" ".join(argv))
302 [options, args] = parser.parse_args(argv)
303
304 if options.remote is None:
305 l.LogError("No remote machine specified.")
306 parser.print_help()
307 return 1
308
309 if not options.board:
310 l.LogError("No board specified.")
311 parser.print_help()
312 return 1
313
314 remote = options.remote
315 tests = options.tests
316 board = options.board
317 exact_remote = options.exact_remote
318 iterations = int(options.iterations)
319
320 autotests = CreateAutotestListFromString(tests, iterations)
321
322 main_chromeos_root = options.chromeos_root
323 images = args[1:]
324 fit_string = options.fit_string
325 full_table = options.full_table
326 rerun = options.rerun
327 rerun_if_failed = options.rerun_if_failed
328
329 MachineManagerSingleton().no_lock = options.no_lock
330
331 # Now try creating all the Autotests
332 autotest_runs = CreateAutotestRuns(images, autotests, remote, board,
333 exact_remote, rerun, rerun_if_failed,
334 main_chromeos_root)
335
336 try:
337 # At this point we have all the autotest runs.
338 for machine in remote.split(","):
339 MachineManagerSingleton().AddMachine(machine)
340
341 retval = RunAutotestRunsInParallel(autotest_runs)
342 if retval: return retval
343
344 # Now print tables
345 ProduceTables(autotest_runs, full_table, fit_string)
346 finally:
347 # not sure why this isn't called at the end normally...
348 MachineManagerSingleton().__del__()
349
350 return 0
351
352if __name__ == "__main__":
353 sys.exit(Main(sys.argv))