crb_driver.py revision c7f1593f9af3ea1b9264b37628c36f3a70e1749a
1#!/usr/bin/python 2# 3# Copyright 2010 Google Inc. All Rights Reserved. 4 5import datetime 6import optparse 7import os 8import smtplib 9import sys 10import time 11from email.mime.text import MIMEText 12 13from 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 17from utils.file_utils import FileUtils 18 19 20def 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) 65 image_checksum = FileUtils().Md5File(image) 66 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): 123 ret = "Done: %s%%" % int(100.0 * num_done / num_total) 124 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)) 354