blob: 36b82510259c12dd3a9016bd4f3e851a3af3ca2b [file] [log] [blame]
Kuang-che Wu32f27242019-05-16 17:34:50 +08001#!/usr/bin/env python2
2# -*- coding: utf-8 -*-
3# Copyright 2019 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6"""Diagnose ChromeOS tast regressions.
7
8This is integrated bisection utility. Given ChromeOS, Chrome, Android source
9tree, and necessary parameters, this script can determine which components to
10bisect, and hopefully output the culprit CL of regression.
11
12Sometimes the script failed to figure out the final CL for various reasons, it
13will cut down the search range as narrow as it can.
14"""
15from __future__ import print_function
Kuang-che Wu32f27242019-05-16 17:34:50 +080016import logging
Kuang-che Wu568c84d2019-11-06 17:23:53 +080017import os
Kuang-che Wu32f27242019-05-16 17:34:50 +080018
19from bisect_kit import cros_lab_util
20from bisect_kit import cros_util
Kuang-che Wu568c84d2019-11-06 17:23:53 +080021from bisect_kit import cr_util
Kuang-che Wu32f27242019-05-16 17:34:50 +080022from bisect_kit import diagnoser_cros
23from bisect_kit import errors
24import setup_cros_bisect
25
26logger = logging.getLogger(__name__)
27
Kuang-che Wu32f27242019-05-16 17:34:50 +080028
29class DiagnoseTastCommandLine(diagnoser_cros.DiagnoseCommandLineBase):
30 """Diagnose command line interface."""
31
32 def check_options(self, opts, path_factory):
33 super(DiagnoseTastCommandLine, self).check_options(opts, path_factory)
34 if not opts.test_name:
35 self.argument_parser.error('argument --test_name is required')
36
37 def init_hook(self, opts):
38 pass
39
40 def _build_cmds(self):
41 common_eval_cmd = [
42 './eval_cros_tast.py',
Kuang-che Wu4fc0f1c2019-08-02 21:56:49 +080043 '--with_private_bundles',
Kuang-che Wu32f27242019-05-16 17:34:50 +080044 '--chromeos_root', self.config['chromeos_root'],
45 '--test_name', self.config['test_name'],
46 ] # yapf: disable
47 if self.config['metric']:
48 common_eval_cmd += [
49 '--metric', self.config['metric'],
50 ] # yapf: disable
51 if self.config['fail_to_pass']:
52 common_eval_cmd.append('--fail_to_pass')
53 if self.config['reboot_before_test']:
54 common_eval_cmd.append('--reboot_before_test')
55
56 return common_eval_cmd
57
58 def cmd_run(self, opts):
59 del opts # unused
60
61 self.states.load()
62
63 try:
64 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
65 self.config['mirror_base'], self.config['work_base'],
66 self.config['session'])
67 common_eval_cmd = self._build_cmds()
68
Kuang-che Wu568c84d2019-11-06 17:23:53 +080069 chrome_src = os.path.join(self.config['chrome_root'], 'src')
70 chrome_binaries = (
71 cr_util.CHROME_BINARIES + cr_util.get_chrome_test_binaries(
72 chrome_src, self.config['board']))
Kuang-che Wu32f27242019-05-16 17:34:50 +080073
Kuang-che Wu0c9b7942019-10-30 16:55:39 +080074 with cros_lab_util.dut_manager(
75 self.config['dut'],
76 lambda: diagnoser_cros.grab_dut(self.config)) as dut:
Kuang-che Wu32f27242019-05-16 17:34:50 +080077 if not dut:
78 raise errors.NoDutAvailable('unable to allocate DUT')
Kuang-che Wu7cb08df2019-06-04 19:12:29 +080079 if not cros_util.is_good_dut(dut):
80 if not cros_lab_util.repair(dut):
81 raise errors.ExternalError('Not a good DUT and unable to repair')
Kuang-che Wu32f27242019-05-16 17:34:50 +080082 assert cros_util.is_dut(dut)
83 if self.config['dut'] == cros_lab_util.LAB_DUT:
84 self.config['allocated_dut'] = dut
85 self.states.save()
86 common_eval_cmd.append(dut)
87
88 diagnoser = diagnoser_cros.CrosDiagnoser(self.states, path_factory,
89 self.config, dut)
90
91 diagnoser.narrow_down_chromeos_prebuilt(
92 self.config['old'], self.config['new'], common_eval_cmd)
93
94 diagnoser.switch_chromeos_to_old(force=self.config['always_reflash'])
Kuang-che Wuc8c495d2019-08-19 17:48:58 +080095 dut_os_version = cros_util.query_dut_short_version(dut)
Kuang-che Wu32f27242019-05-16 17:34:50 +080096
97 try:
98 if diagnoser.narrow_down_android(common_eval_cmd):
99 return
100 except errors.DiagnoseContradiction:
101 raise
102 except Exception:
103 logger.exception('exception in android bisector before verification; '
104 'assume culprit is not inside android and continue')
105 # Assume it's ok to leave random version of android prebuilt on DUT.
106
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800107 # Sanity check. The OS version should not change after android bisect.
Kuang-che Wu523bdf22019-08-20 12:11:09 +0800108 assert dut_os_version == cros_util.query_dut_short_version(dut), \
109 'Someone else reflashed the DUT. ' \
110 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800111
Kuang-che Wu32f27242019-05-16 17:34:50 +0800112 try:
Kuang-che Wude5bfc32019-09-12 21:56:48 +0800113 if self.config['chrome_deploy_image']:
114 eval_cmd = common_eval_cmd + ['--tast_build']
115 else:
116 eval_cmd = common_eval_cmd
Kuang-che Wu32f27242019-05-16 17:34:50 +0800117 if diagnoser.narrow_down_chrome(
Kuang-che Wude5bfc32019-09-12 21:56:48 +0800118 eval_cmd, chrome_binaries=chrome_binaries):
Kuang-che Wu32f27242019-05-16 17:34:50 +0800119 return
120 except errors.DiagnoseContradiction:
121 raise
122 except Exception:
123 logger.exception('exception in chrome bisector before verification; '
124 'assume culprit is not inside chrome and continue')
125
Kuang-che Wude5bfc32019-09-12 21:56:48 +0800126 if not self.config['chrome_deploy_image']:
127 # Sanity check. The OS version should not change after chrome bisect.
128 assert dut_os_version == cros_util.query_dut_short_version(dut), \
129 'Someone else reflashed the DUT. ' \
130 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800131
Kuang-che Wu32f27242019-05-16 17:34:50 +0800132 eval_cmd = common_eval_cmd + ['--tast_build']
133 diagnoser.narrow_down_chromeos_localbuild(eval_cmd)
134 logger.info('%s done', __file__)
135 except Exception as e:
136 logger.exception('got exception; stop')
137 exception_name = e.__class__.__name__
138 self.states.add_history(
139 'failed', '%s: %s' % (exception_name, e), exception=exception_name)
140
141
142if __name__ == '__main__':
143 DiagnoseTastCommandLine().main()