1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright 2019 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7"""Performs bisection on LLVM based off a .JSON file.""" 8 9from __future__ import print_function 10 11import os 12import subprocess 13import sys 14import time 15import traceback 16 17import chroot 18from llvm_bisection import BisectionExitStatus 19import llvm_bisection 20from update_all_tryjobs_with_auto import GetPathToUpdateAllTryjobsWithAutoScript 21 22# Used to re-try for 'llvm_bisection.py' to attempt to launch more tryjobs. 23BISECTION_RETRY_TIME_SECS = 10 * 60 24 25# Wait time to then poll each tryjob whose 'status' value is 'pending'. 26POLL_RETRY_TIME_SECS = 30 * 60 27 28# The number of attempts for 'llvm_bisection.py' to launch more tryjobs. 29# 30# It is reset (break out of the `for` loop/ exit the program) if successfully 31# launched more tryjobs or bisection is finished (no more revisions between 32# start and end of the bisection). 33BISECTION_ATTEMPTS = 3 34 35# The limit for updating all tryjobs whose 'status' is 'pending'. 36# 37# If the time that has passed for polling exceeds this value, then the program 38# will exit with the appropriate exit code. 39POLLING_LIMIT_SECS = 18 * 60 * 60 40 41 42def main(): 43 """Bisects LLVM using the result of `cros buildresult` of each tryjob. 44 45 Raises: 46 AssertionError: The script was run inside the chroot. 47 """ 48 49 chroot.VerifyOutsideChroot() 50 51 args_output = llvm_bisection.GetCommandLineArgs() 52 53 exec_update_tryjobs = [ 54 GetPathToUpdateAllTryjobsWithAutoScript(), '--chroot_path', 55 args_output.chroot_path, '--last_tested', args_output.last_tested 56 ] 57 58 if os.path.isfile(args_output.last_tested): 59 print('Resuming bisection for %s' % args_output.last_tested) 60 else: 61 print('Starting a new bisection for %s' % args_output.last_tested) 62 63 while True: 64 if os.path.isfile(args_output.last_tested): 65 update_start_time = time.time() 66 67 # Update all tryjobs whose status is 'pending' to the result of `cros 68 # buildresult`. 69 while True: 70 print('\nAttempting to update all tryjobs whose "status" is ' 71 '"pending":') 72 print('-' * 40) 73 74 update_ret = subprocess.call(exec_update_tryjobs) 75 76 print('-' * 40) 77 78 # Successfully updated all tryjobs whose 'status' was 'pending'/ no 79 # updates were needed (all tryjobs already have been updated). 80 if update_ret == 0: 81 break 82 83 delta_time = time.time() - update_start_time 84 85 if delta_time > POLLING_LIMIT_SECS: 86 print('Unable to update tryjobs whose status is "pending" to ' 87 'the result of `cros buildresult`.') 88 89 # Something is wrong with updating the tryjobs's 'status' via 90 # `cros buildresult` (e.g. network issue, etc.). 91 sys.exit(1) 92 93 print('Sleeping for %d minutes.' % (POLL_RETRY_TIME_SECS // 60)) 94 time.sleep(POLL_RETRY_TIME_SECS) 95 96 # Launch more tryjobs if possible to narrow down the bad commit/revision or 97 # terminate the bisection because the bad commit/revision was found. 98 for cur_try in range(1, BISECTION_ATTEMPTS + 1): 99 try: 100 print('\nAttempting to launch more tryjobs if possible:') 101 print('-' * 40) 102 103 bisection_ret = llvm_bisection.main(args_output) 104 105 print('-' * 40) 106 107 # Exit code 126 means that there are no more revisions to test between 108 # 'start' and 'end', so bisection is complete. 109 if bisection_ret == BisectionExitStatus.BISECTION_COMPLETE.value: 110 sys.exit(0) 111 112 # Successfully launched more tryjobs. 113 break 114 except Exception: 115 traceback.print_exc() 116 117 print('-' * 40) 118 119 # Exceeded the number of times to launch more tryjobs. 120 if cur_try == BISECTION_ATTEMPTS: 121 print('Unable to continue bisection.') 122 123 sys.exit(1) 124 125 num_retries_left = BISECTION_ATTEMPTS - cur_try 126 127 print('Retries left to continue bisection %d.' % num_retries_left) 128 129 print('Sleeping for %d minutes.' % (BISECTION_RETRY_TIME_SECS // 60)) 130 time.sleep(BISECTION_RETRY_TIME_SECS) 131 132 133if __name__ == '__main__': 134 main() 135