OLD | NEW |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import base64 | 5 import base64 |
6 import collections | 6 import collections |
7 import json | 7 import json |
8 | 8 |
| 9 from auto_bisect import bisect_exceptions |
9 | 10 |
10 def perform_bisect(api, **flags): | 11 def perform_bisect(api, **flags): |
11 # Try catch all the exceptions thrown in bisection so that recipe can | 12 # Try catch all the exceptions thrown in bisection so that recipe can |
12 # post the failed job result to dashboard | 13 # post the failed job result to dashboard |
13 try: | 14 try: |
14 bisect_attempts = [] | 15 bisect_attempts = [] |
15 if api.m.chromium.c.TARGET_PLATFORM != 'android': | 16 if api.m.chromium.c.TARGET_PLATFORM != 'android': |
16 _perform_single_bisect(api, bisect_attempts, **flags) | 17 _perform_single_bisect(api, bisect_attempts, **flags) |
17 else: | 18 else: |
18 # pick an available device if targe platform is android | 19 # pick an available device if targe platform is android |
19 connected_devices = _get_connected_devices(api) | 20 connected_devices = _get_connected_devices(api) |
20 if not connected_devices: | 21 if not connected_devices: |
21 raise api.m.step.StepFailure( | 22 raise api.m.step.StepFailure( |
22 'No Android test devices are available') | 23 'No Android test devices are available') |
23 for device in connected_devices: | 24 for device in connected_devices: |
24 api.m.bisect_tester.device_to_test = device | 25 api.m.bisect_tester.device_to_test = device |
25 try: | 26 try: |
26 _perform_single_bisect(api, bisect_attempts, **flags) | 27 _perform_single_bisect(api, bisect_attempts, **flags) |
27 break | 28 break |
28 except api.m.step.StepFailure: | 29 except api.m.step.StepFailure: |
29 # Redo the bisect job if target platform is android and bisect | 30 # Redo the bisect job if target platform is android and bisect |
30 # failed because the test device disconnected | 31 # failed because the test device disconnected |
31 current_connected_devices = _get_connected_devices(api) | 32 current_connected_devices = _get_connected_devices(api) |
32 if (api.m.bisect_tester.device_to_test not in | 33 if (api.m.bisect_tester.device_to_test not in |
33 current_connected_devices): | 34 current_connected_devices): |
34 continue | 35 continue |
35 else: | 36 else: |
36 raise | 37 raise |
37 except: # pylint: disable=bare-except | 38 except bisect_exceptions.InconclusiveBisectException: |
38 if bisect_attempts: | 39 if bisect_attempts: |
39 bisect_attempts[-1].post_result(halt_on_failure=True) | 40 bisect_attempts[-1].post_result() |
| 41 raise api.m.step.StepFailure('Bisect cannot identify a culprit') |
| 42 except Exception: # pylint: disable=bare-except |
| 43 if bisect_attempts: |
| 44 bisect_attempts[-1].post_result() |
40 raise | 45 raise |
41 | 46 |
42 def _perform_single_bisect(api, bisect_attempts, **flags): | 47 def _perform_single_bisect(api, bisect_attempts, **flags): |
43 bisect_config = dict(api.m.properties.get('bisect_config')) | 48 bisect_config = dict(api.m.properties.get('bisect_config')) |
44 if bisect_attempts: | 49 if bisect_attempts: |
45 bisect_config['good_revision'] = bisect_attempts[-1].lkgr.commit_hash | 50 bisect_config['good_revision'] = bisect_attempts[-1].lkgr.commit_hash |
46 bisect_config['bad_revision'] = bisect_attempts[-1].fkbr.commit_hash | 51 bisect_config['bad_revision'] = bisect_attempts[-1].fkbr.commit_hash |
47 bisector = api.create_bisector(bisect_config, **flags) | 52 bisector = api.create_bisector(bisect_config, **flags) |
48 bisect_attempts.append(bisector) | 53 bisect_attempts.append(bisector) |
49 with api.m.step.nest('Gathering reference values'): | 54 with api.m.step.nest('Gathering reference values'): |
50 _gather_reference_range(api, bisector) | 55 _gather_reference_range(bisector) |
51 if (not bisector.failed and bisector.check_improvement_direction() and | 56 if (not bisector.failed and bisector.check_improvement_direction() and |
52 bisector.check_initial_confidence()): | 57 bisector.check_initial_confidence()): |
| 58 bisector.compute_relative_change() |
53 if bisector.check_reach_adjacent_revision( | 59 if bisector.check_reach_adjacent_revision( |
54 bisector.good_rev): # pragma: no cover | 60 bisector.good_rev): # pragma: no cover |
55 # Only show this step if bisect has reached adjacent revisions. | 61 # Only show this step if bisect has reached adjacent revisions. |
56 with api.m.step.nest(str('Check bisect finished on revision ' + | 62 with api.m.step.nest(str('Check bisect finished on revision ' + |
57 bisector.good_rev.revision_string())): | 63 bisector.good_rev.revision_string())): |
58 if bisector.check_bisect_finished(bisector.good_rev): | 64 if bisector.check_bisect_finished(bisector.good_rev): |
59 bisector.bisect_over = True | 65 bisector.bisect_over = True |
60 if not bisector.bisect_over: | 66 if not bisector.bisect_over: |
61 _bisect_main_loop(bisector) | 67 _bisect_main_loop(bisector) |
62 else: | 68 else: |
63 bisector.bisect_over = True | 69 bisector.bisect_over = True |
64 bisector.print_result_debug_info() | 70 bisector.print_result_debug_info() |
65 bisector.post_result(halt_on_failure=True) | 71 bisector.post_result(halt_on_failure=True) |
66 | 72 |
67 def _get_connected_devices(api): | 73 def _get_connected_devices(api): |
68 api.m.chromium_android.device_status() | 74 api.m.chromium_android.device_status() |
69 return api.m.chromium_android.devices | 75 return api.m.chromium_android.devices |
70 | 76 |
71 def _gather_reference_range(api, bisector): # pragma: no cover | 77 def _gather_reference_range(bisector): # pragma: no cover |
72 bisector.good_rev.start_job() | 78 bisector.good_rev.start_job() |
73 bisector.bad_rev.start_job() | 79 bisector.bad_rev.start_job() |
74 bisector.wait_for_all([bisector.good_rev, bisector.bad_rev]) | |
75 if bisector.good_rev.failed: | 80 if bisector.good_rev.failed: |
76 bisector.surface_result('REF_RANGE_FAIL') | 81 bisector.surface_result('REF_RANGE_FAIL') |
77 api.m.halt('Testing the "good" revision failed') | |
78 bisector.failed = True | 82 bisector.failed = True |
| 83 raise bisect_exceptions.InconclusiveBisectException( |
| 84 'Testing the "good" revision failed') |
79 elif bisector.bad_rev.failed: | 85 elif bisector.bad_rev.failed: |
80 bisector.surface_result('REF_RANGE_FAIL') | 86 bisector.surface_result('REF_RANGE_FAIL') |
81 api.m.halt('Testing the "bad" revision failed') | |
82 bisector.failed = True | 87 bisector.failed = True |
83 api.m.halt('Testing the "good" revision failed') | 88 raise bisect_exceptions.InconclusiveBisectException( |
84 else: | 89 'Testing the "bad" revision failed') |
85 bisector.compute_relative_change() | |
86 | |
87 | 90 |
88 def _bisect_main_loop(bisector): # pragma: no cover | 91 def _bisect_main_loop(bisector): # pragma: no cover |
89 """This is the main bisect loop. | 92 """This is the main bisect loop. |
90 | 93 |
91 It gets an evenly distributed number of revisions in the candidate range, | 94 It gets an evenly distributed number of revisions in the candidate range, |
92 then it starts them in parallel and waits for them to finish. | 95 then it starts them in parallel and waits for them to finish. |
93 """ | 96 """ |
94 while not bisector.bisect_over: | 97 while not bisector.bisect_over: |
95 revision_to_check = bisector.get_revision_to_eval() | 98 revision_to_check = bisector.get_revision_to_eval() |
96 if not revision_to_check: | 99 if not revision_to_check: |
97 bisector.bisect_over = True | 100 bisector.bisect_over = True |
98 break | 101 break |
99 | 102 |
100 with bisector.api.m.step.nest(str('Working on revision ' + | 103 with bisector.api.m.step.nest(str('Working on revision ' + |
101 revision_to_check.revision_string())): | 104 revision_to_check.revision_string())): |
102 bisector.post_result(halt_on_failure=False) | 105 bisector.post_result(halt_on_failure=False) |
103 revision_to_check.start_job() | 106 revision_to_check.start_job() |
104 bisector.wait_for(revision_to_check) | |
105 | 107 |
106 if bisector.check_reach_adjacent_revision(revision_to_check): | 108 if bisector.check_reach_adjacent_revision(revision_to_check): |
107 # Only show this step if bisect has reached adjacent revisions. | 109 # Only show this step if bisect has reached adjacent revisions. |
108 with bisector.api.m.step.nest( | 110 with bisector.api.m.step.nest( |
109 str('Check bisect finished on revision ' + | 111 str('Check bisect finished on revision ' + |
110 revision_to_check.revision_string())): | 112 revision_to_check.revision_string())): |
111 if bisector.check_bisect_finished(revision_to_check): | 113 if bisector.check_bisect_finished(revision_to_check): |
112 bisector.bisect_over = True | 114 bisector.bisect_over = True |
OLD | NEW |