blob: 2d81fc536455ee87b92203f10d911675f9d58a78 [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()
Zheng-Jie Changa34c68e2020-04-29 07:07:19 +080089 util.check_call(
90 *(common_switch_cmd + [self.config['old'], '--dut', opts.dut]))
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +080091
Kuang-che Wua41525a2018-10-17 23:52:24 +080092 def _build_cmds(self):
93 # prebuilt version will be specified later.
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +080094 common_switch_cmd = [
Kuang-che Wua41525a2018-10-17 23:52:24 +080095 './switch_autotest_prebuilt.py',
Kuang-che Wu99e808f2019-06-26 12:17:32 +080096 '--chromeos_root',
97 self.config['chromeos_root'],
98 '--board',
99 self.config['board'],
100 ]
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800101 if self.config['test_name'] and not self.config['cts_test']:
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800102 common_switch_cmd += ['--test_name', self.config['test_name']]
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800103
Kuang-che Wua41525a2018-10-17 23:52:24 +0800104 common_eval_cmd = [
105 './eval_cros_autotest.py',
106 '--chromeos_root', self.config['chromeos_root'],
Kuang-che Wua41525a2018-10-17 23:52:24 +0800107 ] # yapf: disable
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800108 if self.config['test_name'] and not self.config['cts_test']:
109 common_eval_cmd += ['--test_name', self.config['test_name']]
Kuang-che Wua41525a2018-10-17 23:52:24 +0800110 if self.config['metric']:
111 common_eval_cmd += [
112 '--metric', self.config['metric'],
Kuang-che Wua41525a2018-10-17 23:52:24 +0800113 ] # yapf: disable
Kuang-che Wu32f27242019-05-16 17:34:50 +0800114 if self.config['fail_to_pass']:
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800115 common_eval_cmd.append('--fail_to_pass')
Kuang-che Wuda3abfe2019-03-21 14:48:12 +0800116 if self.config['reboot_before_test']:
117 common_eval_cmd.append('--reboot_before_test')
Kuang-che Wua41525a2018-10-17 23:52:24 +0800118 if self.config['test_that_args']:
119 common_eval_cmd += ['--args', self.config['test_that_args']]
Kuang-che Wud4603d72018-11-29 17:51:21 +0800120 if self.config['test_name'].startswith('telemetry_'):
121 common_eval_cmd += ['--chrome_root', self.config['chrome_root']]
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800122
123 for arg_name in [
124 'cts_revision', 'cts_abi', 'cts_prefix', 'cts_module', 'cts_test',
125 'cts_timeout'
126 ]:
127 if self.config.get(arg_name) is not None:
128 common_eval_cmd += ['--%s' % arg_name, str(self.config[arg_name])]
129
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800130 return common_switch_cmd, common_eval_cmd
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800131
Kuang-che Wua41525a2018-10-17 23:52:24 +0800132 def cmd_run(self, opts):
133 del opts # unused
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800134
Kuang-che Wua41525a2018-10-17 23:52:24 +0800135 self.states.load()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800136
Kuang-che Wu8b654092018-11-09 17:56:25 +0800137 try:
138 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
139 self.config['mirror_base'], self.config['work_base'],
140 self.config['session'])
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800141 common_switch_cmd, common_eval_cmd = self._build_cmds()
Kuang-che Wu8b654092018-11-09 17:56:25 +0800142
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800143 with cros_lab_util.dut_manager(
144 self.config['dut'],
145 lambda: diagnoser_cros.grab_dut(self.config)) as dut:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800146 if not dut:
147 raise errors.NoDutAvailable('unable to allocate DUT')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800148
Kuang-che Wu7cb08df2019-06-04 19:12:29 +0800149 if not cros_util.is_good_dut(dut):
150 if not cros_lab_util.repair(dut):
151 raise errors.ExternalError('Not a good DUT and unable to repair')
Kuang-che Wu8b654092018-11-09 17:56:25 +0800152 if self.config['dut'] == cros_lab_util.LAB_DUT:
153 self.config['allocated_dut'] = dut
154 self.states.save()
155 common_eval_cmd.append(dut)
156
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800157 util.check_call('./switch_cros_localbuild.py', '--nobuild',
158 '--chromeos_root', self.config['chromeos_root'],
159 '--chromeos_mirror', self.config['chromeos_mirror'],
Kuang-che Wu0ebbf7c2019-08-28 18:19:19 +0800160 '--board', self.config['board'], self.config['new'])
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800161
Kuang-che Wu186071a2019-03-28 17:11:41 +0800162 diagnoser = diagnoser_cros.CrosDiagnoser(self.states, path_factory,
163 self.config, dut)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800164
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800165 eval_cmd = common_eval_cmd + ['--prebuilt']
Kuang-che Wu8b654092018-11-09 17:56:25 +0800166 # Do not specify version for autotest prebuilt switching here. The trick
167 # is that version number is obtained via bisector's environment variable
168 # CROS_VERSION.
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800169 extra_switch_cmd = common_switch_cmd
Kuang-che Wu8b654092018-11-09 17:56:25 +0800170 diagnoser.narrow_down_chromeos_prebuilt(
171 self.config['old'],
172 self.config['new'],
173 eval_cmd,
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800174 extra_switch_cmd=extra_switch_cmd)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800175
176 diagnoser.switch_chromeos_to_old(force=self.config['always_reflash'])
Zheng-Jie Changa34c68e2020-04-29 07:07:19 +0800177 util.check_call(
178 *(common_switch_cmd + [diagnoser.cros_old, '--dut', dut]))
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800179 dut_os_version = cros_util.query_dut_short_version(dut)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800180
181 try:
182 if diagnoser.narrow_down_android(eval_cmd):
183 return
Kuang-che Wuec566f32019-03-07 16:53:32 +0800184 except errors.DiagnoseContradiction:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800185 raise
186 except Exception:
187 logger.exception('exception in android bisector before verification; '
188 'assume culprit is not inside android and continue')
189 # Assume it's ok to leave random version of android prebuilt on DUT.
190
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800191 # Sanity check. The OS version should not change after android bisect.
Kuang-che Wu523bdf22019-08-20 12:11:09 +0800192 assert dut_os_version == cros_util.query_dut_short_version(dut), \
193 'Someone else reflashed the DUT. ' \
194 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800195
Kuang-che Wu8b654092018-11-09 17:56:25 +0800196 eval_cmd = common_eval_cmd + ['--prebuilt']
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800197
Kuang-che Wuda9cb702019-11-11 14:40:56 +0800198 chrome_with_tests = bool(self.config['test_name']) # not CTS/GTS
199 if chrome_with_tests:
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800200 # Now, the version of autotest on the DUT is unknown and may be even
201 # not installed. Invoke the test once here, so
202 # - make sure autotest-deps is installed, with expected version
Kuang-che Wuda9cb702019-11-11 14:40:56 +0800203 # - autotest-deps is installed first, so our chrome test binaries
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800204 # won't be reset to default version during bisection.
205 # It's acceptable to spend extra time to run test once because
206 # - only few tests do so
207 # - tests are migrating away from autotest
208 util.call(*eval_cmd)
209
Kuang-che Wu8b654092018-11-09 17:56:25 +0800210 try:
211 if diagnoser.narrow_down_chrome(
Kuang-che Wuda9cb702019-11-11 14:40:56 +0800212 eval_cmd, with_tests=chrome_with_tests):
Kuang-che Wu8b654092018-11-09 17:56:25 +0800213 return
Kuang-che Wuec566f32019-03-07 16:53:32 +0800214 except errors.DiagnoseContradiction:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800215 raise
216 except Exception:
217 logger.exception('exception in chrome bisector before verification; '
218 'assume culprit is not inside chrome and continue')
219
Kuang-che Wude5bfc32019-09-12 21:56:48 +0800220 if not self.config['chrome_deploy_image']:
221 # Sanity check. The OS version should not change after chrome bisect.
222 assert dut_os_version == cros_util.query_dut_short_version(dut), \
223 'Someone else reflashed the DUT. ' \
224 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800225
Zheng-Jie Changd3fd8f12020-04-14 11:41:06 +0800226 if self.config['disable_buildbucket_chromeos']:
Zheng-Jie Chang181be6f2020-03-17 16:16:08 +0800227 eval_cmd = common_eval_cmd
228 extra_switch_cmd = None
Zheng-Jie Changd3fd8f12020-04-14 11:41:06 +0800229 else:
230 eval_cmd = common_eval_cmd + ['--prebuilt']
231 extra_switch_cmd = common_switch_cmd
Zheng-Jie Chang181be6f2020-03-17 16:16:08 +0800232 diagnoser.narrow_down_chromeos_localbuild(
233 eval_cmd, extra_switch_cmd=extra_switch_cmd)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800234 logger.info('%s done', __file__)
235 except Exception as e:
236 logger.exception('got exception; stop')
237 exception_name = e.__class__.__name__
238 self.states.add_history(
239 'failed', '%s: %s' % (exception_name, e), exception=exception_name)
240
Kuang-che Wu32f27242019-05-16 17:34:50 +0800241 def create_argument_parser_hook(self, parser_init):
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800242 group = parser_init.add_argument_group(title='Options for CTS/GTS tests')
243 group.add_argument('--cts_revision', help='CTS revision, like "9.0_r3"')
Kuang-che Wu63f836a2019-02-21 16:33:32 +0000244 group.add_argument('--cts_abi', choices=['arm', 'x86'])
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800245 group.add_argument(
246 '--cts_prefix',
247 help='Prefix of autotest test name, '
248 'like cheets_CTS_N, cheets_CTS_P, cheets_GTS')
249 group.add_argument(
250 '--cts_module', help='CTS/GTS module name, like "CtsCameraTestCases"')
251 group.add_argument(
252 '--cts_test',
253 help='CTS/GTS test name, like '
254 '"android.hardware.cts.CameraTest#testDisplayOrientation"')
255 group.add_argument('--cts_timeout', type=float, help='timeout, in seconds')
256
Kuang-che Wua41525a2018-10-17 23:52:24 +0800257 group = parser_init.add_argument_group(title='Options passed to test_that')
258 group.add_argument(
259 '--args',
260 help='Extra args passed to "test_that --args"; Overrides the default')
261
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800262
263if __name__ == '__main__':
Kuang-che Wu32f27242019-05-16 17:34:50 +0800264 DiagnoseAutotestCommandLine().main()