blob: 5f7200ce63c3bdfb8ef566557e0ba753270b24e1 [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'],
50 'video_VDAPerf': ['video_decode_accelerator_unittest'],
51 'video_VDASanity': ['video_decode_accelerator_unittest'],
52 'video_VEAPerf': ['video_encode_accelerator_unittest'],
53 'video_VideoDecodeAccelerator*': ['video_decode_accelerator_unittest'],
54 'video_VideoEncodeAccelerator*': ['video_encode_accelerator_unittest'],
55}
56
57
Kuang-che Wua41525a2018-10-17 23:52:24 +080058def grab_dut(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 Wua41525a2018-10-17 23:52:24 +080066 reason = 'bisect-kit: %s' % config['session']
Kuang-che Wuf65c61d2018-10-19 17:48:30 +080067 if config.get('allocated_dut'):
68 host_name = cros_lab_util.dut_host_name(config['allocated_dut'])
69 logger.info('try to allocate the same host (%s) as last run', host_name)
70 host = cros_lab_util.allocate_host(reason, host=host_name)
71 else:
72 extra_labels = []
73 dependencies = info.variables.get('DEPENDENCIES', '')
74 for label in dependencies.split(','):
75 label = label.strip()
76 # Skip non-machine labels
77 if label in ['cleanup-reboot']:
78 continue
79 extra_labels.append(label)
80
81 host = cros_lab_util.allocate_host(
82 reason,
83 model=config['model'],
84 sku=config['sku'],
Kuang-che Wufcac3f22019-01-10 00:36:42 +080085 pools=config.get('pools', '').split(','),
Kuang-che Wuf65c61d2018-10-19 17:48:30 +080086 extra_labels=extra_labels)
87
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080088 if not host:
89 logger.error('unable to allocate dut')
90 return None
91
92 logger.info('allocated host %s', host)
93 return host
94
95
96def may_depend_on_extra_chrome_binaries(autotest_dir, test_name):
97 info = cros_util.get_autotest_test_info(autotest_dir, test_name)
Kuang-che Wu0f1c3b02019-01-10 01:21:01 +080098 assert info, 'incorrect test name? %s' % test_name
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080099 dirpath = os.path.dirname(info.path)
100 for pypath in glob.glob(os.path.join(dirpath, '*.py')):
101 if 'ChromeBinaryTest' in open(pypath).read():
102 return True
103 return False
104
105
Kuang-che Wua41525a2018-10-17 23:52:24 +0800106def determine_chrome_binaries(chromeos_root, test_name):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800107 chrome_binaries = None
108 for name_pattern, binaries in CHROME_BINARIES_OF_TEST.items():
Kuang-che Wua41525a2018-10-17 23:52:24 +0800109 if fnmatch.fnmatch(test_name, name_pattern):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800110 chrome_binaries = binaries
111 break
112
Kuang-che Wua41525a2018-10-17 23:52:24 +0800113 autotest_dir = os.path.join(chromeos_root, cros_util.prebuilt_autotest_dir)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800114 if chrome_binaries:
115 logger.info('This test depends on chrome binary: %s', chrome_binaries)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800116 elif may_depend_on_extra_chrome_binaries(autotest_dir, test_name):
Kuang-che Wu74768d32018-09-07 12:03:24 +0800117 logger.warning(
118 '%s code used ChromeBinaryTest but the binary is unknown; '
Kuang-che Wua41525a2018-10-17 23:52:24 +0800119 'please update CHROME_BINARIES_OF_TEST table', test_name)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800120 return chrome_binaries
121
122
Kuang-che Wua41525a2018-10-17 23:52:24 +0800123class DiagnoseCommandLine(object):
124 """Diagnose command line interface."""
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800125
Kuang-che Wua41525a2018-10-17 23:52:24 +0800126 def __init__(self):
127 common.init()
128 self.argument_parser = self.create_argument_parser()
129 self.states = None
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800130
Kuang-che Wua41525a2018-10-17 23:52:24 +0800131 @property
132 def config(self):
133 return self.states.config
134
135 def check_options(self, opts, path_factory):
136 if not opts.chromeos_mirror:
137 opts.chromeos_mirror = path_factory.get_chromeos_mirror()
138 logger.info('chromeos_mirror = %s', opts.chromeos_mirror)
139 if not opts.chromeos_root:
140 opts.chromeos_root = path_factory.get_chromeos_tree()
141 logger.info('chromeos_root = %s', opts.chromeos_root)
142 if not opts.chrome_mirror:
143 opts.chrome_mirror = path_factory.get_chrome_cache()
144 logger.info('chrome_mirror = %s', opts.chrome_mirror)
145 if not opts.chrome_root:
146 opts.chrome_root = path_factory.get_chrome_tree()
147 logger.info('chrome_root = %s', opts.chrome_root)
148
Kuang-che Wu248c5182018-10-19 17:08:11 +0800149 if opts.dut == cros_lab_util.LAB_DUT:
150 if not opts.model and not opts.sku:
151 self.argument_parser.error(
152 'either --model or --sku need to be specified if DUT is "%s"' %
153 cros_lab_util.LAB_DUT)
154 # Board name cannot be deduced from auto allocated devices because they
155 # may be provisioned with image of unexpected board.
156 if not opts.board:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800157 self.argument_parser.error('--board need to be specified if DUT is "%s"'
158 % cros_lab_util.LAB_DUT)
Kuang-che Wu248c5182018-10-19 17:08:11 +0800159 else:
160 if not opts.board:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800161 opts.board = cros_util.query_dut_board(opts.dut)
162
163 if cros_util.is_cros_short_version(opts.old):
164 opts.old = cros_util.version_to_full(opts.board, opts.old)
165 if cros_util.is_cros_short_version(opts.new):
166 opts.new = cros_util.version_to_full(opts.board, opts.new)
167
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800168 is_cts = (
169 opts.cts_revision or opts.cts_abi or opts.cts_prefix or
170 opts.cts_module or opts.cts_test or opts.cts_timeout)
171 if is_cts:
172 if opts.test_name or opts.metric or opts.args:
173 self.argument_parser.error(
174 'do not specify --test_name, --metric, --args for CTS/GTS tests')
175 opts.test_name = '%s.tradefed-run-test' % opts.cts_prefix
176
Kuang-che Wua41525a2018-10-17 23:52:24 +0800177 if opts.metric:
178 if opts.old_value is None:
179 self.argument_parser.error('--old_value is not provided')
180 if opts.new_value is None:
181 self.argument_parser.error('--new_value is not provided')
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800182 if opts.fail_to_pass:
183 self.argument_parser.error(
184 '--fail_to_pass is not for benchmark test (--metric)')
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800185 else:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800186 if opts.old_value is not None:
187 self.argument_parser.error(
188 '--old_value is provided but --metric is not')
189 if opts.new_value is not None:
190 self.argument_parser.error(
191 '--new_value is provided but --metric is not')
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800192
Kuang-che Wua41525a2018-10-17 23:52:24 +0800193 def cmd_init(self, opts):
194 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
195 opts.mirror_base, opts.work_base, opts.session)
196 self.check_options(opts, path_factory)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800197
Kuang-che Wua41525a2018-10-17 23:52:24 +0800198 config = dict(
199 session=opts.session,
200 mirror_base=opts.mirror_base,
201 work_base=opts.work_base,
202 chromeos_root=opts.chromeos_root,
203 chromeos_mirror=opts.chromeos_mirror,
204 chrome_root=opts.chrome_root,
205 chrome_mirror=opts.chrome_mirror,
206 android_root=opts.android_root,
207 android_mirror=opts.android_mirror,
Kuang-che Wu248c5182018-10-19 17:08:11 +0800208 dut=opts.dut,
Kuang-che Wufcac3f22019-01-10 00:36:42 +0800209 pools=opts.pools,
Kuang-che Wu248c5182018-10-19 17:08:11 +0800210 model=opts.model,
211 sku=opts.sku,
212 board=opts.board,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800213 old=opts.old,
214 new=opts.new,
215 test_name=opts.test_name,
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800216 fail_to_pass=opts.fail_to_pass,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800217 metric=opts.metric,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800218 old_value=opts.old_value,
219 new_value=opts.new_value,
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800220 cts_revision=opts.cts_revision,
221 cts_abi=opts.cts_abi,
222 cts_prefix=opts.cts_prefix,
223 cts_module=opts.cts_module,
224 cts_test=opts.cts_test,
225 cts_timeout=opts.cts_timeout,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800226 noisy=opts.noisy,
227 test_that_args=opts.args,
228 always_reflash=opts.always_reflash,
229 )
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800230
Kuang-che Wua41525a2018-10-17 23:52:24 +0800231 self.states.init(config)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800232
Kuang-che Wua41525a2018-10-17 23:52:24 +0800233 # Unpack old autotest prebuilt, assume following information don't change
234 # between versions:
235 # - what chrome binaries to run
236 # - dependency labels for DUT allocation
237 common_switch_cmd, _common_eval_cmd = self._build_cmds()
238 util.check_call(*(common_switch_cmd + [self.config['old']]))
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800239
Kuang-che Wua41525a2018-10-17 23:52:24 +0800240 self.states.save()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800241
Kuang-che Wua41525a2018-10-17 23:52:24 +0800242 def _build_cmds(self):
243 # prebuilt version will be specified later.
244 common_switch_cmd = [
245 './switch_autotest_prebuilt.py',
246 '--chromeos_root', self.config['chromeos_root'],
Kuang-che Wua41525a2018-10-17 23:52:24 +0800247 '--board', self.config['board'],
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800248 ] # yapf: disable
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800249 if self.config['test_name'] and not self.config['cts_test']:
250 common_switch_cmd += ['--test_name', self.config['test_name']]
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800251
Kuang-che Wua41525a2018-10-17 23:52:24 +0800252 common_eval_cmd = [
253 './eval_cros_autotest.py',
254 '--chromeos_root', self.config['chromeos_root'],
Kuang-che Wua41525a2018-10-17 23:52:24 +0800255 ] # yapf: disable
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800256 if self.config['test_name'] and not self.config['cts_test']:
257 common_eval_cmd += ['--test_name', self.config['test_name']]
Kuang-che Wua41525a2018-10-17 23:52:24 +0800258 if self.config['metric']:
259 common_eval_cmd += [
260 '--metric', self.config['metric'],
261 '--old_value', str(self.config['old_value']),
262 '--new_value', str(self.config['new_value']),
263 ] # yapf: disable
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800264 elif self.config['fail_to_pass']:
265 common_eval_cmd.append('--fail_to_pass')
Kuang-che Wua41525a2018-10-17 23:52:24 +0800266 if self.config['test_that_args']:
267 common_eval_cmd += ['--args', self.config['test_that_args']]
Kuang-che Wud4603d72018-11-29 17:51:21 +0800268 if self.config['test_name'].startswith('telemetry_'):
269 common_eval_cmd += ['--chrome_root', self.config['chrome_root']]
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800270
271 for arg_name in [
272 'cts_revision', 'cts_abi', 'cts_prefix', 'cts_module', 'cts_test',
273 'cts_timeout'
274 ]:
275 if self.config.get(arg_name) is not None:
276 common_eval_cmd += ['--%s' % arg_name, str(self.config[arg_name])]
277
Kuang-che Wua41525a2018-10-17 23:52:24 +0800278 return common_switch_cmd, common_eval_cmd
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800279
Kuang-che Wua41525a2018-10-17 23:52:24 +0800280 def cmd_run(self, opts):
281 del opts # unused
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800282
Kuang-che Wua41525a2018-10-17 23:52:24 +0800283 self.states.load()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800284
Kuang-che Wu8b654092018-11-09 17:56:25 +0800285 try:
286 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
287 self.config['mirror_base'], self.config['work_base'],
288 self.config['session'])
289 common_switch_cmd, common_eval_cmd = self._build_cmds()
290
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800291 if self.config['test_name']:
292 chrome_binaries = determine_chrome_binaries(
293 self.config['chromeos_root'], self.config['test_name'])
294 else:
295 chrome_binaries = None
Kuang-che Wu8b654092018-11-09 17:56:25 +0800296
297 with cros_lab_util.dut_manager(self.config['dut'],
298 lambda: grab_dut(self.config)) as dut:
299 if not dut:
300 raise errors.NoDutAvailable('unable to allocate DUT')
301 assert cros_util.is_dut(dut)
302 if self.config['dut'] == cros_lab_util.LAB_DUT:
303 self.config['allocated_dut'] = dut
304 self.states.save()
305 common_eval_cmd.append(dut)
306
307 diagnoser = diagnoser_cros.CrosDiagnoser(
308 self.states, self.config['session'], path_factory,
309 self.config['chromeos_root'], self.config['chromeos_mirror'],
310 self.config['android_root'], self.config['android_mirror'],
311 self.config['chrome_root'], self.config['chrome_mirror'],
312 self.config['board'], self.config['noisy'], dut)
313
314 eval_cmd = common_eval_cmd + ['--prebuilt', '--reinstall']
315 # Do not specify version for autotest prebuilt switching here. The trick
316 # is that version number is obtained via bisector's environment variable
317 # CROS_VERSION.
318 extra_switch_cmd = common_switch_cmd
319 diagnoser.narrow_down_chromeos_prebuilt(
320 self.config['old'],
321 self.config['new'],
322 eval_cmd,
323 extra_switch_cmd=extra_switch_cmd)
324
325 diagnoser.switch_chromeos_to_old(force=self.config['always_reflash'])
326 util.check_call(*(common_switch_cmd + [diagnoser.cros_old]))
327 util.check_call('ssh', dut, 'rm', '-rf', '/usr/local/autotest')
328
329 try:
330 if diagnoser.narrow_down_android(eval_cmd):
331 return
332 except errors.VerificationFailed:
333 raise
334 except Exception:
335 logger.exception('exception in android bisector before verification; '
336 'assume culprit is not inside android and continue')
337 # Assume it's ok to leave random version of android prebuilt on DUT.
338
339 # Don't --reinstall to keep chrome binaries override.
340 eval_cmd = common_eval_cmd + ['--prebuilt']
341 try:
342 if diagnoser.narrow_down_chrome(
Kuang-che Wu50d8ff42018-11-26 12:48:30 +0800343 eval_cmd, chrome_binaries=chrome_binaries):
Kuang-che Wu8b654092018-11-09 17:56:25 +0800344 return
Kuang-che Wu1dc5bd72019-01-19 00:14:46 +0800345 except errors.VerifyOldBehaviorFailed:
Kuang-che Wu8b654092018-11-09 17:56:25 +0800346 logger.fatal('expect old chrome has old behavior but failed')
347 raise
348 except Exception:
349 logger.exception('exception in chrome bisector before verification; '
350 'assume culprit is not inside chrome and continue')
351
352 eval_cmd = common_eval_cmd + ['--reinstall']
353 diagnoser.narrow_down_chromeos_localbuild(eval_cmd)
354 logger.info('%s done', __file__)
355 except Exception as e:
356 logger.exception('got exception; stop')
357 exception_name = e.__class__.__name__
358 self.states.add_history(
359 'failed', '%s: %s' % (exception_name, e), exception=exception_name)
360
361 def cmd_log(self, opts):
362 self.states.load()
Kuang-che Wua41525a2018-10-17 23:52:24 +0800363 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
364 self.config['mirror_base'], self.config['work_base'],
365 self.config['session'])
Kuang-che Wu8b654092018-11-09 17:56:25 +0800366 diagnoser = diagnoser_cros.CrosDiagnoser(
367 self.states, self.config['session'], path_factory,
368 self.config['chromeos_root'], self.config['chromeos_mirror'],
369 self.config['android_root'], self.config['android_mirror'],
370 self.config['chrome_root'], self.config['chrome_mirror'],
371 self.config['board'], self.config['noisy'], None)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800372
Kuang-che Wu8b654092018-11-09 17:56:25 +0800373 diagnoser.cmd_log(opts.json)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800374
Kuang-che Wue80bb872018-11-15 19:45:25 +0800375 def cmd_view(self, opts):
376 self.states.load()
377 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
378 self.config['mirror_base'], self.config['work_base'],
379 self.config['session'])
380 diagnoser = diagnoser_cros.CrosDiagnoser(
381 self.states, self.config['session'], path_factory,
382 self.config['chromeos_root'], self.config['chromeos_mirror'],
383 self.config['android_root'], self.config['android_mirror'],
384 self.config['chrome_root'], self.config['chrome_mirror'],
385 self.config['board'], self.config['noisy'], None)
386 diagnoser.cmd_view(opts.json, opts.verbose)
387
Kuang-che Wua41525a2018-10-17 23:52:24 +0800388 def create_argument_parser(self):
389 parser = argparse.ArgumentParser()
390 common.add_common_arguments(parser)
391 parser.add_argument('--session_base', default='bisect.sessions')
392 parser.add_argument('--session', help='Session name', required=True)
393 subparsers = parser.add_subparsers(
394 dest='command', title='commands', metavar='<command>')
395
396 parser_init = subparsers.add_parser('init', help='Initialize')
397 group = parser_init.add_argument_group(
398 title='Source tree path options',
399 description='''
400 Specify the paths of chromeos/chrome/android mirror and checkout. They
401 have the same default values as setup_cros_bisect.py, so usually you can
402 omit them and it just works.
403 ''')
404 group.add_argument(
405 '--mirror_base',
406 metavar='MIRROR_BASE',
407 default=configure.get('MIRROR_BASE',
408 setup_cros_bisect.DEFAULT_MIRROR_BASE),
409 help='Directory for mirrors (default: %(default)s)')
410 group.add_argument(
411 '--work_base',
412 metavar='WORK_BASE',
413 default=configure.get('WORK_BASE', setup_cros_bisect.DEFAULT_WORK_BASE),
414 help='Directory for bisection working directories '
415 '(default: %(default)s)')
416 group.add_argument(
417 '--chromeos_root',
418 metavar='CHROMEOS_ROOT',
419 type=cli.argtype_dir_path,
420 default=configure.get('CHROMEOS_ROOT'),
421 help='ChromeOS tree root')
422 group.add_argument(
423 '--chromeos_mirror',
424 type=cli.argtype_dir_path,
425 default=configure.get('CHROMEOS_MIRROR'),
426 help='ChromeOS repo mirror path')
427 group.add_argument(
428 '--android_root',
429 metavar='ANDROID_ROOT',
430 type=cli.argtype_dir_path,
431 default=configure.get('ANDROID_ROOT'),
432 help='Android tree root')
433 group.add_argument(
434 '--android_mirror',
435 type=cli.argtype_dir_path,
436 default=configure.get('ANDROID_MIRROR'),
437 help='Android repo mirror path')
438 group.add_argument(
439 '--chrome_root',
440 metavar='CHROME_ROOT',
441 type=cli.argtype_dir_path,
442 default=configure.get('CHROME_ROOT'),
443 help='Chrome tree root')
444 group.add_argument(
445 '--chrome_mirror',
446 metavar='CHROME_MIRROR',
447 type=cli.argtype_dir_path,
448 default=configure.get('CHROME_MIRROR'),
449 help="chrome's gclient cache dir")
450
Kuang-che Wu248c5182018-10-19 17:08:11 +0800451 group = parser_init.add_argument_group(title='DUT allocation options')
452 group.add_argument(
453 '--dut',
454 metavar='DUT',
455 required=True,
456 help='Address of DUT (Device Under Test). If "%s", DUT will be '
457 'automatically allocated from the lab' % cros_lab_util.LAB_DUT)
458 group.add_argument(
Kuang-che Wufcac3f22019-01-10 00:36:42 +0800459 '--pools',
460 type=cli.argtype_notempty,
Kuang-che Wu8c6d9dd2019-01-16 10:29:13 +0800461 default='suites',
Kuang-che Wufcac3f22019-01-10 00:36:42 +0800462 help='Desired pools, comma separated and ordered by preference '
463 '(default: %(default)s)')
464 group.add_argument(
Kuang-che Wu248c5182018-10-19 17:08:11 +0800465 '--model',
466 metavar='MODEL',
467 help='"model" criteria if DUT is auto allocated from the lab')
468 group.add_argument(
469 '--sku',
470 metavar='SKU',
471 help='"sku" criteria if DUT is auto allocated from the lab')
472
Kuang-che Wua41525a2018-10-17 23:52:24 +0800473 group = parser_init.add_argument_group(title='Essential options')
474 group.add_argument(
Kuang-che Wu248c5182018-10-19 17:08:11 +0800475 '--board',
476 metavar='BOARD',
477 default=configure.get('BOARD'),
478 help='ChromeOS board name; auto detected if DUT is not auto allocated')
479 group.add_argument(
Kuang-che Wua41525a2018-10-17 23:52:24 +0800480 '--old',
481 type=cros_util.argtype_cros_version,
482 required=True,
483 help='ChromeOS version with old behavior')
484 group.add_argument(
485 '--new',
486 type=cros_util.argtype_cros_version,
487 required=True,
488 help='ChromeOS version with new behavior')
Kuang-che Wua41525a2018-10-17 23:52:24 +0800489
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800490 group = parser_init.add_argument_group(
491 title='Options for normal autotest tests')
492 group.add_argument('--test_name', help='Test name')
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800493 group.add_argument(
494 '--fail_to_pass',
495 action='store_true',
496 help='For functional tests: bisect the CL fixed the regression (when '
497 'test became PASS). If not specified, the default is to bisect the CL '
498 'which broke the test (when test became FAIL)')
Kuang-che Wua41525a2018-10-17 23:52:24 +0800499 group.add_argument('--metric', help='Metric name of benchmark test')
500 group.add_argument(
501 '--old_value',
502 type=float,
503 help='For benchmark test, old value of metric')
504 group.add_argument(
505 '--new_value',
506 type=float,
507 help='For benchmark test, new value of metric')
508
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800509 group = parser_init.add_argument_group(title='Options for CTS/GTS tests')
510 group.add_argument('--cts_revision', help='CTS revision, like "9.0_r3"')
511 group.add_argument('--cts_abi', choices=['arm', 'x86'])
512 group.add_argument(
513 '--cts_prefix',
514 help='Prefix of autotest test name, '
515 'like cheets_CTS_N, cheets_CTS_P, cheets_GTS')
516 group.add_argument(
517 '--cts_module', help='CTS/GTS module name, like "CtsCameraTestCases"')
518 group.add_argument(
519 '--cts_test',
520 help='CTS/GTS test name, like '
521 '"android.hardware.cts.CameraTest#testDisplayOrientation"')
522 group.add_argument('--cts_timeout', type=float, help='timeout, in seconds')
523
Kuang-che Wua41525a2018-10-17 23:52:24 +0800524 group = parser_init.add_argument_group(title='Options passed to test_that')
525 group.add_argument(
526 '--args',
527 help='Extra args passed to "test_that --args"; Overrides the default')
528
529 group = parser_init.add_argument_group(title='Bisect behavior options')
530 group.add_argument(
531 '--noisy',
532 help='Enable noisy binary search. Example value: "old=1/10,new=2/3"')
533 group.add_argument(
534 '--always_reflash',
535 action='store_true',
536 help='Do not trust ChromeOS version number of DUT and always reflash. '
537 'This is usually only needed when resume because previous bisect was '
538 'interrupted and the DUT may be in an unexpected state')
539 parser_init.set_defaults(func=self.cmd_init)
540
541 parser_run = subparsers.add_parser('run', help='Start auto bisection')
542 parser_run.set_defaults(func=self.cmd_run)
543
Kuang-che Wu8b654092018-11-09 17:56:25 +0800544 parser_log = subparsers.add_parser(
545 'log', help='Prints what has been done so far')
546 parser_log.add_argument(
547 '--json', action='store_true', help='Machine readable output')
548 parser_log.set_defaults(func=self.cmd_log)
549
Kuang-che Wue80bb872018-11-15 19:45:25 +0800550 parser_view = subparsers.add_parser(
551 'view', help='Prints summary of current status')
552 parser_view.add_argument('--verbose', '-v', action='store_true')
553 parser_view.add_argument(
554 '--json', action='store_true', help='Machine readable output')
555 parser_view.set_defaults(func=self.cmd_view)
556
Kuang-che Wua41525a2018-10-17 23:52:24 +0800557 return parser
558
559 def main(self, args=None):
560 opts = self.argument_parser.parse_args(args)
561 common.config_logging(opts)
562
563 session_base = configure.get('SESSION_BASE', common.DEFAULT_SESSION_BASE)
564 session_file = os.path.join(session_base, opts.session,
565 self.__class__.__name__)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800566 self.states = diagnoser_cros.DiagnoseStates(session_file)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800567 opts.func(opts)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800568
569
570if __name__ == '__main__':
Kuang-che Wua41525a2018-10-17 23:52:24 +0800571 DiagnoseCommandLine().main()