| 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 . import 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 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 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 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 |