| #!/usr/bin/env python3 |
| # Copyright 2019 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Diagnose ChromeOS tast regressions. |
| |
| This is integrated bisection utility. Given ChromeOS, Chrome, Android source |
| tree, and necessary parameters, this script can determine which components to |
| bisect, and hopefully output the culprit CL of regression. |
| |
| Sometimes the script failed to figure out the final CL for various reasons, it |
| will cut down the search range as narrow as it can. |
| """ |
| |
| import logging |
| |
| from bisect_kit import cros_util |
| from bisect_kit import diagnoser_cros |
| from bisect_kit import dut_manager as dut_manager_module |
| from bisect_kit import errors |
| from bisect_kit import util |
| import experiment |
| |
| |
| logger = logging.getLogger(__name__) |
| |
| |
| class DiagnoseTastCommandLine(diagnoser_cros.DiagnoseCommandLineBase): |
| """Diagnose command line interface.""" |
| |
| def check_options(self, opts): |
| super().check_options(opts) |
| if not opts.test_name: |
| self.argument_parser.error('argument --test-name is required') |
| |
| def init_hook(self, opts): |
| pass |
| |
| def _build_cmds(self): |
| # prebuilt version will be specified later. |
| switch_test_harness_cmd = [ |
| './switch_tast_prebuilt.py', |
| '--rich-result', |
| '--chromeos-root', |
| self.config['chromeos_root'], |
| ] |
| common_eval_cmd = [ |
| './eval_cros_tast.py', |
| '--rich-result', |
| '--with-private-bundles', |
| '--chromeos-root', |
| self.config['chromeos_root'], |
| '--test-name', |
| self.config['test_name'], |
| ] |
| if self.config['metric']: |
| common_eval_cmd += [ |
| '--metric', |
| self.config['metric'], |
| ] |
| if self.config['fail_to_pass']: |
| common_eval_cmd.append('--fail-to-pass') |
| if self.config['reboot_before_test']: |
| common_eval_cmd.append('--reboot-before-test') |
| for var in self.config['extra_test_variables']: |
| common_eval_cmd.append('--args') |
| common_eval_cmd.append(var) |
| if self.config['consider_test_crash_as_failure']: |
| common_eval_cmd.append('--consider-test-crash-as-failure') |
| |
| return switch_test_harness_cmd, common_eval_cmd |
| |
| def do_run(self, dut): |
| diagnoser = diagnoser_cros.CrosDiagnoser(self.states, self.config, dut) |
| |
| switch_test_harness_cmd, common_eval_cmd = self._build_cmds() |
| |
| cros_prebuilt_eval_cmd = common_eval_cmd[:] |
| lacros_prebuilt_eval_cmd = ( |
| common_eval_cmd |
| + ['--args'] |
| + ['lacros.DeployedBinary=/usr/local/lacros-chrome'] |
| ) |
| android_prebuilt_eval_cmd = common_eval_cmd |
| if self.config['chrome_deploy_image']: |
| chrome_localbuild_eval_cmd = common_eval_cmd + ['--tast-build'] |
| else: |
| chrome_localbuild_eval_cmd = common_eval_cmd + ['--prebuilt'] |
| |
| chrome_localbuild_eval_cmd = ( |
| chrome_localbuild_eval_cmd |
| + ['--args'] |
| + ['lacros.DeployedBinary=/usr/local/lacros-chrome'] |
| ) |
| |
| cros_buildbucket_build = ( |
| cros_util.is_buildbucket_buildable(self.config['cros_prebuilt_old']) |
| and not self.config['disable_buildbucket_chromeos'] |
| ) |
| if not cros_buildbucket_build: |
| cros_localbuild_eval_cmd = common_eval_cmd + ['--tast-build'] |
| else: |
| cros_localbuild_eval_cmd = common_eval_cmd + ['--prebuilt'] |
| |
| diagnoser.diagnose( |
| is_autotest=False, |
| switch_test_harness_cmd=switch_test_harness_cmd, |
| cros_prebuilt_eval_cmd=cros_prebuilt_eval_cmd, |
| android_prebuilt_eval_cmd=android_prebuilt_eval_cmd, |
| lacros_prebuilt_eval_cmd=lacros_prebuilt_eval_cmd, |
| chrome_localbuild_eval_cmd=chrome_localbuild_eval_cmd, |
| should_build_chrome_localbuild_with_tests=True, |
| cros_localbuild_eval_cmd=cros_localbuild_eval_cmd, |
| ) |
| |
| @util.MethodTimer( |
| diagnoser_cros.DiagnoseCommandLineBase.write_total_execution_time |
| ) |
| def cmd_run(self, opts): |
| del opts # unused |
| |
| self.states.load_states() |
| |
| try: |
| # Allocate a DUT globally only if not performing a stateless bisect. |
| logger.info('experiments: %s', self.config['experiments']) |
| is_vm_board = cros_util.is_vm_board( |
| self.states.dut_allocate_spec.board |
| ) |
| should_auto_allocate = ( |
| not experiment.is_in_experiment( |
| self.config.get('experiments'), experiment.ID.STATELESS |
| ) |
| and not is_vm_board |
| ) |
| dut_manager = dut_manager_module.DutManager( |
| 'Global', |
| self.states, |
| self.config['dut'], |
| should_auto_allocate, |
| should_force_monitoring=not is_vm_board, |
| ) |
| with dut_manager.provision() as dut: |
| self.do_run(dut) |
| logger.info('%s done', __file__) |
| except Exception as e: |
| logger.exception('got exception; stop') |
| exception_name = e.__class__.__name__ |
| self.states.add_history( |
| 'failed', |
| text='%s: %s' % (exception_name, e), |
| exception=exception_name, |
| error_type=errors.error_type(e), |
| ) |
| |
| |
| if __name__ == '__main__': |
| DiagnoseTastCommandLine().main() |