blob: 6eae802a5e2dfdb93bcca021adda5fe608a7e62f [file] [log] [blame]
Kuang-che Wu1fcc0222018-07-07 16:43:22 +08001#!/usr/bin/env python2
2# -*- 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
Kuang-che Wu568c84d2019-11-06 17:23:53 +080021from bisect_kit import cr_util
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080022from bisect_kit import diagnoser_cros
Kuang-che Wue121fae2018-11-09 16:18:39 +080023from bisect_kit import errors
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080024from bisect_kit import util
Kuang-che Wud8fc9572018-10-03 21:00:41 +080025import setup_cros_bisect
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080026
27logger = logging.getLogger(__name__)
28
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080029
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080030def get_test_dependency_labels(config):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080031 # Assume "DEPENDENCIES" is identical between the period of
32 # `old` and `new` version.
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +080033 autotest_dir = os.path.join(config['chromeos_root'],
34 cros_util.prebuilt_autotest_dir)
Kuang-che Wua41525a2018-10-17 23:52:24 +080035 info = cros_util.get_autotest_test_info(autotest_dir, config['test_name'])
Kuang-che Wu0f1c3b02019-01-10 01:21:01 +080036 assert info, 'incorrect test name? %s' % config['test_name']
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080037
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080038 extra_labels = []
39 dependencies = info.variables.get('DEPENDENCIES', '')
40 for label in dependencies.split(','):
41 label = label.strip()
42 # Skip non-machine labels
43 if not label or label in ['cleanup-reboot']:
44 continue
45 extra_labels.append(label)
46
47 return extra_labels
48
49
Kuang-che Wu32f27242019-05-16 17:34:50 +080050class DiagnoseAutotestCommandLine(diagnoser_cros.DiagnoseCommandLineBase):
Kuang-che Wua41525a2018-10-17 23:52:24 +080051 """Diagnose command line interface."""
Kuang-che Wud8fc9572018-10-03 21:00:41 +080052
Kuang-che Wua41525a2018-10-17 23:52:24 +080053 def check_options(self, opts, path_factory):
Kuang-che Wu32f27242019-05-16 17:34:50 +080054 super(DiagnoseAutotestCommandLine, self).check_options(opts, path_factory)
Kuang-che Wua41525a2018-10-17 23:52:24 +080055
Kuang-che Wu85c613c2019-01-09 15:46:11 +080056 is_cts = (
57 opts.cts_revision or opts.cts_abi or opts.cts_prefix or
58 opts.cts_module or opts.cts_test or opts.cts_timeout)
59 if is_cts:
60 if opts.test_name or opts.metric or opts.args:
61 self.argument_parser.error(
62 'do not specify --test_name, --metric, --args for CTS/GTS tests')
Kuang-che Wub91b1512019-05-29 15:16:29 +080063 if not opts.cts_prefix:
64 self.argument_parser.error(
65 '--cts_prefix should be specified for CTS/GTS tests')
66 if not opts.cts_module:
67 self.argument_parser.error(
68 '--cts_module should be specified for CTS/GTS tests')
Kuang-che Wu85c613c2019-01-09 15:46:11 +080069 opts.test_name = '%s.tradefed-run-test' % opts.cts_prefix
Kuang-che Wu32f27242019-05-16 17:34:50 +080070 elif not opts.test_name:
71 self.argument_parser.error(
72 '--test_name should be specified if not CTS/GTS tests')
Kuang-che Wu85c613c2019-01-09 15:46:11 +080073
Kuang-che Wu32f27242019-05-16 17:34:50 +080074 def init_hook(self, opts):
75 self.states.config.update(
Kuang-che Wu85c613c2019-01-09 15:46:11 +080076 cts_revision=opts.cts_revision,
77 cts_abi=opts.cts_abi,
78 cts_prefix=opts.cts_prefix,
79 cts_module=opts.cts_module,
80 cts_test=opts.cts_test,
81 cts_timeout=opts.cts_timeout,
Kuang-che Wua41525a2018-10-17 23:52:24 +080082 test_that_args=opts.args,
Kuang-che Wua41525a2018-10-17 23:52:24 +080083 )
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080084
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +080085 # Unpack old autotest prebuilt, assume following information don't change
86 # between versions:
87 # - what chrome binaries to run
88 # - dependency labels for DUT allocation
89 common_switch_cmd, _common_eval_cmd = self._build_cmds()
90 util.check_call(*(common_switch_cmd + [self.config['old']]))
91
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 Wu85c613c2019-01-09 15:46:11 +0800143 if self.config['test_name']:
Kuang-che Wu568c84d2019-11-06 17:23:53 +0800144 chrome_src = os.path.join(self.config['chrome_root'], 'src')
145 chrome_binaries = (
146 cr_util.CHROME_BINARIES + cr_util.get_chrome_test_binaries(
147 chrome_src, self.config['board']))
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800148 else:
149 chrome_binaries = None
Kuang-che Wu8b654092018-11-09 17:56:25 +0800150
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800151 with cros_lab_util.dut_manager(
152 self.config['dut'],
153 lambda: diagnoser_cros.grab_dut(self.config)) as dut:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800154 if not dut:
155 raise errors.NoDutAvailable('unable to allocate DUT')
Kuang-che Wu0c9b7942019-10-30 16:55:39 +0800156
Kuang-che Wu7cb08df2019-06-04 19:12:29 +0800157 if not cros_util.is_good_dut(dut):
158 if not cros_lab_util.repair(dut):
159 raise errors.ExternalError('Not a good DUT and unable to repair')
Kuang-che Wu8b654092018-11-09 17:56:25 +0800160 if self.config['dut'] == cros_lab_util.LAB_DUT:
161 self.config['allocated_dut'] = dut
162 self.states.save()
163 common_eval_cmd.append(dut)
164
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800165 util.check_call('./switch_cros_localbuild.py', '--nobuild',
166 '--chromeos_root', self.config['chromeos_root'],
167 '--chromeos_mirror', self.config['chromeos_mirror'],
Kuang-che Wu0ebbf7c2019-08-28 18:19:19 +0800168 '--board', self.config['board'], self.config['new'])
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800169
Kuang-che Wu186071a2019-03-28 17:11:41 +0800170 diagnoser = diagnoser_cros.CrosDiagnoser(self.states, path_factory,
171 self.config, dut)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800172
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800173 eval_cmd = common_eval_cmd + ['--prebuilt']
Kuang-che Wu8b654092018-11-09 17:56:25 +0800174 # Do not specify version for autotest prebuilt switching here. The trick
175 # is that version number is obtained via bisector's environment variable
176 # CROS_VERSION.
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800177 extra_switch_cmd = common_switch_cmd
Kuang-che Wu8b654092018-11-09 17:56:25 +0800178 diagnoser.narrow_down_chromeos_prebuilt(
179 self.config['old'],
180 self.config['new'],
181 eval_cmd,
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800182 extra_switch_cmd=extra_switch_cmd)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800183
184 diagnoser.switch_chromeos_to_old(force=self.config['always_reflash'])
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800185 util.check_call(*(common_switch_cmd + [diagnoser.cros_old]))
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800186 dut_os_version = cros_util.query_dut_short_version(dut)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800187
188 try:
189 if diagnoser.narrow_down_android(eval_cmd):
190 return
Kuang-che Wuec566f32019-03-07 16:53:32 +0800191 except errors.DiagnoseContradiction:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800192 raise
193 except Exception:
194 logger.exception('exception in android bisector before verification; '
195 'assume culprit is not inside android and continue')
196 # Assume it's ok to leave random version of android prebuilt on DUT.
197
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800198 # Sanity check. The OS version should not change after android bisect.
Kuang-che Wu523bdf22019-08-20 12:11:09 +0800199 assert dut_os_version == cros_util.query_dut_short_version(dut), \
200 'Someone else reflashed the DUT. ' \
201 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800202
Kuang-che Wu8b654092018-11-09 17:56:25 +0800203 eval_cmd = common_eval_cmd + ['--prebuilt']
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800204
205 if chrome_binaries:
206 # Now, the version of autotest on the DUT is unknown and may be even
207 # not installed. Invoke the test once here, so
208 # - make sure autotest-deps is installed, with expected version
209 # - autotest-deps is installed first, so our chrome_binaries
210 # won't be reset to default version during bisection.
211 # It's acceptable to spend extra time to run test once because
212 # - only few tests do so
213 # - tests are migrating away from autotest
214 util.call(*eval_cmd)
215
Kuang-che Wu8b654092018-11-09 17:56:25 +0800216 try:
217 if diagnoser.narrow_down_chrome(
Kuang-che Wu50d8ff42018-11-26 12:48:30 +0800218 eval_cmd, chrome_binaries=chrome_binaries):
Kuang-che Wu8b654092018-11-09 17:56:25 +0800219 return
Kuang-che Wuec566f32019-03-07 16:53:32 +0800220 except errors.DiagnoseContradiction:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800221 raise
222 except Exception:
223 logger.exception('exception in chrome bisector before verification; '
224 'assume culprit is not inside chrome and continue')
225
Kuang-che Wude5bfc32019-09-12 21:56:48 +0800226 if not self.config['chrome_deploy_image']:
227 # Sanity check. The OS version should not change after chrome bisect.
228 assert dut_os_version == cros_util.query_dut_short_version(dut), \
229 'Someone else reflashed the DUT. ' \
230 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800231
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +0800232 eval_cmd = common_eval_cmd
Kuang-che Wu8b654092018-11-09 17:56:25 +0800233 diagnoser.narrow_down_chromeos_localbuild(eval_cmd)
234 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()