aboutsummaryrefslogtreecommitdiff
path: root/llvm_tools/auto_llvm_bisection.py
blob: cd3d70b6e9f6a9e33eb0763facb6d7a4a916ba8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/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

from assert_not_in_chroot import VerifyOutsideChroot
from update_all_tryjobs_with_auto import GetPathToUpdateAllTryjobsWithAutoScript
from llvm_bisection import BisectionExitStatus
import llvm_bisection

# 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.
  """

  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()