blob: 8c767fb8e59aaa56b36cc4085984b13106a5833c [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
Caroline Ticea8af9a72016-07-20 12:52:59 -070016from cros_utils import logger
17from cros_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)
Luis Lozanof2a3ef42015-12-15 13:49:30 -080022 if os.path.isfile(os.path.join(chromeos_root, 'src/scripts/enter_chroot.sh')):
asharif265cda32013-02-15 21:57:02 +000023 return chromeos_root
24 else:
25 return None
26
27
28class Autotest(object):
Luis Lozanof2a3ef42015-12-15 13:49:30 -080029
asharif265cda32013-02-15 21:57:02 +000030 def __init__(self, autotest_string):
31 self.name = None
32 self.iterations = None
33 self.args = None
Luis Lozanof2a3ef42015-12-15 13:49:30 -080034 fields = autotest_string.split(',', 1)
asharif265cda32013-02-15 21:57:02 +000035 self.name = fields[0]
36 if len(fields) > 1:
37 autotest_string = fields[1]
Luis Lozanof2a3ef42015-12-15 13:49:30 -080038 fields = autotest_string.split(',', 1)
39 else:
40 return
asharif265cda32013-02-15 21:57:02 +000041 self.iterations = int(fields[0])
42 if len(fields) > 1:
43 self.args = fields[1]
Luis Lozanof2a3ef42015-12-15 13:49:30 -080044 else:
45 return
asharif265cda32013-02-15 21:57:02 +000046
47 def __str__(self):
Luis Lozanof2a3ef42015-12-15 13:49:30 -080048 return '\n'.join([self.name, self.iterations, self.args])
asharif265cda32013-02-15 21:57:02 +000049
50
51def CreateAutotestListFromString(autotest_strings, default_iterations=None):
52 autotest_list = []
Luis Lozanof2a3ef42015-12-15 13:49:30 -080053 for autotest_string in autotest_strings.split(':'):
asharif265cda32013-02-15 21:57:02 +000054 autotest = Autotest(autotest_string)
55 if default_iterations and not autotest.iterations:
56 autotest.iterations = default_iterations
57
58 autotest_list.append(autotest)
59 return autotest_list
60
61
Luis Lozanof2a3ef42015-12-15 13:49:30 -080062def CreateAutotestRuns(images,
63 autotests,
64 remote,
65 board,
66 exact_remote,
67 rerun,
68 rerun_if_failed,
69 main_chromeos_root=None):
asharif265cda32013-02-15 21:57:02 +000070 autotest_runs = []
71 for image in images:
Luis Lozanof2a3ef42015-12-15 13:49:30 -080072 logger.GetLogger().LogOutput('Computing md5sum of: %s' % image)
raymes6623db62013-02-15 22:10:10 +000073 image_checksum = FileUtils().Md5File(image)
Luis Lozanof2a3ef42015-12-15 13:49:30 -080074 logger.GetLogger().LogOutput('md5sum %s: %s' % (image, image_checksum))
75 ### image_checksum = "abcdefghi"
asharif265cda32013-02-15 21:57:02 +000076
77 chromeos_root = main_chromeos_root
78 if not main_chromeos_root:
Luis Lozanof2a3ef42015-12-15 13:49:30 -080079 image_chromeos_root = os.path.join(
80 os.path.dirname(image), '../../../../..')
asharif265cda32013-02-15 21:57:02 +000081 chromeos_root = CanonicalizeChromeOSRoot(image_chromeos_root)
Luis Lozanof2a3ef42015-12-15 13:49:30 -080082 assert chromeos_root, 'chromeos_root: %s invalid' % image_chromeos_root
asharif265cda32013-02-15 21:57:02 +000083 else:
84 chromeos_root = CanonicalizeChromeOSRoot(main_chromeos_root)
Luis Lozanof2a3ef42015-12-15 13:49:30 -080085 assert chromeos_root, 'chromeos_root: %s invalid' % main_chromeos_root
asharif265cda32013-02-15 21:57:02 +000086
87 # We just need a single ChromeOS root in the MachineManagerSingleton. It is
88 # needed because we can save re-image time by checking the image checksum at
89 # the beginning and assigning autotests to machines appropriately.
90 if not MachineManagerSingleton().chromeos_root:
91 MachineManagerSingleton().chromeos_root = chromeos_root
92
93 for autotest in autotests:
94 for iteration in range(autotest.iterations):
95 autotest_run = AutotestRun(autotest,
96 chromeos_root=chromeos_root,
97 chromeos_image=image,
98 board=board,
99 remote=remote,
100 iteration=iteration,
101 image_checksum=image_checksum,
102 exact_remote=exact_remote,
103 rerun=rerun,
104 rerun_if_failed=rerun_if_failed)
105 autotest_runs.append(autotest_run)
106 return autotest_runs
107
108
109def GetNamesAndIterations(autotest_runs):
110 strings = []
111 for autotest_run in autotest_runs:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800112 strings.append('%s:%s' % (autotest_run.autotest.name,
asharif265cda32013-02-15 21:57:02 +0000113 autotest_run.iteration))
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800114 return ' %s (%s)' % (len(strings), ' '.join(strings))
asharif265cda32013-02-15 21:57:02 +0000115
116
117def GetStatusString(autotest_runs):
118 status_bins = {}
119 for autotest_run in autotest_runs:
120 if autotest_run.status not in status_bins:
121 status_bins[autotest_run.status] = []
122 status_bins[autotest_run.status].append(autotest_run)
123
124 status_strings = []
125 for key, val in status_bins.items():
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800126 status_strings.append('%s: %s' % (key, GetNamesAndIterations(val)))
127 return 'Thread Status:\n%s' % '\n'.join(status_strings)
asharif265cda32013-02-15 21:57:02 +0000128
129
130def GetProgressBar(num_done, num_total):
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800131 ret = 'Done: %s%%' % int(100.0 * num_done / num_total)
asharif265cda32013-02-15 21:57:02 +0000132 bar_length = 50
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800133 done_char = '>'
134 undone_char = ' '
asharif265cda32013-02-15 21:57:02 +0000135 num_done_chars = bar_length * num_done / num_total
136 num_undone_chars = bar_length - num_done_chars
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800137 ret += ' [%s%s]' % (num_done_chars * done_char,
138 num_undone_chars * undone_char)
asharif265cda32013-02-15 21:57:02 +0000139 return ret
140
141
142def GetProgressString(start_time, num_remain, num_total):
143 current_time = time.time()
144 elapsed_time = current_time - start_time
145 try:
146 eta_seconds = float(num_remain) * elapsed_time / (num_total - num_remain)
147 eta_seconds = int(eta_seconds)
148 eta = datetime.timedelta(seconds=eta_seconds)
149 except ZeroDivisionError:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800150 eta = 'Unknown'
asharif265cda32013-02-15 21:57:02 +0000151 strings = []
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800152 strings.append('Current time: %s Elapsed: %s ETA: %s' %
153 (datetime.datetime.now(),
154 datetime.timedelta(seconds=int(elapsed_time)), eta))
asharif265cda32013-02-15 21:57:02 +0000155 strings.append(GetProgressBar(num_total - num_remain, num_total))
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800156 return '\n'.join(strings)
asharif265cda32013-02-15 21:57:02 +0000157
158
159def RunAutotestRunsInParallel(autotest_runs):
160 start_time = time.time()
161 active_threads = []
162 for autotest_run in autotest_runs:
163 # Set threads to daemon so program exits when ctrl-c is pressed.
164 autotest_run.daemon = True
165 autotest_run.start()
166 active_threads.append(autotest_run)
167
168 print_interval = 30
169 last_printed_time = time.time()
170 while active_threads:
171 try:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800172 active_threads = [t for t in active_threads
173 if t is not None and t.isAlive()]
asharif265cda32013-02-15 21:57:02 +0000174 for t in active_threads:
175 t.join(1)
176 if time.time() - last_printed_time > print_interval:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800177 border = '=============================='
asharif265cda32013-02-15 21:57:02 +0000178 logger.GetLogger().LogOutput(border)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800179 logger.GetLogger().LogOutput(GetProgressString(start_time, len(
180 [t for t in autotest_runs if t.status not in ['SUCCEEDED', 'FAILED']
181 ]), len(autotest_runs)))
asharif265cda32013-02-15 21:57:02 +0000182 logger.GetLogger().LogOutput(GetStatusString(autotest_runs))
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800183 logger.GetLogger().LogOutput('%s\n' %
asharif265cda32013-02-15 21:57:02 +0000184 MachineManagerSingleton().AsString())
185 logger.GetLogger().LogOutput(border)
186 last_printed_time = time.time()
187 except KeyboardInterrupt:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800188 print 'C-c received... cleaning up threads.'
asharif265cda32013-02-15 21:57:02 +0000189 for t in active_threads:
190 t.terminate = True
191 return 1
192 return 0
193
194
195def RunAutotestRunsSerially(autotest_runs):
196 for autotest_run in autotest_runs:
197 retval = autotest_run.Run()
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800198 if retval:
199 return retval
asharif265cda32013-02-15 21:57:02 +0000200
201
202def ProduceTables(autotest_runs, full_table, fit_string):
203 l = logger.GetLogger()
204 ags_dict = {}
205 for autotest_run in autotest_runs:
206 name = autotest_run.full_name
207 if name not in ags_dict:
208 ags_dict[name] = AutotestGatherer()
209 ags_dict[name].runs.append(autotest_run)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800210 output = ''
asharif265cda32013-02-15 21:57:02 +0000211 for b, ag in ags_dict.items():
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800212 output += 'Benchmark: %s\n' % b
asharif265cda32013-02-15 21:57:02 +0000213 output += ag.GetFormattedMainTable(percents_only=not full_table,
214 fit_string=fit_string)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800215 output += '\n'
asharif265cda32013-02-15 21:57:02 +0000216
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800217 summary = ''
asharif265cda32013-02-15 21:57:02 +0000218 for b, ag in ags_dict.items():
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800219 summary += 'Benchmark Summary Table: %s\n' % b
asharif265cda32013-02-15 21:57:02 +0000220 summary += ag.GetFormattedSummaryTable(percents_only=not full_table,
221 fit_string=fit_string)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800222 summary += '\n'
asharif265cda32013-02-15 21:57:02 +0000223
224 output += summary
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800225 output += ('Number of re-images performed: %s' %
asharif265cda32013-02-15 21:57:02 +0000226 MachineManagerSingleton().num_reimages)
227 l.LogOutput(output)
228
229 if autotest_runs:
230 board = autotest_runs[0].board
231 else:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800232 board = ''
asharif265cda32013-02-15 21:57:02 +0000233
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800234 subject = '%s: %s' % (board, ', '.join(ags_dict.keys()))
asharif265cda32013-02-15 21:57:02 +0000235
236 if any(autotest_run.run_completed for autotest_run in autotest_runs):
237 SendEmailToUser(subject, summary)
238
239
240def SendEmailToUser(subject, text_to_send):
241 # Email summary to the current user.
242 msg = MIMEText(text_to_send)
243
244 # me == the sender's email address
245 # you == the recipient's email address
246 me = os.path.basename(__file__)
247 you = os.getlogin()
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800248 msg['Subject'] = '[%s] %s' % (os.path.basename(__file__), subject)
249 msg['From'] = me
250 msg['To'] = you
asharif265cda32013-02-15 21:57:02 +0000251
252 # Send the message via our own SMTP server, but don't include the
253 # envelope header.
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800254 s = smtplib.SMTP('localhost')
asharif265cda32013-02-15 21:57:02 +0000255 s.sendmail(me, [you], msg.as_string())
256 s.quit()
257
258
259def Main(argv):
260 """The main function."""
261 # Common initializations
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800262 ### command_executer.InitCommandExecuter(True)
asharif265cda32013-02-15 21:57:02 +0000263 l = logger.GetLogger()
264
265 parser = optparse.OptionParser()
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800266 parser.add_option('-t',
267 '--tests',
268 dest='tests',
269 help=('Tests to compare.'
270 'Optionally specify per-test iterations by:'
271 '<test>,<iter>:<args>'))
272 parser.add_option('-c',
273 '--chromeos_root',
274 dest='chromeos_root',
275 help='A *single* chromeos_root where scripts can be found.')
276 parser.add_option('-n',
277 '--iterations',
278 dest='iterations',
279 help='Iterations to run per benchmark.',
asharif265cda32013-02-15 21:57:02 +0000280 default=1)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800281 parser.add_option('-r',
282 '--remote',
283 dest='remote',
284 help='The remote chromeos machine.')
285 parser.add_option('-b', '--board', dest='board', help='The remote board.')
286 parser.add_option('--full_table',
287 dest='full_table',
288 help='Print full tables.',
289 action='store_true',
asharif265cda32013-02-15 21:57:02 +0000290 default=True)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800291 parser.add_option('--exact_remote',
292 dest='exact_remote',
293 help='Run tests on the exact remote.',
294 action='store_true',
asharif265cda32013-02-15 21:57:02 +0000295 default=False)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800296 parser.add_option('--fit_string',
297 dest='fit_string',
298 help='Fit strings to fixed sizes.',
299 action='store_true',
asharif265cda32013-02-15 21:57:02 +0000300 default=False)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800301 parser.add_option('--rerun',
302 dest='rerun',
303 help='Re-run regardless of cache hit.',
304 action='store_true',
asharif265cda32013-02-15 21:57:02 +0000305 default=False)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800306 parser.add_option('--rerun_if_failed',
307 dest='rerun_if_failed',
308 help='Re-run if previous run was a failure.',
309 action='store_true',
asharif265cda32013-02-15 21:57:02 +0000310 default=False)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800311 parser.add_option('--no_lock',
312 dest='no_lock',
313 help='Do not lock the machine before running the tests.',
314 action='store_true',
asharif265cda32013-02-15 21:57:02 +0000315 default=False)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800316 l.LogOutput(' '.join(argv))
asharif265cda32013-02-15 21:57:02 +0000317 [options, args] = parser.parse_args(argv)
318
319 if options.remote is None:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800320 l.LogError('No remote machine specified.')
asharif265cda32013-02-15 21:57:02 +0000321 parser.print_help()
322 return 1
323
324 if not options.board:
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800325 l.LogError('No board specified.')
asharif265cda32013-02-15 21:57:02 +0000326 parser.print_help()
327 return 1
328
329 remote = options.remote
330 tests = options.tests
331 board = options.board
332 exact_remote = options.exact_remote
333 iterations = int(options.iterations)
334
335 autotests = CreateAutotestListFromString(tests, iterations)
336
337 main_chromeos_root = options.chromeos_root
338 images = args[1:]
339 fit_string = options.fit_string
340 full_table = options.full_table
341 rerun = options.rerun
342 rerun_if_failed = options.rerun_if_failed
343
344 MachineManagerSingleton().no_lock = options.no_lock
345
346 # Now try creating all the Autotests
347 autotest_runs = CreateAutotestRuns(images, autotests, remote, board,
348 exact_remote, rerun, rerun_if_failed,
349 main_chromeos_root)
350
351 try:
352 # At this point we have all the autotest runs.
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800353 for machine in remote.split(','):
asharif265cda32013-02-15 21:57:02 +0000354 MachineManagerSingleton().AddMachine(machine)
355
356 retval = RunAutotestRunsInParallel(autotest_runs)
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800357 if retval:
358 return retval
asharif265cda32013-02-15 21:57:02 +0000359
360 # Now print tables
361 ProduceTables(autotest_runs, full_table, fit_string)
362 finally:
363 # not sure why this isn't called at the end normally...
364 MachineManagerSingleton().__del__()
365
366 return 0
367
Luis Lozanof2a3ef42015-12-15 13:49:30 -0800368
369if __name__ == '__main__':
asharif265cda32013-02-15 21:57:02 +0000370 sys.exit(Main(sys.argv))