#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2019 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Performs bisection on LLVM based off a .JSON file.""" from __future__ import print_function import os import subprocess import sys import time import traceback import chroot from llvm_bisection import BisectionExitStatus import llvm_bisection from update_all_tryjobs_with_auto import GetPathToUpdateAllTryjobsWithAutoScript # Used to re-try for 'llvm_bisection.py' to attempt to launch more tryjobs. BISECTION_RETRY_TIME_SECS = 10 * 60 # Wait time to then poll each tryjob whose 'status' value is 'pending'. POLL_RETRY_TIME_SECS = 30 * 60 # The number of attempts for 'llvm_bisection.py' to launch more tryjobs. # # It is reset (break out of the `for` loop/ exit the program) if successfully # launched more tryjobs or bisection is finished (no more revisions between # start and end of the bisection). BISECTION_ATTEMPTS = 3 # The limit for updating all tryjobs whose 'status' is 'pending'. # # If the time that has passed for polling exceeds this value, then the program # will exit with the appropriate exit code. POLLING_LIMIT_SECS = 18 * 60 * 60 def main(): """Bisects LLVM using the result of `cros buildresult` of each tryjob. Raises: AssertionError: The script was run inside the chroot. """ chroot.VerifyOutsideChroot() args_output = llvm_bisection.GetCommandLineArgs() exec_update_tryjobs = [ GetPathToUpdateAllTryjobsWithAutoScript(), '--chroot_path', args_output.chroot_path, '--last_tested', args_output.last_tested ] if os.path.isfile(args_output.last_tested): print('Resuming bisection for %s' % args_output.last_tested) else: print('Starting a new bisection for %s' % args_output.last_tested) while True: if os.path.isfile(args_output.last_tested): update_start_time = time.time() # Update all tryjobs whose status is 'pending' to the result of `cros # buildresult`. while True: print('\nAttempting to update all tryjobs whose "status" is ' '"pending":') print('-' * 40) update_ret = subprocess.call(exec_update_tryjobs) print('-' * 40) # Successfully updated all tryjobs whose 'status' was 'pending'/ no # updates were needed (all tryjobs already have been updated). if update_ret == 0: break delta_time = time.time() - update_start_time if delta_time > POLLING_LIMIT_SECS: print('Unable to update tryjobs whose status is "pending" to ' 'the result of `cros buildresult`.') # Something is wrong with updating the tryjobs's 'status' via # `cros buildresult` (e.g. network issue, etc.). sys.exit(1) print('Sleeping for %d minutes.' % (POLL_RETRY_TIME_SECS // 60)) time.sleep(POLL_RETRY_TIME_SECS) # Launch more tryjobs if possible to narrow down the bad commit/revision or # terminate the bisection because the bad commit/revision was found. for cur_try in range(1, BISECTION_ATTEMPTS + 1): try: print('\nAttempting to launch more tryjobs if possible:') print('-' * 40) bisection_ret = llvm_bisection.main(args_output) print('-' * 40) # Exit code 126 means that there are no more revisions to test between # 'start' and 'end', so bisection is complete. if bisection_ret == BisectionExitStatus.BISECTION_COMPLETE.value: sys.exit(0) # Successfully launched more tryjobs. break except Exception: traceback.print_exc() print('-' * 40) # Exceeded the number of times to launch more tryjobs. if cur_try == BISECTION_ATTEMPTS: print('Unable to continue bisection.') sys.exit(1) num_retries_left = BISECTION_ATTEMPTS - cur_try print('Retries left to continue bisection %d.' % num_retries_left) print('Sleeping for %d minutes.' % (BISECTION_RETRY_TIME_SECS // 60)) time.sleep(BISECTION_RETRY_TIME_SECS) if __name__ == '__main__': main()