blob: af07921d0e9443bc541195c6035131fdc3616d91 [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
168 if opts.metric:
169 if opts.old_value is None:
170 self.argument_parser.error('--old_value is not provided')
171 if opts.new_value is None:
172 self.argument_parser.error('--new_value is not provided')
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800173 else:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800174 if opts.old_value is not None:
175 self.argument_parser.error(
176 '--old_value is provided but --metric is not')
177 if opts.new_value is not None:
178 self.argument_parser.error(
179 '--new_value is provided but --metric is not')
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800180
Kuang-che Wua41525a2018-10-17 23:52:24 +0800181 def cmd_init(self, opts):
182 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
183 opts.mirror_base, opts.work_base, opts.session)
184 self.check_options(opts, path_factory)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800185
Kuang-che Wua41525a2018-10-17 23:52:24 +0800186 config = dict(
187 session=opts.session,
188 mirror_base=opts.mirror_base,
189 work_base=opts.work_base,
190 chromeos_root=opts.chromeos_root,
191 chromeos_mirror=opts.chromeos_mirror,
192 chrome_root=opts.chrome_root,
193 chrome_mirror=opts.chrome_mirror,
194 android_root=opts.android_root,
195 android_mirror=opts.android_mirror,
Kuang-che Wu248c5182018-10-19 17:08:11 +0800196 dut=opts.dut,
Kuang-che Wufcac3f22019-01-10 00:36:42 +0800197 pools=opts.pools,
Kuang-che Wu248c5182018-10-19 17:08:11 +0800198 model=opts.model,
199 sku=opts.sku,
200 board=opts.board,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800201 old=opts.old,
202 new=opts.new,
203 test_name=opts.test_name,
204 metric=opts.metric,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800205 old_value=opts.old_value,
206 new_value=opts.new_value,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800207 noisy=opts.noisy,
208 test_that_args=opts.args,
209 always_reflash=opts.always_reflash,
210 )
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800211
Kuang-che Wua41525a2018-10-17 23:52:24 +0800212 self.states.init(config)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800213
Kuang-che Wua41525a2018-10-17 23:52:24 +0800214 # Unpack old autotest prebuilt, assume following information don't change
215 # between versions:
216 # - what chrome binaries to run
217 # - dependency labels for DUT allocation
218 common_switch_cmd, _common_eval_cmd = self._build_cmds()
219 util.check_call(*(common_switch_cmd + [self.config['old']]))
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800220
Kuang-che Wua41525a2018-10-17 23:52:24 +0800221 self.states.save()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800222
Kuang-che Wua41525a2018-10-17 23:52:24 +0800223 def _build_cmds(self):
224 # prebuilt version will be specified later.
225 common_switch_cmd = [
226 './switch_autotest_prebuilt.py',
227 '--chromeos_root', self.config['chromeos_root'],
228 '--test_name', self.config['test_name'],
229 '--board', self.config['board'],
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800230 ] # yapf: disable
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800231
Kuang-che Wua41525a2018-10-17 23:52:24 +0800232 common_eval_cmd = [
233 './eval_cros_autotest.py',
234 '--chromeos_root', self.config['chromeos_root'],
235 '--test_name', self.config['test_name'],
236 ] # yapf: disable
237 if self.config['metric']:
238 common_eval_cmd += [
239 '--metric', self.config['metric'],
240 '--old_value', str(self.config['old_value']),
241 '--new_value', str(self.config['new_value']),
242 ] # yapf: disable
243 if self.config['test_that_args']:
244 common_eval_cmd += ['--args', self.config['test_that_args']]
Kuang-che Wud4603d72018-11-29 17:51:21 +0800245 if self.config['test_name'].startswith('telemetry_'):
246 common_eval_cmd += ['--chrome_root', self.config['chrome_root']]
Kuang-che Wua41525a2018-10-17 23:52:24 +0800247 return common_switch_cmd, common_eval_cmd
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800248
Kuang-che Wua41525a2018-10-17 23:52:24 +0800249 def cmd_run(self, opts):
250 del opts # unused
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800251
Kuang-che Wua41525a2018-10-17 23:52:24 +0800252 self.states.load()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800253
Kuang-che Wu8b654092018-11-09 17:56:25 +0800254 try:
255 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
256 self.config['mirror_base'], self.config['work_base'],
257 self.config['session'])
258 common_switch_cmd, common_eval_cmd = self._build_cmds()
259
260 chrome_binaries = determine_chrome_binaries(self.config['chromeos_root'],
261 self.config['test_name'])
262
263 with cros_lab_util.dut_manager(self.config['dut'],
264 lambda: grab_dut(self.config)) as dut:
265 if not dut:
266 raise errors.NoDutAvailable('unable to allocate DUT')
267 assert cros_util.is_dut(dut)
268 if self.config['dut'] == cros_lab_util.LAB_DUT:
269 self.config['allocated_dut'] = dut
270 self.states.save()
271 common_eval_cmd.append(dut)
272
273 diagnoser = diagnoser_cros.CrosDiagnoser(
274 self.states, self.config['session'], path_factory,
275 self.config['chromeos_root'], self.config['chromeos_mirror'],
276 self.config['android_root'], self.config['android_mirror'],
277 self.config['chrome_root'], self.config['chrome_mirror'],
278 self.config['board'], self.config['noisy'], dut)
279
280 eval_cmd = common_eval_cmd + ['--prebuilt', '--reinstall']
281 # Do not specify version for autotest prebuilt switching here. The trick
282 # is that version number is obtained via bisector's environment variable
283 # CROS_VERSION.
284 extra_switch_cmd = common_switch_cmd
285 diagnoser.narrow_down_chromeos_prebuilt(
286 self.config['old'],
287 self.config['new'],
288 eval_cmd,
289 extra_switch_cmd=extra_switch_cmd)
290
291 diagnoser.switch_chromeos_to_old(force=self.config['always_reflash'])
292 util.check_call(*(common_switch_cmd + [diagnoser.cros_old]))
293 util.check_call('ssh', dut, 'rm', '-rf', '/usr/local/autotest')
294
295 try:
296 if diagnoser.narrow_down_android(eval_cmd):
297 return
298 except errors.VerificationFailed:
299 raise
300 except Exception:
301 logger.exception('exception in android bisector before verification; '
302 'assume culprit is not inside android and continue')
303 # Assume it's ok to leave random version of android prebuilt on DUT.
304
305 # Don't --reinstall to keep chrome binaries override.
306 eval_cmd = common_eval_cmd + ['--prebuilt']
307 try:
308 if diagnoser.narrow_down_chrome(
Kuang-che Wu50d8ff42018-11-26 12:48:30 +0800309 eval_cmd, chrome_binaries=chrome_binaries):
Kuang-che Wu8b654092018-11-09 17:56:25 +0800310 return
311 except errors.VerifyOldFailed:
312 logger.fatal('expect old chrome has old behavior but failed')
313 raise
314 except Exception:
315 logger.exception('exception in chrome bisector before verification; '
316 'assume culprit is not inside chrome and continue')
317
318 eval_cmd = common_eval_cmd + ['--reinstall']
319 diagnoser.narrow_down_chromeos_localbuild(eval_cmd)
320 logger.info('%s done', __file__)
321 except Exception as e:
322 logger.exception('got exception; stop')
323 exception_name = e.__class__.__name__
324 self.states.add_history(
325 'failed', '%s: %s' % (exception_name, e), exception=exception_name)
326
327 def cmd_log(self, opts):
328 self.states.load()
Kuang-che Wua41525a2018-10-17 23:52:24 +0800329 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
330 self.config['mirror_base'], self.config['work_base'],
331 self.config['session'])
Kuang-che Wu8b654092018-11-09 17:56:25 +0800332 diagnoser = diagnoser_cros.CrosDiagnoser(
333 self.states, self.config['session'], path_factory,
334 self.config['chromeos_root'], self.config['chromeos_mirror'],
335 self.config['android_root'], self.config['android_mirror'],
336 self.config['chrome_root'], self.config['chrome_mirror'],
337 self.config['board'], self.config['noisy'], None)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800338
Kuang-che Wu8b654092018-11-09 17:56:25 +0800339 diagnoser.cmd_log(opts.json)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800340
Kuang-che Wue80bb872018-11-15 19:45:25 +0800341 def cmd_view(self, opts):
342 self.states.load()
343 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
344 self.config['mirror_base'], self.config['work_base'],
345 self.config['session'])
346 diagnoser = diagnoser_cros.CrosDiagnoser(
347 self.states, self.config['session'], path_factory,
348 self.config['chromeos_root'], self.config['chromeos_mirror'],
349 self.config['android_root'], self.config['android_mirror'],
350 self.config['chrome_root'], self.config['chrome_mirror'],
351 self.config['board'], self.config['noisy'], None)
352 diagnoser.cmd_view(opts.json, opts.verbose)
353
Kuang-che Wua41525a2018-10-17 23:52:24 +0800354 def create_argument_parser(self):
355 parser = argparse.ArgumentParser()
356 common.add_common_arguments(parser)
357 parser.add_argument('--session_base', default='bisect.sessions')
358 parser.add_argument('--session', help='Session name', required=True)
359 subparsers = parser.add_subparsers(
360 dest='command', title='commands', metavar='<command>')
361
362 parser_init = subparsers.add_parser('init', help='Initialize')
363 group = parser_init.add_argument_group(
364 title='Source tree path options',
365 description='''
366 Specify the paths of chromeos/chrome/android mirror and checkout. They
367 have the same default values as setup_cros_bisect.py, so usually you can
368 omit them and it just works.
369 ''')
370 group.add_argument(
371 '--mirror_base',
372 metavar='MIRROR_BASE',
373 default=configure.get('MIRROR_BASE',
374 setup_cros_bisect.DEFAULT_MIRROR_BASE),
375 help='Directory for mirrors (default: %(default)s)')
376 group.add_argument(
377 '--work_base',
378 metavar='WORK_BASE',
379 default=configure.get('WORK_BASE', setup_cros_bisect.DEFAULT_WORK_BASE),
380 help='Directory for bisection working directories '
381 '(default: %(default)s)')
382 group.add_argument(
383 '--chromeos_root',
384 metavar='CHROMEOS_ROOT',
385 type=cli.argtype_dir_path,
386 default=configure.get('CHROMEOS_ROOT'),
387 help='ChromeOS tree root')
388 group.add_argument(
389 '--chromeos_mirror',
390 type=cli.argtype_dir_path,
391 default=configure.get('CHROMEOS_MIRROR'),
392 help='ChromeOS repo mirror path')
393 group.add_argument(
394 '--android_root',
395 metavar='ANDROID_ROOT',
396 type=cli.argtype_dir_path,
397 default=configure.get('ANDROID_ROOT'),
398 help='Android tree root')
399 group.add_argument(
400 '--android_mirror',
401 type=cli.argtype_dir_path,
402 default=configure.get('ANDROID_MIRROR'),
403 help='Android repo mirror path')
404 group.add_argument(
405 '--chrome_root',
406 metavar='CHROME_ROOT',
407 type=cli.argtype_dir_path,
408 default=configure.get('CHROME_ROOT'),
409 help='Chrome tree root')
410 group.add_argument(
411 '--chrome_mirror',
412 metavar='CHROME_MIRROR',
413 type=cli.argtype_dir_path,
414 default=configure.get('CHROME_MIRROR'),
415 help="chrome's gclient cache dir")
416
Kuang-che Wu248c5182018-10-19 17:08:11 +0800417 group = parser_init.add_argument_group(title='DUT allocation options')
418 group.add_argument(
419 '--dut',
420 metavar='DUT',
421 required=True,
422 help='Address of DUT (Device Under Test). If "%s", DUT will be '
423 'automatically allocated from the lab' % cros_lab_util.LAB_DUT)
424 group.add_argument(
Kuang-che Wufcac3f22019-01-10 00:36:42 +0800425 '--pools',
426 type=cli.argtype_notempty,
427 default='bisector,suites',
428 help='Desired pools, comma separated and ordered by preference '
429 '(default: %(default)s)')
430 group.add_argument(
Kuang-che Wu248c5182018-10-19 17:08:11 +0800431 '--model',
432 metavar='MODEL',
433 help='"model" criteria if DUT is auto allocated from the lab')
434 group.add_argument(
435 '--sku',
436 metavar='SKU',
437 help='"sku" criteria if DUT is auto allocated from the lab')
438
Kuang-che Wua41525a2018-10-17 23:52:24 +0800439 group = parser_init.add_argument_group(title='Essential options')
440 group.add_argument(
Kuang-che Wu248c5182018-10-19 17:08:11 +0800441 '--board',
442 metavar='BOARD',
443 default=configure.get('BOARD'),
444 help='ChromeOS board name; auto detected if DUT is not auto allocated')
445 group.add_argument(
Kuang-che Wua41525a2018-10-17 23:52:24 +0800446 '--old',
447 type=cros_util.argtype_cros_version,
448 required=True,
449 help='ChromeOS version with old behavior')
450 group.add_argument(
451 '--new',
452 type=cros_util.argtype_cros_version,
453 required=True,
454 help='ChromeOS version with new behavior')
455 group.add_argument('--test_name', required=True, help='Test name')
456
457 group = parser_init.add_argument_group(title='Options for benchmark test')
458 group.add_argument('--metric', help='Metric name of benchmark test')
459 group.add_argument(
460 '--old_value',
461 type=float,
462 help='For benchmark test, old value of metric')
463 group.add_argument(
464 '--new_value',
465 type=float,
466 help='For benchmark test, new value of metric')
467
Kuang-che Wua41525a2018-10-17 23:52:24 +0800468 group = parser_init.add_argument_group(title='Options passed to test_that')
469 group.add_argument(
470 '--args',
471 help='Extra args passed to "test_that --args"; Overrides the default')
472
473 group = parser_init.add_argument_group(title='Bisect behavior options')
474 group.add_argument(
475 '--noisy',
476 help='Enable noisy binary search. Example value: "old=1/10,new=2/3"')
477 group.add_argument(
478 '--always_reflash',
479 action='store_true',
480 help='Do not trust ChromeOS version number of DUT and always reflash. '
481 'This is usually only needed when resume because previous bisect was '
482 'interrupted and the DUT may be in an unexpected state')
483 parser_init.set_defaults(func=self.cmd_init)
484
485 parser_run = subparsers.add_parser('run', help='Start auto bisection')
486 parser_run.set_defaults(func=self.cmd_run)
487
Kuang-che Wu8b654092018-11-09 17:56:25 +0800488 parser_log = subparsers.add_parser(
489 'log', help='Prints what has been done so far')
490 parser_log.add_argument(
491 '--json', action='store_true', help='Machine readable output')
492 parser_log.set_defaults(func=self.cmd_log)
493
Kuang-che Wue80bb872018-11-15 19:45:25 +0800494 parser_view = subparsers.add_parser(
495 'view', help='Prints summary of current status')
496 parser_view.add_argument('--verbose', '-v', action='store_true')
497 parser_view.add_argument(
498 '--json', action='store_true', help='Machine readable output')
499 parser_view.set_defaults(func=self.cmd_view)
500
Kuang-che Wua41525a2018-10-17 23:52:24 +0800501 return parser
502
503 def main(self, args=None):
504 opts = self.argument_parser.parse_args(args)
505 common.config_logging(opts)
506
507 session_base = configure.get('SESSION_BASE', common.DEFAULT_SESSION_BASE)
508 session_file = os.path.join(session_base, opts.session,
509 self.__class__.__name__)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800510 self.states = diagnoser_cros.DiagnoseStates(session_file)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800511 opts.func(opts)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800512
513
514if __name__ == '__main__':
Kuang-che Wua41525a2018-10-17 23:52:24 +0800515 DiagnoseCommandLine().main()