blob: 555a5c64498daacf279428a40aea58ea7a3a700c [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
16import argparse
17import fnmatch
18import glob
19import logging
20import os
21
22from bisect_kit import cli
23from bisect_kit import common
24from bisect_kit import configure
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080025from bisect_kit import cros_lab_util
26from bisect_kit import cros_util
27from bisect_kit import diagnoser_cros
Kuang-che Wue121fae2018-11-09 16:18:39 +080028from bisect_kit import errors
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080029from bisect_kit import util
Kuang-che Wud8fc9572018-10-03 21:00:41 +080030import setup_cros_bisect
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080031
32logger = logging.getLogger(__name__)
33
34# What chrome binaries to build for given autotest.
35# This dict is created manually by inspecting output of
36# 'grep -r ChromeBinaryTest autotest/files/client/site_tests'
37# If you change this dict, build_and_deploy_chrome_helper.sh may need update
38# as well.
39CHROME_BINARIES_OF_TEST = {
40 'graphics_Chrome.ozone_gl_unittests': ['ozone_gl_unittests'],
41 'security_SandboxLinuxUnittests': ['sandbox_linux_unittests'],
42 'video_HangoutHardwarePerf*': [
43 'video_decode_accelerator_unittest',
44 'video_encode_accelerator_unittest',
45 ],
46 'video_JDAPerf*': ['jpeg_decode_accelerator_unittest'],
47 'video_JEAPerf': ['jpeg_encode_accelerator_unittest'],
48 'video_JpegDecodeAccelerator': ['jpeg_decode_accelerator_unittest'],
49 'video_JpegEncodeAccelerator': ['jpeg_encode_accelerator_unittest'],
Kuang-che Wu17cdeb52019-01-30 17:58:46 +080050 'video_VDAPerf*': ['video_decode_accelerator_unittest'],
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080051 'video_VDASanity': ['video_decode_accelerator_unittest'],
Kuang-che Wu17cdeb52019-01-30 17:58:46 +080052 'video_VEAPerf*': ['video_encode_accelerator_unittest'],
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080053 'video_VideoDecodeAccelerator*': ['video_decode_accelerator_unittest'],
54 'video_VideoEncodeAccelerator*': ['video_encode_accelerator_unittest'],
55}
56
57
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080058def get_test_dependency_labels(config):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080059 # Assume "DEPENDENCIES" is identical between the period of
60 # `old` and `new` version.
Kuang-che Wua41525a2018-10-17 23:52:24 +080061 autotest_dir = os.path.join(config['chromeos_root'],
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080062 cros_util.prebuilt_autotest_dir)
Kuang-che Wua41525a2018-10-17 23:52:24 +080063 info = cros_util.get_autotest_test_info(autotest_dir, config['test_name'])
Kuang-che Wu0f1c3b02019-01-10 01:21:01 +080064 assert info, 'incorrect test name? %s' % config['test_name']
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080065
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080066 extra_labels = []
67 dependencies = info.variables.get('DEPENDENCIES', '')
68 for label in dependencies.split(','):
69 label = label.strip()
70 # Skip non-machine labels
71 if not label or label in ['cleanup-reboot']:
72 continue
73 extra_labels.append(label)
74
75 return extra_labels
76
77
78def grab_dut(config):
79 reason = cros_lab_util.make_lock_reason(config['session'])
Kuang-che Wuf65c61d2018-10-19 17:48:30 +080080 if config.get('allocated_dut'):
81 host_name = cros_lab_util.dut_host_name(config['allocated_dut'])
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080082 logger.info('try to lock the same host (%s) as last run', host_name)
83 candidates = cros_lab_util.list_host(host=host_name).values()
Kuang-che Wuf65c61d2018-10-19 17:48:30 +080084 else:
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080085 extra_labels = get_test_dependency_labels(config)
86 candidates = cros_lab_util.seek_host(
87 pools=config.get('pools', '').split(','),
Kuang-che Wuf65c61d2018-10-19 17:48:30 +080088 model=config['model'],
89 sku=config['sku'],
90 extra_labels=extra_labels)
91
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080092 if (not candidates or candidates[0]['Status'] != 'Ready' or
93 candidates[0]['Locked']):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080094 logger.error('unable to allocate dut')
95 return None
96
Kuang-che Wu22aa9d42019-01-25 10:35:33 +080097 host_name = candidates[0]['Host']
98 info = cros_lab_util.lock_host(host_name, reason)
99 if info['Status'] != 'Ready':
100 cros_lab_util.unlock_host(host_name)
101 raise Exception(
102 'unexpected host status=%s, a race condition?' % info['Status'])
103
104 logger.info('allocated host %s', host_name)
105 return host_name
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800106
107
108def may_depend_on_extra_chrome_binaries(autotest_dir, test_name):
109 info = cros_util.get_autotest_test_info(autotest_dir, test_name)
Kuang-che Wu0f1c3b02019-01-10 01:21:01 +0800110 assert info, 'incorrect test name? %s' % test_name
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800111 dirpath = os.path.dirname(info.path)
112 for pypath in glob.glob(os.path.join(dirpath, '*.py')):
113 if 'ChromeBinaryTest' in open(pypath).read():
114 return True
115 return False
116
117
Kuang-che Wua41525a2018-10-17 23:52:24 +0800118def determine_chrome_binaries(chromeos_root, test_name):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800119 chrome_binaries = None
120 for name_pattern, binaries in CHROME_BINARIES_OF_TEST.items():
Kuang-che Wua41525a2018-10-17 23:52:24 +0800121 if fnmatch.fnmatch(test_name, name_pattern):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800122 chrome_binaries = binaries
123 break
124
Kuang-che Wua41525a2018-10-17 23:52:24 +0800125 autotest_dir = os.path.join(chromeos_root, cros_util.prebuilt_autotest_dir)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800126 if chrome_binaries:
127 logger.info('This test depends on chrome binary: %s', chrome_binaries)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800128 elif may_depend_on_extra_chrome_binaries(autotest_dir, test_name):
Kuang-che Wu74768d32018-09-07 12:03:24 +0800129 logger.warning(
130 '%s code used ChromeBinaryTest but the binary is unknown; '
Kuang-che Wua41525a2018-10-17 23:52:24 +0800131 'please update CHROME_BINARIES_OF_TEST table', test_name)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800132 return chrome_binaries
133
134
Kuang-che Wua41525a2018-10-17 23:52:24 +0800135class DiagnoseCommandLine(object):
136 """Diagnose command line interface."""
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800137
Kuang-che Wua41525a2018-10-17 23:52:24 +0800138 def __init__(self):
139 common.init()
140 self.argument_parser = self.create_argument_parser()
141 self.states = None
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800142
Kuang-che Wua41525a2018-10-17 23:52:24 +0800143 @property
144 def config(self):
145 return self.states.config
146
147 def check_options(self, opts, path_factory):
148 if not opts.chromeos_mirror:
149 opts.chromeos_mirror = path_factory.get_chromeos_mirror()
150 logger.info('chromeos_mirror = %s', opts.chromeos_mirror)
151 if not opts.chromeos_root:
152 opts.chromeos_root = path_factory.get_chromeos_tree()
153 logger.info('chromeos_root = %s', opts.chromeos_root)
154 if not opts.chrome_mirror:
155 opts.chrome_mirror = path_factory.get_chrome_cache()
156 logger.info('chrome_mirror = %s', opts.chrome_mirror)
157 if not opts.chrome_root:
158 opts.chrome_root = path_factory.get_chrome_tree()
159 logger.info('chrome_root = %s', opts.chrome_root)
160
Kuang-che Wu248c5182018-10-19 17:08:11 +0800161 if opts.dut == cros_lab_util.LAB_DUT:
162 if not opts.model and not opts.sku:
163 self.argument_parser.error(
164 'either --model or --sku need to be specified if DUT is "%s"' %
165 cros_lab_util.LAB_DUT)
166 # Board name cannot be deduced from auto allocated devices because they
167 # may be provisioned with image of unexpected board.
168 if not opts.board:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800169 self.argument_parser.error('--board need to be specified if DUT is "%s"'
170 % cros_lab_util.LAB_DUT)
Kuang-che Wu248c5182018-10-19 17:08:11 +0800171 else:
172 if not opts.board:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800173 opts.board = cros_util.query_dut_board(opts.dut)
174
175 if cros_util.is_cros_short_version(opts.old):
176 opts.old = cros_util.version_to_full(opts.board, opts.old)
177 if cros_util.is_cros_short_version(opts.new):
178 opts.new = cros_util.version_to_full(opts.board, opts.new)
179
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800180 is_cts = (
181 opts.cts_revision or opts.cts_abi or opts.cts_prefix or
182 opts.cts_module or opts.cts_test or opts.cts_timeout)
183 if is_cts:
184 if opts.test_name or opts.metric or opts.args:
185 self.argument_parser.error(
186 'do not specify --test_name, --metric, --args for CTS/GTS tests')
187 opts.test_name = '%s.tradefed-run-test' % opts.cts_prefix
188
Kuang-che Wua41525a2018-10-17 23:52:24 +0800189 if opts.metric:
190 if opts.old_value is None:
191 self.argument_parser.error('--old_value is not provided')
192 if opts.new_value is None:
193 self.argument_parser.error('--new_value is not provided')
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800194 if opts.fail_to_pass:
195 self.argument_parser.error(
196 '--fail_to_pass is not for benchmark test (--metric)')
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800197 else:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800198 if opts.old_value is not None:
199 self.argument_parser.error(
200 '--old_value is provided but --metric is not')
201 if opts.new_value is not None:
202 self.argument_parser.error(
203 '--new_value is provided but --metric is not')
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800204
Kuang-che Wua41525a2018-10-17 23:52:24 +0800205 def cmd_init(self, opts):
206 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
207 opts.mirror_base, opts.work_base, opts.session)
208 self.check_options(opts, path_factory)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800209
Kuang-che Wua41525a2018-10-17 23:52:24 +0800210 config = dict(
211 session=opts.session,
212 mirror_base=opts.mirror_base,
213 work_base=opts.work_base,
214 chromeos_root=opts.chromeos_root,
215 chromeos_mirror=opts.chromeos_mirror,
216 chrome_root=opts.chrome_root,
217 chrome_mirror=opts.chrome_mirror,
218 android_root=opts.android_root,
219 android_mirror=opts.android_mirror,
Kuang-che Wu248c5182018-10-19 17:08:11 +0800220 dut=opts.dut,
Kuang-che Wufcac3f22019-01-10 00:36:42 +0800221 pools=opts.pools,
Kuang-che Wu248c5182018-10-19 17:08:11 +0800222 model=opts.model,
223 sku=opts.sku,
224 board=opts.board,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800225 old=opts.old,
226 new=opts.new,
227 test_name=opts.test_name,
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800228 fail_to_pass=opts.fail_to_pass,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800229 metric=opts.metric,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800230 old_value=opts.old_value,
231 new_value=opts.new_value,
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800232 cts_revision=opts.cts_revision,
233 cts_abi=opts.cts_abi,
234 cts_prefix=opts.cts_prefix,
235 cts_module=opts.cts_module,
236 cts_test=opts.cts_test,
237 cts_timeout=opts.cts_timeout,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800238 noisy=opts.noisy,
239 test_that_args=opts.args,
240 always_reflash=opts.always_reflash,
241 )
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800242
Kuang-che Wua41525a2018-10-17 23:52:24 +0800243 self.states.init(config)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800244
Kuang-che Wua41525a2018-10-17 23:52:24 +0800245 # Unpack old autotest prebuilt, assume following information don't change
246 # between versions:
247 # - what chrome binaries to run
248 # - dependency labels for DUT allocation
249 common_switch_cmd, _common_eval_cmd = self._build_cmds()
250 util.check_call(*(common_switch_cmd + [self.config['old']]))
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800251
Kuang-che Wua41525a2018-10-17 23:52:24 +0800252 self.states.save()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800253
Kuang-che Wua41525a2018-10-17 23:52:24 +0800254 def _build_cmds(self):
255 # prebuilt version will be specified later.
256 common_switch_cmd = [
257 './switch_autotest_prebuilt.py',
258 '--chromeos_root', self.config['chromeos_root'],
Kuang-che Wua41525a2018-10-17 23:52:24 +0800259 '--board', self.config['board'],
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800260 ] # yapf: disable
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800261 if self.config['test_name'] and not self.config['cts_test']:
262 common_switch_cmd += ['--test_name', self.config['test_name']]
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800263
Kuang-che Wua41525a2018-10-17 23:52:24 +0800264 common_eval_cmd = [
265 './eval_cros_autotest.py',
266 '--chromeos_root', self.config['chromeos_root'],
Kuang-che Wua41525a2018-10-17 23:52:24 +0800267 ] # yapf: disable
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800268 if self.config['test_name'] and not self.config['cts_test']:
269 common_eval_cmd += ['--test_name', self.config['test_name']]
Kuang-che Wua41525a2018-10-17 23:52:24 +0800270 if self.config['metric']:
271 common_eval_cmd += [
272 '--metric', self.config['metric'],
273 '--old_value', str(self.config['old_value']),
274 '--new_value', str(self.config['new_value']),
275 ] # yapf: disable
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800276 elif self.config['fail_to_pass']:
277 common_eval_cmd.append('--fail_to_pass')
Kuang-che Wua41525a2018-10-17 23:52:24 +0800278 if self.config['test_that_args']:
279 common_eval_cmd += ['--args', self.config['test_that_args']]
Kuang-che Wud4603d72018-11-29 17:51:21 +0800280 if self.config['test_name'].startswith('telemetry_'):
281 common_eval_cmd += ['--chrome_root', self.config['chrome_root']]
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800282
283 for arg_name in [
284 'cts_revision', 'cts_abi', 'cts_prefix', 'cts_module', 'cts_test',
285 'cts_timeout'
286 ]:
287 if self.config.get(arg_name) is not None:
288 common_eval_cmd += ['--%s' % arg_name, str(self.config[arg_name])]
289
Kuang-che Wua41525a2018-10-17 23:52:24 +0800290 return common_switch_cmd, common_eval_cmd
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800291
Kuang-che Wua41525a2018-10-17 23:52:24 +0800292 def cmd_run(self, opts):
293 del opts # unused
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800294
Kuang-che Wua41525a2018-10-17 23:52:24 +0800295 self.states.load()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800296
Kuang-che Wu8b654092018-11-09 17:56:25 +0800297 try:
298 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
299 self.config['mirror_base'], self.config['work_base'],
300 self.config['session'])
301 common_switch_cmd, common_eval_cmd = self._build_cmds()
302
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800303 if self.config['test_name']:
304 chrome_binaries = determine_chrome_binaries(
305 self.config['chromeos_root'], self.config['test_name'])
306 else:
307 chrome_binaries = None
Kuang-che Wu8b654092018-11-09 17:56:25 +0800308
309 with cros_lab_util.dut_manager(self.config['dut'],
310 lambda: grab_dut(self.config)) as dut:
311 if not dut:
312 raise errors.NoDutAvailable('unable to allocate DUT')
313 assert cros_util.is_dut(dut)
314 if self.config['dut'] == cros_lab_util.LAB_DUT:
315 self.config['allocated_dut'] = dut
316 self.states.save()
317 common_eval_cmd.append(dut)
318
319 diagnoser = diagnoser_cros.CrosDiagnoser(
320 self.states, self.config['session'], path_factory,
321 self.config['chromeos_root'], self.config['chromeos_mirror'],
322 self.config['android_root'], self.config['android_mirror'],
323 self.config['chrome_root'], self.config['chrome_mirror'],
324 self.config['board'], self.config['noisy'], dut)
325
326 eval_cmd = common_eval_cmd + ['--prebuilt', '--reinstall']
327 # Do not specify version for autotest prebuilt switching here. The trick
328 # is that version number is obtained via bisector's environment variable
329 # CROS_VERSION.
330 extra_switch_cmd = common_switch_cmd
331 diagnoser.narrow_down_chromeos_prebuilt(
332 self.config['old'],
333 self.config['new'],
334 eval_cmd,
335 extra_switch_cmd=extra_switch_cmd)
336
337 diagnoser.switch_chromeos_to_old(force=self.config['always_reflash'])
338 util.check_call(*(common_switch_cmd + [diagnoser.cros_old]))
339 util.check_call('ssh', dut, 'rm', '-rf', '/usr/local/autotest')
340
341 try:
342 if diagnoser.narrow_down_android(eval_cmd):
343 return
344 except errors.VerificationFailed:
345 raise
346 except Exception:
347 logger.exception('exception in android bisector before verification; '
348 'assume culprit is not inside android and continue')
349 # Assume it's ok to leave random version of android prebuilt on DUT.
350
351 # Don't --reinstall to keep chrome binaries override.
352 eval_cmd = common_eval_cmd + ['--prebuilt']
353 try:
354 if diagnoser.narrow_down_chrome(
Kuang-che Wu50d8ff42018-11-26 12:48:30 +0800355 eval_cmd, chrome_binaries=chrome_binaries):
Kuang-che Wu8b654092018-11-09 17:56:25 +0800356 return
Kuang-che Wu1dc5bd72019-01-19 00:14:46 +0800357 except errors.VerifyOldBehaviorFailed:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800358 logger.fatal('expect old chrome has old behavior but failed')
359 raise
360 except Exception:
361 logger.exception('exception in chrome bisector before verification; '
362 'assume culprit is not inside chrome and continue')
363
364 eval_cmd = common_eval_cmd + ['--reinstall']
365 diagnoser.narrow_down_chromeos_localbuild(eval_cmd)
366 logger.info('%s done', __file__)
367 except Exception as e:
368 logger.exception('got exception; stop')
369 exception_name = e.__class__.__name__
370 self.states.add_history(
371 'failed', '%s: %s' % (exception_name, e), exception=exception_name)
372
373 def cmd_log(self, opts):
374 self.states.load()
Kuang-che Wua41525a2018-10-17 23:52:24 +0800375 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
376 self.config['mirror_base'], self.config['work_base'],
377 self.config['session'])
Kuang-che Wu8b654092018-11-09 17:56:25 +0800378 diagnoser = diagnoser_cros.CrosDiagnoser(
379 self.states, self.config['session'], path_factory,
380 self.config['chromeos_root'], self.config['chromeos_mirror'],
381 self.config['android_root'], self.config['android_mirror'],
382 self.config['chrome_root'], self.config['chrome_mirror'],
383 self.config['board'], self.config['noisy'], None)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800384
Kuang-che Wu8b654092018-11-09 17:56:25 +0800385 diagnoser.cmd_log(opts.json)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800386
Kuang-che Wue80bb872018-11-15 19:45:25 +0800387 def cmd_view(self, opts):
388 self.states.load()
389 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
390 self.config['mirror_base'], self.config['work_base'],
391 self.config['session'])
392 diagnoser = diagnoser_cros.CrosDiagnoser(
393 self.states, self.config['session'], path_factory,
394 self.config['chromeos_root'], self.config['chromeos_mirror'],
395 self.config['android_root'], self.config['android_mirror'],
396 self.config['chrome_root'], self.config['chrome_mirror'],
397 self.config['board'], self.config['noisy'], None)
398 diagnoser.cmd_view(opts.json, opts.verbose)
399
Kuang-che Wua41525a2018-10-17 23:52:24 +0800400 def create_argument_parser(self):
401 parser = argparse.ArgumentParser()
402 common.add_common_arguments(parser)
403 parser.add_argument('--session_base', default='bisect.sessions')
404 parser.add_argument('--session', help='Session name', required=True)
405 subparsers = parser.add_subparsers(
406 dest='command', title='commands', metavar='<command>')
407
408 parser_init = subparsers.add_parser('init', help='Initialize')
409 group = parser_init.add_argument_group(
410 title='Source tree path options',
411 description='''
412 Specify the paths of chromeos/chrome/android mirror and checkout. They
413 have the same default values as setup_cros_bisect.py, so usually you can
414 omit them and it just works.
415 ''')
416 group.add_argument(
417 '--mirror_base',
418 metavar='MIRROR_BASE',
419 default=configure.get('MIRROR_BASE',
420 setup_cros_bisect.DEFAULT_MIRROR_BASE),
421 help='Directory for mirrors (default: %(default)s)')
422 group.add_argument(
423 '--work_base',
424 metavar='WORK_BASE',
425 default=configure.get('WORK_BASE', setup_cros_bisect.DEFAULT_WORK_BASE),
426 help='Directory for bisection working directories '
427 '(default: %(default)s)')
428 group.add_argument(
429 '--chromeos_root',
430 metavar='CHROMEOS_ROOT',
431 type=cli.argtype_dir_path,
432 default=configure.get('CHROMEOS_ROOT'),
433 help='ChromeOS tree root')
434 group.add_argument(
435 '--chromeos_mirror',
436 type=cli.argtype_dir_path,
437 default=configure.get('CHROMEOS_MIRROR'),
438 help='ChromeOS repo mirror path')
439 group.add_argument(
440 '--android_root',
441 metavar='ANDROID_ROOT',
442 type=cli.argtype_dir_path,
443 default=configure.get('ANDROID_ROOT'),
444 help='Android tree root')
445 group.add_argument(
446 '--android_mirror',
447 type=cli.argtype_dir_path,
448 default=configure.get('ANDROID_MIRROR'),
449 help='Android repo mirror path')
450 group.add_argument(
451 '--chrome_root',
452 metavar='CHROME_ROOT',
453 type=cli.argtype_dir_path,
454 default=configure.get('CHROME_ROOT'),
455 help='Chrome tree root')
456 group.add_argument(
457 '--chrome_mirror',
458 metavar='CHROME_MIRROR',
459 type=cli.argtype_dir_path,
460 default=configure.get('CHROME_MIRROR'),
461 help="chrome's gclient cache dir")
462
Kuang-che Wu248c5182018-10-19 17:08:11 +0800463 group = parser_init.add_argument_group(title='DUT allocation options')
464 group.add_argument(
465 '--dut',
466 metavar='DUT',
467 required=True,
468 help='Address of DUT (Device Under Test). If "%s", DUT will be '
469 'automatically allocated from the lab' % cros_lab_util.LAB_DUT)
470 group.add_argument(
Kuang-che Wufcac3f22019-01-10 00:36:42 +0800471 '--pools',
472 type=cli.argtype_notempty,
Kuang-che Wu8c6d9dd2019-01-16 10:29:13 +0800473 default='suites',
Kuang-che Wufcac3f22019-01-10 00:36:42 +0800474 help='Desired pools, comma separated and ordered by preference '
475 '(default: %(default)s)')
476 group.add_argument(
Kuang-che Wu248c5182018-10-19 17:08:11 +0800477 '--model',
478 metavar='MODEL',
479 help='"model" criteria if DUT is auto allocated from the lab')
480 group.add_argument(
481 '--sku',
482 metavar='SKU',
483 help='"sku" criteria if DUT is auto allocated from the lab')
484
Kuang-che Wua41525a2018-10-17 23:52:24 +0800485 group = parser_init.add_argument_group(title='Essential options')
486 group.add_argument(
Kuang-che Wu248c5182018-10-19 17:08:11 +0800487 '--board',
488 metavar='BOARD',
489 default=configure.get('BOARD'),
490 help='ChromeOS board name; auto detected if DUT is not auto allocated')
491 group.add_argument(
Kuang-che Wua41525a2018-10-17 23:52:24 +0800492 '--old',
493 type=cros_util.argtype_cros_version,
494 required=True,
495 help='ChromeOS version with old behavior')
496 group.add_argument(
497 '--new',
498 type=cros_util.argtype_cros_version,
499 required=True,
500 help='ChromeOS version with new behavior')
Kuang-che Wua41525a2018-10-17 23:52:24 +0800501
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800502 group = parser_init.add_argument_group(
503 title='Options for normal autotest tests')
504 group.add_argument('--test_name', help='Test name')
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800505 group.add_argument(
506 '--fail_to_pass',
507 action='store_true',
508 help='For functional tests: bisect the CL fixed the regression (when '
509 'test became PASS). If not specified, the default is to bisect the CL '
510 'which broke the test (when test became FAIL)')
Kuang-che Wua41525a2018-10-17 23:52:24 +0800511 group.add_argument('--metric', help='Metric name of benchmark test')
512 group.add_argument(
513 '--old_value',
514 type=float,
515 help='For benchmark test, old value of metric')
516 group.add_argument(
517 '--new_value',
518 type=float,
519 help='For benchmark test, new value of metric')
520
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800521 group = parser_init.add_argument_group(title='Options for CTS/GTS tests')
522 group.add_argument('--cts_revision', help='CTS revision, like "9.0_r3"')
523 group.add_argument('--cts_abi', choices=['arm', 'x86'])
524 group.add_argument(
525 '--cts_prefix',
526 help='Prefix of autotest test name, '
527 'like cheets_CTS_N, cheets_CTS_P, cheets_GTS')
528 group.add_argument(
529 '--cts_module', help='CTS/GTS module name, like "CtsCameraTestCases"')
530 group.add_argument(
531 '--cts_test',
532 help='CTS/GTS test name, like '
533 '"android.hardware.cts.CameraTest#testDisplayOrientation"')
534 group.add_argument('--cts_timeout', type=float, help='timeout, in seconds')
535
Kuang-che Wua41525a2018-10-17 23:52:24 +0800536 group = parser_init.add_argument_group(title='Options passed to test_that')
537 group.add_argument(
538 '--args',
539 help='Extra args passed to "test_that --args"; Overrides the default')
540
541 group = parser_init.add_argument_group(title='Bisect behavior options')
542 group.add_argument(
543 '--noisy',
544 help='Enable noisy binary search. Example value: "old=1/10,new=2/3"')
545 group.add_argument(
546 '--always_reflash',
547 action='store_true',
548 help='Do not trust ChromeOS version number of DUT and always reflash. '
549 'This is usually only needed when resume because previous bisect was '
550 'interrupted and the DUT may be in an unexpected state')
551 parser_init.set_defaults(func=self.cmd_init)
552
553 parser_run = subparsers.add_parser('run', help='Start auto bisection')
554 parser_run.set_defaults(func=self.cmd_run)
555
Kuang-che Wu8b654092018-11-09 17:56:25 +0800556 parser_log = subparsers.add_parser(
557 'log', help='Prints what has been done so far')
558 parser_log.add_argument(
559 '--json', action='store_true', help='Machine readable output')
560 parser_log.set_defaults(func=self.cmd_log)
561
Kuang-che Wue80bb872018-11-15 19:45:25 +0800562 parser_view = subparsers.add_parser(
563 'view', help='Prints summary of current status')
564 parser_view.add_argument('--verbose', '-v', action='store_true')
565 parser_view.add_argument(
566 '--json', action='store_true', help='Machine readable output')
567 parser_view.set_defaults(func=self.cmd_view)
568
Kuang-che Wua41525a2018-10-17 23:52:24 +0800569 return parser
570
571 def main(self, args=None):
572 opts = self.argument_parser.parse_args(args)
573 common.config_logging(opts)
574
575 session_base = configure.get('SESSION_BASE', common.DEFAULT_SESSION_BASE)
576 session_file = os.path.join(session_base, opts.session,
577 self.__class__.__name__)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800578 self.states = diagnoser_cros.DiagnoseStates(session_file)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800579 opts.func(opts)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800580
581
582if __name__ == '__main__':
Kuang-che Wua41525a2018-10-17 23:52:24 +0800583 DiagnoseCommandLine().main()