blob: 7be67bffd15e3e1c1a8f04d0fc49ec02cda0328b [file] [log] [blame]
Kuang-che Wu875c89a2020-01-08 14:30:55 +08001#!/usr/bin/env python3
Kuang-che Wu1fcc0222018-07-07 16:43:22 +08002# -*- coding: utf-8 -*-
3# Copyright 2018 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 autotest regressions.
7
8This is integrated bisection utility. Given ChromeOS, Chrome, Android source
Kuang-che Wu927231f2018-07-24 14:21:56 +08009tree, and necessary parameters, this script can determine which components to
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080010bisect, 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 Wu1fcc0222018-07-07 16:43:22 +080016import logging
17import os
18
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080019from bisect_kit import cros_lab_util
20from bisect_kit import cros_util
21from bisect_kit import diagnoser_cros
Kuang-che Wue121fae2018-11-09 16:18:39 +080022from bisect_kit import errors
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080023from bisect_kit import util
Kuang-che Wud8fc9572018-10-03 21:00:41 +080024import setup_cros_bisect
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080025
26logger = logging.getLogger(__name__)
27
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080028
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080029def get_test_dependency_labels(config):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080030 # Assume "DEPENDENCIES" is identical between the period of
31 # `old` and `new` version.
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +080032 autotest_dir = os.path.join(config['chromeos_root'],
33 cros_util.prebuilt_autotest_dir)
Kuang-che Wua41525a2018-10-17 23:52:24 +080034 info = cros_util.get_autotest_test_info(autotest_dir, config['test_name'])
Kuang-che Wu0f1c3b02019-01-10 01:21:01 +080035 assert info, 'incorrect test name? %s' % config['test_name']
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080036
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080037 extra_labels = []
38 dependencies = info.variables.get('DEPENDENCIES', '')
39 for label in dependencies.split(','):
40 label = label.strip()
41 # Skip non-machine labels
42 if not label or label in ['cleanup-reboot']:
43 continue
44 extra_labels.append(label)
45
46 return extra_labels
47
48
Kuang-che Wu32f27242019-05-16 17:34:50 +080049class DiagnoseAutotestCommandLine(diagnoser_cros.DiagnoseCommandLineBase):
Kuang-che Wua41525a2018-10-17 23:52:24 +080050 """Diagnose command line interface."""
Kuang-che Wud8fc9572018-10-03 21:00:41 +080051
Kuang-che Wua41525a2018-10-17 23:52:24 +080052 def check_options(self, opts, path_factory):
Kuang-che Wu32f27242019-05-16 17:34:50 +080053 super(DiagnoseAutotestCommandLine, self).check_options(opts, path_factory)
Kuang-che Wua41525a2018-10-17 23:52:24 +080054
Kuang-che Wu85c613c2019-01-09 15:46:11 +080055 is_cts = (
56 opts.cts_revision or opts.cts_abi or opts.cts_prefix or
57 opts.cts_module or opts.cts_test or opts.cts_timeout)
58 if is_cts:
59 if opts.test_name or opts.metric or opts.args:
60 self.argument_parser.error(
61 'do not specify --test_name, --metric, --args for CTS/GTS tests')
Kuang-che Wub91b1512019-05-29 15:16:29 +080062 if not opts.cts_prefix:
63 self.argument_parser.error(
64 '--cts_prefix should be specified for CTS/GTS tests')
65 if not opts.cts_module:
66 self.argument_parser.error(
67 '--cts_module should be specified for CTS/GTS tests')
Kuang-che Wu85c613c2019-01-09 15:46:11 +080068 opts.test_name = '%s.tradefed-run-test' % opts.cts_prefix
Kuang-che Wu32f27242019-05-16 17:34:50 +080069 elif not opts.test_name:
70 self.argument_parser.error(
71 '--test_name should be specified if not CTS/GTS tests')
Kuang-che Wu85c613c2019-01-09 15:46:11 +080072
Kuang-che Wu32f27242019-05-16 17:34:50 +080073 def init_hook(self, opts):
74 self.states.config.update(
Kuang-che Wu85c613c2019-01-09 15:46:11 +080075 cts_revision=opts.cts_revision,
76 cts_abi=opts.cts_abi,
77 cts_prefix=opts.cts_prefix,
78 cts_module=opts.cts_module,
79 cts_test=opts.cts_test,
80 cts_timeout=opts.cts_timeout,
Kuang-che Wua41525a2018-10-17 23:52:24 +080081 test_that_args=opts.args,
Kuang-che Wua41525a2018-10-17 23:52:24 +080082 )
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080083
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +080084 # Unpack old autotest prebuilt, assume following information don't change
85 # between versions:
86 # - what chrome binaries to run
87 # - dependency labels for DUT allocation
88 common_switch_cmd, _common_eval_cmd = self._build_cmds()
89 util.check_call(*(common_switch_cmd + [self.config['old']]))
90
Kuang-che Wua41525a2018-10-17 23:52:24 +080091 def _build_cmds(self):
92 # prebuilt version will be specified later.
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +080093 common_switch_cmd = [
Kuang-che Wua41525a2018-10-17 23:52:24 +080094 './switch_autotest_prebuilt.py',
Kuang-che Wu99e808f2019-06-26 12:17:32 +080095 '--chromeos_root',
96 self.config['chromeos_root'],
97 '--board',
98 self.config['board'],
99 ]
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800100 if self.config['test_name'] and not self.config['cts_test']:
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800101 common_switch_cmd += ['--test_name', self.config['test_name']]
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800102
Kuang-che Wua41525a2018-10-17 23:52:24 +0800103 common_eval_cmd = [
104 './eval_cros_autotest.py',
105 '--chromeos_root', self.config['chromeos_root'],
Kuang-che Wua41525a2018-10-17 23:52:24 +0800106 ] # yapf: disable
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800107 if self.config['test_name'] and not self.config['cts_test']:
108 common_eval_cmd += ['--test_name', self.config['test_name']]
Kuang-che Wua41525a2018-10-17 23:52:24 +0800109 if self.config['metric']:
110 common_eval_cmd += [
111 '--metric', self.config['metric'],
Kuang-che Wua41525a2018-10-17 23:52:24 +0800112 ] # yapf: disable
Kuang-che Wu32f27242019-05-16 17:34:50 +0800113 if self.config['fail_to_pass']:
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800114 common_eval_cmd.append('--fail_to_pass')
Kuang-che Wuda3abfe2019-03-21 14:48:12 +0800115 if self.config['reboot_before_test']:
116 common_eval_cmd.append('--reboot_before_test')
Kuang-che Wua41525a2018-10-17 23:52:24 +0800117 if self.config['test_that_args']:
118 common_eval_cmd += ['--args', self.config['test_that_args']]
Kuang-che Wud4603d72018-11-29 17:51:21 +0800119 if self.config['test_name'].startswith('telemetry_'):
120 common_eval_cmd += ['--chrome_root', self.config['chrome_root']]
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800121
122 for arg_name in [
123 'cts_revision', 'cts_abi', 'cts_prefix', 'cts_module', 'cts_test',
124 'cts_timeout'
125 ]:
126 if self.config.get(arg_name) is not None:
127 common_eval_cmd += ['--%s' % arg_name, str(self.config[arg_name])]
128
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800129 return common_switch_cmd, common_eval_cmd
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800130
Kuang-che Wua41525a2018-10-17 23:52:24 +0800131 def cmd_run(self, opts):
132 del opts # unused
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800133
Kuang-che Wua41525a2018-10-17 23:52:24 +0800134 self.states.load()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800135
Kuang-che Wu8b654092018-11-09 17:56:25 +0800136 try:
137 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
138 self.config['mirror_base'], self.config['work_base'],
139 self.config['session'])
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800140 common_switch_cmd, common_eval_cmd = self._build_cmds()
Kuang-che Wu8b654092018-11-09 17:56:25 +0800141
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800142 with cros_lab_util.dut_manager(
143 self.config['dut'],
144 lambda: diagnoser_cros.grab_dut(self.config)) as dut:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800145 if not dut:
146 raise errors.NoDutAvailable('unable to allocate DUT')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800147
Kuang-che Wu7cb08df2019-06-04 19:12:29 +0800148 if not cros_util.is_good_dut(dut):
149 if not cros_lab_util.repair(dut):
150 raise errors.ExternalError('Not a good DUT and unable to repair')
Kuang-che Wu8b654092018-11-09 17:56:25 +0800151 if self.config['dut'] == cros_lab_util.LAB_DUT:
152 self.config['allocated_dut'] = dut
153 self.states.save()
154 common_eval_cmd.append(dut)
155
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800156 util.check_call('./switch_cros_localbuild.py', '--nobuild',
157 '--chromeos_root', self.config['chromeos_root'],
158 '--chromeos_mirror', self.config['chromeos_mirror'],
Kuang-che Wu0ebbf7c2019-08-28 18:19:19 +0800159 '--board', self.config['board'], self.config['new'])
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800160
Kuang-che Wu186071a2019-03-28 17:11:41 +0800161 diagnoser = diagnoser_cros.CrosDiagnoser(self.states, path_factory,
162 self.config, dut)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800163
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800164 eval_cmd = common_eval_cmd + ['--prebuilt']
Kuang-che Wu8b654092018-11-09 17:56:25 +0800165 # Do not specify version for autotest prebuilt switching here. The trick
166 # is that version number is obtained via bisector's environment variable
167 # CROS_VERSION.
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800168 extra_switch_cmd = common_switch_cmd
Kuang-che Wu8b654092018-11-09 17:56:25 +0800169 diagnoser.narrow_down_chromeos_prebuilt(
170 self.config['old'],
171 self.config['new'],
172 eval_cmd,
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800173 extra_switch_cmd=extra_switch_cmd)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800174
175 diagnoser.switch_chromeos_to_old(force=self.config['always_reflash'])
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800176 util.check_call(*(common_switch_cmd + [diagnoser.cros_old]))
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800177 dut_os_version = cros_util.query_dut_short_version(dut)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800178
179 try:
180 if diagnoser.narrow_down_android(eval_cmd):
181 return
Kuang-che Wuec566f32019-03-07 16:53:32 +0800182 except errors.DiagnoseContradiction:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800183 raise
184 except Exception:
185 logger.exception('exception in android bisector before verification; '
186 'assume culprit is not inside android and continue')
187 # Assume it's ok to leave random version of android prebuilt on DUT.
188
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800189 # Sanity check. The OS version should not change after android bisect.
Kuang-che Wu523bdf22019-08-20 12:11:09 +0800190 assert dut_os_version == cros_util.query_dut_short_version(dut), \
191 'Someone else reflashed the DUT. ' \
192 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800193
Kuang-che Wu8b654092018-11-09 17:56:25 +0800194 eval_cmd = common_eval_cmd + ['--prebuilt']
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800195
Kuang-che Wuda9cb702019-11-11 14:40:56 +0800196 chrome_with_tests = bool(self.config['test_name']) # not CTS/GTS
197 if chrome_with_tests:
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800198 # Now, the version of autotest on the DUT is unknown and may be even
199 # not installed. Invoke the test once here, so
200 # - make sure autotest-deps is installed, with expected version
Kuang-che Wuda9cb702019-11-11 14:40:56 +0800201 # - autotest-deps is installed first, so our chrome test binaries
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800202 # won't be reset to default version during bisection.
203 # It's acceptable to spend extra time to run test once because
204 # - only few tests do so
205 # - tests are migrating away from autotest
206 util.call(*eval_cmd)
207
Kuang-che Wu8b654092018-11-09 17:56:25 +0800208 try:
209 if diagnoser.narrow_down_chrome(
Kuang-che Wuda9cb702019-11-11 14:40:56 +0800210 eval_cmd, with_tests=chrome_with_tests):
Kuang-che Wu8b654092018-11-09 17:56:25 +0800211 return
Kuang-che Wuec566f32019-03-07 16:53:32 +0800212 except errors.DiagnoseContradiction:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800213 raise
214 except Exception:
215 logger.exception('exception in chrome bisector before verification; '
216 'assume culprit is not inside chrome and continue')
217
Kuang-che Wude5bfc32019-09-12 21:56:48 +0800218 if not self.config['chrome_deploy_image']:
219 # Sanity check. The OS version should not change after chrome bisect.
220 assert dut_os_version == cros_util.query_dut_short_version(dut), \
221 'Someone else reflashed the DUT. ' \
222 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800223
Zheng-Jie Chang181be6f2020-03-17 16:16:08 +0800224 if self.config['buildbucket_build_chromeos']:
225 eval_cmd = common_eval_cmd + ['--prebuilt']
226 extra_switch_cmd = common_switch_cmd
227 else:
228 eval_cmd = common_eval_cmd
229 extra_switch_cmd = None
230 diagnoser.narrow_down_chromeos_localbuild(
231 eval_cmd, extra_switch_cmd=extra_switch_cmd)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800232 logger.info('%s done', __file__)
233 except Exception as e:
234 logger.exception('got exception; stop')
235 exception_name = e.__class__.__name__
236 self.states.add_history(
237 'failed', '%s: %s' % (exception_name, e), exception=exception_name)
238
Kuang-che Wu32f27242019-05-16 17:34:50 +0800239 def create_argument_parser_hook(self, parser_init):
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800240 group = parser_init.add_argument_group(title='Options for CTS/GTS tests')
241 group.add_argument('--cts_revision', help='CTS revision, like "9.0_r3"')
Kuang-che Wu63f836a2019-02-21 16:33:32 +0000242 group.add_argument('--cts_abi', choices=['arm', 'x86'])
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800243 group.add_argument(
244 '--cts_prefix',
245 help='Prefix of autotest test name, '
246 'like cheets_CTS_N, cheets_CTS_P, cheets_GTS')
247 group.add_argument(
248 '--cts_module', help='CTS/GTS module name, like "CtsCameraTestCases"')
249 group.add_argument(
250 '--cts_test',
251 help='CTS/GTS test name, like '
252 '"android.hardware.cts.CameraTest#testDisplayOrientation"')
253 group.add_argument('--cts_timeout', type=float, help='timeout, in seconds')
254
Kuang-che Wua41525a2018-10-17 23:52:24 +0800255 group = parser_init.add_argument_group(title='Options passed to test_that')
256 group.add_argument(
257 '--args',
258 help='Extra args passed to "test_that --args"; Overrides the default')
259
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800260
261if __name__ == '__main__':
Kuang-che Wu32f27242019-05-16 17:34:50 +0800262 DiagnoseAutotestCommandLine().main()