blob: f9e089a560b33dc5bdf6991ef78c2af29324c6f8 [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'],
85 extra_labels=extra_labels)
86
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080087 if not host:
88 logger.error('unable to allocate dut')
89 return None
90
91 logger.info('allocated host %s', host)
92 return host
93
94
95def may_depend_on_extra_chrome_binaries(autotest_dir, test_name):
96 info = cros_util.get_autotest_test_info(autotest_dir, test_name)
Kuang-che Wu0f1c3b02019-01-10 01:21:01 +080097 assert info, 'incorrect test name? %s' % test_name
Kuang-che Wu1fcc0222018-07-07 16:43:22 +080098 dirpath = os.path.dirname(info.path)
99 for pypath in glob.glob(os.path.join(dirpath, '*.py')):
100 if 'ChromeBinaryTest' in open(pypath).read():
101 return True
102 return False
103
104
Kuang-che Wua41525a2018-10-17 23:52:24 +0800105def determine_chrome_binaries(chromeos_root, test_name):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800106 chrome_binaries = None
107 for name_pattern, binaries in CHROME_BINARIES_OF_TEST.items():
Kuang-che Wua41525a2018-10-17 23:52:24 +0800108 if fnmatch.fnmatch(test_name, name_pattern):
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800109 chrome_binaries = binaries
110 break
111
Kuang-che Wua41525a2018-10-17 23:52:24 +0800112 autotest_dir = os.path.join(chromeos_root, cros_util.prebuilt_autotest_dir)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800113 if chrome_binaries:
114 logger.info('This test depends on chrome binary: %s', chrome_binaries)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800115 elif may_depend_on_extra_chrome_binaries(autotest_dir, test_name):
Kuang-che Wu74768d32018-09-07 12:03:24 +0800116 logger.warning(
117 '%s code used ChromeBinaryTest but the binary is unknown; '
Kuang-che Wua41525a2018-10-17 23:52:24 +0800118 'please update CHROME_BINARIES_OF_TEST table', test_name)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800119 return chrome_binaries
120
121
Kuang-che Wua41525a2018-10-17 23:52:24 +0800122class DiagnoseCommandLine(object):
123 """Diagnose command line interface."""
Kuang-che Wud8fc9572018-10-03 21:00:41 +0800124
Kuang-che Wua41525a2018-10-17 23:52:24 +0800125 def __init__(self):
126 common.init()
127 self.argument_parser = self.create_argument_parser()
128 self.states = None
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800129
Kuang-che Wua41525a2018-10-17 23:52:24 +0800130 @property
131 def config(self):
132 return self.states.config
133
134 def check_options(self, opts, path_factory):
135 if not opts.chromeos_mirror:
136 opts.chromeos_mirror = path_factory.get_chromeos_mirror()
137 logger.info('chromeos_mirror = %s', opts.chromeos_mirror)
138 if not opts.chromeos_root:
139 opts.chromeos_root = path_factory.get_chromeos_tree()
140 logger.info('chromeos_root = %s', opts.chromeos_root)
141 if not opts.chrome_mirror:
142 opts.chrome_mirror = path_factory.get_chrome_cache()
143 logger.info('chrome_mirror = %s', opts.chrome_mirror)
144 if not opts.chrome_root:
145 opts.chrome_root = path_factory.get_chrome_tree()
146 logger.info('chrome_root = %s', opts.chrome_root)
147
Kuang-che Wu248c5182018-10-19 17:08:11 +0800148 if opts.dut == cros_lab_util.LAB_DUT:
149 if not opts.model and not opts.sku:
150 self.argument_parser.error(
151 'either --model or --sku need to be specified if DUT is "%s"' %
152 cros_lab_util.LAB_DUT)
153 # Board name cannot be deduced from auto allocated devices because they
154 # may be provisioned with image of unexpected board.
155 if not opts.board:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800156 self.argument_parser.error('--board need to be specified if DUT is "%s"'
157 % cros_lab_util.LAB_DUT)
Kuang-che Wu248c5182018-10-19 17:08:11 +0800158 else:
159 if not opts.board:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800160 opts.board = cros_util.query_dut_board(opts.dut)
161
162 if cros_util.is_cros_short_version(opts.old):
163 opts.old = cros_util.version_to_full(opts.board, opts.old)
164 if cros_util.is_cros_short_version(opts.new):
165 opts.new = cros_util.version_to_full(opts.board, opts.new)
166
167 if opts.metric:
168 if opts.old_value is None:
169 self.argument_parser.error('--old_value is not provided')
170 if opts.new_value is None:
171 self.argument_parser.error('--new_value is not provided')
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800172 else:
Kuang-che Wua41525a2018-10-17 23:52:24 +0800173 if opts.old_value is not None:
174 self.argument_parser.error(
175 '--old_value is provided but --metric is not')
176 if opts.new_value is not None:
177 self.argument_parser.error(
178 '--new_value is provided but --metric is not')
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800179
Kuang-che Wua41525a2018-10-17 23:52:24 +0800180 def cmd_init(self, opts):
181 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
182 opts.mirror_base, opts.work_base, opts.session)
183 self.check_options(opts, path_factory)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800184
Kuang-che Wua41525a2018-10-17 23:52:24 +0800185 config = dict(
186 session=opts.session,
187 mirror_base=opts.mirror_base,
188 work_base=opts.work_base,
189 chromeos_root=opts.chromeos_root,
190 chromeos_mirror=opts.chromeos_mirror,
191 chrome_root=opts.chrome_root,
192 chrome_mirror=opts.chrome_mirror,
193 android_root=opts.android_root,
194 android_mirror=opts.android_mirror,
Kuang-che Wu248c5182018-10-19 17:08:11 +0800195 dut=opts.dut,
196 model=opts.model,
197 sku=opts.sku,
198 board=opts.board,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800199 old=opts.old,
200 new=opts.new,
201 test_name=opts.test_name,
202 metric=opts.metric,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800203 old_value=opts.old_value,
204 new_value=opts.new_value,
Kuang-che Wua41525a2018-10-17 23:52:24 +0800205 noisy=opts.noisy,
206 test_that_args=opts.args,
207 always_reflash=opts.always_reflash,
208 )
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800209
Kuang-che Wua41525a2018-10-17 23:52:24 +0800210 self.states.init(config)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800211
Kuang-che Wua41525a2018-10-17 23:52:24 +0800212 # Unpack old autotest prebuilt, assume following information don't change
213 # between versions:
214 # - what chrome binaries to run
215 # - dependency labels for DUT allocation
216 common_switch_cmd, _common_eval_cmd = self._build_cmds()
217 util.check_call(*(common_switch_cmd + [self.config['old']]))
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800218
Kuang-che Wua41525a2018-10-17 23:52:24 +0800219 self.states.save()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800220
Kuang-che Wua41525a2018-10-17 23:52:24 +0800221 def _build_cmds(self):
222 # prebuilt version will be specified later.
223 common_switch_cmd = [
224 './switch_autotest_prebuilt.py',
225 '--chromeos_root', self.config['chromeos_root'],
226 '--test_name', self.config['test_name'],
227 '--board', self.config['board'],
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800228 ] # yapf: disable
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800229
Kuang-che Wua41525a2018-10-17 23:52:24 +0800230 common_eval_cmd = [
231 './eval_cros_autotest.py',
232 '--chromeos_root', self.config['chromeos_root'],
233 '--test_name', self.config['test_name'],
234 ] # yapf: disable
235 if self.config['metric']:
236 common_eval_cmd += [
237 '--metric', self.config['metric'],
238 '--old_value', str(self.config['old_value']),
239 '--new_value', str(self.config['new_value']),
240 ] # yapf: disable
241 if self.config['test_that_args']:
242 common_eval_cmd += ['--args', self.config['test_that_args']]
Kuang-che Wud4603d72018-11-29 17:51:21 +0800243 if self.config['test_name'].startswith('telemetry_'):
244 common_eval_cmd += ['--chrome_root', self.config['chrome_root']]
Kuang-che Wua41525a2018-10-17 23:52:24 +0800245 return common_switch_cmd, common_eval_cmd
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800246
Kuang-che Wua41525a2018-10-17 23:52:24 +0800247 def cmd_run(self, opts):
248 del opts # unused
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800249
Kuang-che Wua41525a2018-10-17 23:52:24 +0800250 self.states.load()
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800251
Kuang-che Wu8b654092018-11-09 17:56:25 +0800252 try:
253 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
254 self.config['mirror_base'], self.config['work_base'],
255 self.config['session'])
256 common_switch_cmd, common_eval_cmd = self._build_cmds()
257
258 chrome_binaries = determine_chrome_binaries(self.config['chromeos_root'],
259 self.config['test_name'])
260
261 with cros_lab_util.dut_manager(self.config['dut'],
262 lambda: grab_dut(self.config)) as dut:
263 if not dut:
264 raise errors.NoDutAvailable('unable to allocate DUT')
265 assert cros_util.is_dut(dut)
266 if self.config['dut'] == cros_lab_util.LAB_DUT:
267 self.config['allocated_dut'] = dut
268 self.states.save()
269 common_eval_cmd.append(dut)
270
271 diagnoser = diagnoser_cros.CrosDiagnoser(
272 self.states, self.config['session'], path_factory,
273 self.config['chromeos_root'], self.config['chromeos_mirror'],
274 self.config['android_root'], self.config['android_mirror'],
275 self.config['chrome_root'], self.config['chrome_mirror'],
276 self.config['board'], self.config['noisy'], dut)
277
278 eval_cmd = common_eval_cmd + ['--prebuilt', '--reinstall']
279 # Do not specify version for autotest prebuilt switching here. The trick
280 # is that version number is obtained via bisector's environment variable
281 # CROS_VERSION.
282 extra_switch_cmd = common_switch_cmd
283 diagnoser.narrow_down_chromeos_prebuilt(
284 self.config['old'],
285 self.config['new'],
286 eval_cmd,
287 extra_switch_cmd=extra_switch_cmd)
288
289 diagnoser.switch_chromeos_to_old(force=self.config['always_reflash'])
290 util.check_call(*(common_switch_cmd + [diagnoser.cros_old]))
291 util.check_call('ssh', dut, 'rm', '-rf', '/usr/local/autotest')
292
293 try:
294 if diagnoser.narrow_down_android(eval_cmd):
295 return
296 except errors.VerificationFailed:
297 raise
298 except Exception:
299 logger.exception('exception in android bisector before verification; '
300 'assume culprit is not inside android and continue')
301 # Assume it's ok to leave random version of android prebuilt on DUT.
302
303 # Don't --reinstall to keep chrome binaries override.
304 eval_cmd = common_eval_cmd + ['--prebuilt']
305 try:
306 if diagnoser.narrow_down_chrome(
Kuang-che Wu50d8ff42018-11-26 12:48:30 +0800307 eval_cmd, chrome_binaries=chrome_binaries):
Kuang-che Wu8b654092018-11-09 17:56:25 +0800308 return
309 except errors.VerifyOldFailed:
310 logger.fatal('expect old chrome has old behavior but failed')
311 raise
312 except Exception:
313 logger.exception('exception in chrome bisector before verification; '
314 'assume culprit is not inside chrome and continue')
315
316 eval_cmd = common_eval_cmd + ['--reinstall']
317 diagnoser.narrow_down_chromeos_localbuild(eval_cmd)
318 logger.info('%s done', __file__)
319 except Exception as e:
320 logger.exception('got exception; stop')
321 exception_name = e.__class__.__name__
322 self.states.add_history(
323 'failed', '%s: %s' % (exception_name, e), exception=exception_name)
324
325 def cmd_log(self, opts):
326 self.states.load()
Kuang-che Wua41525a2018-10-17 23:52:24 +0800327 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
328 self.config['mirror_base'], self.config['work_base'],
329 self.config['session'])
Kuang-che Wu8b654092018-11-09 17:56:25 +0800330 diagnoser = diagnoser_cros.CrosDiagnoser(
331 self.states, self.config['session'], path_factory,
332 self.config['chromeos_root'], self.config['chromeos_mirror'],
333 self.config['android_root'], self.config['android_mirror'],
334 self.config['chrome_root'], self.config['chrome_mirror'],
335 self.config['board'], self.config['noisy'], None)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800336
Kuang-che Wu8b654092018-11-09 17:56:25 +0800337 diagnoser.cmd_log(opts.json)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800338
Kuang-che Wue80bb872018-11-15 19:45:25 +0800339 def cmd_view(self, opts):
340 self.states.load()
341 path_factory = setup_cros_bisect.DefaultProjectPathFactory(
342 self.config['mirror_base'], self.config['work_base'],
343 self.config['session'])
344 diagnoser = diagnoser_cros.CrosDiagnoser(
345 self.states, self.config['session'], path_factory,
346 self.config['chromeos_root'], self.config['chromeos_mirror'],
347 self.config['android_root'], self.config['android_mirror'],
348 self.config['chrome_root'], self.config['chrome_mirror'],
349 self.config['board'], self.config['noisy'], None)
350 diagnoser.cmd_view(opts.json, opts.verbose)
351
Kuang-che Wua41525a2018-10-17 23:52:24 +0800352 def create_argument_parser(self):
353 parser = argparse.ArgumentParser()
354 common.add_common_arguments(parser)
355 parser.add_argument('--session_base', default='bisect.sessions')
356 parser.add_argument('--session', help='Session name', required=True)
357 subparsers = parser.add_subparsers(
358 dest='command', title='commands', metavar='<command>')
359
360 parser_init = subparsers.add_parser('init', help='Initialize')
361 group = parser_init.add_argument_group(
362 title='Source tree path options',
363 description='''
364 Specify the paths of chromeos/chrome/android mirror and checkout. They
365 have the same default values as setup_cros_bisect.py, so usually you can
366 omit them and it just works.
367 ''')
368 group.add_argument(
369 '--mirror_base',
370 metavar='MIRROR_BASE',
371 default=configure.get('MIRROR_BASE',
372 setup_cros_bisect.DEFAULT_MIRROR_BASE),
373 help='Directory for mirrors (default: %(default)s)')
374 group.add_argument(
375 '--work_base',
376 metavar='WORK_BASE',
377 default=configure.get('WORK_BASE', setup_cros_bisect.DEFAULT_WORK_BASE),
378 help='Directory for bisection working directories '
379 '(default: %(default)s)')
380 group.add_argument(
381 '--chromeos_root',
382 metavar='CHROMEOS_ROOT',
383 type=cli.argtype_dir_path,
384 default=configure.get('CHROMEOS_ROOT'),
385 help='ChromeOS tree root')
386 group.add_argument(
387 '--chromeos_mirror',
388 type=cli.argtype_dir_path,
389 default=configure.get('CHROMEOS_MIRROR'),
390 help='ChromeOS repo mirror path')
391 group.add_argument(
392 '--android_root',
393 metavar='ANDROID_ROOT',
394 type=cli.argtype_dir_path,
395 default=configure.get('ANDROID_ROOT'),
396 help='Android tree root')
397 group.add_argument(
398 '--android_mirror',
399 type=cli.argtype_dir_path,
400 default=configure.get('ANDROID_MIRROR'),
401 help='Android repo mirror path')
402 group.add_argument(
403 '--chrome_root',
404 metavar='CHROME_ROOT',
405 type=cli.argtype_dir_path,
406 default=configure.get('CHROME_ROOT'),
407 help='Chrome tree root')
408 group.add_argument(
409 '--chrome_mirror',
410 metavar='CHROME_MIRROR',
411 type=cli.argtype_dir_path,
412 default=configure.get('CHROME_MIRROR'),
413 help="chrome's gclient cache dir")
414
Kuang-che Wu248c5182018-10-19 17:08:11 +0800415 group = parser_init.add_argument_group(title='DUT allocation options')
416 group.add_argument(
417 '--dut',
418 metavar='DUT',
419 required=True,
420 help='Address of DUT (Device Under Test). If "%s", DUT will be '
421 'automatically allocated from the lab' % cros_lab_util.LAB_DUT)
422 group.add_argument(
423 '--model',
424 metavar='MODEL',
425 help='"model" criteria if DUT is auto allocated from the lab')
426 group.add_argument(
427 '--sku',
428 metavar='SKU',
429 help='"sku" criteria if DUT is auto allocated from the lab')
430
Kuang-che Wua41525a2018-10-17 23:52:24 +0800431 group = parser_init.add_argument_group(title='Essential options')
432 group.add_argument(
Kuang-che Wu248c5182018-10-19 17:08:11 +0800433 '--board',
434 metavar='BOARD',
435 default=configure.get('BOARD'),
436 help='ChromeOS board name; auto detected if DUT is not auto allocated')
437 group.add_argument(
Kuang-che Wua41525a2018-10-17 23:52:24 +0800438 '--old',
439 type=cros_util.argtype_cros_version,
440 required=True,
441 help='ChromeOS version with old behavior')
442 group.add_argument(
443 '--new',
444 type=cros_util.argtype_cros_version,
445 required=True,
446 help='ChromeOS version with new behavior')
447 group.add_argument('--test_name', required=True, help='Test name')
448
449 group = parser_init.add_argument_group(title='Options for benchmark test')
450 group.add_argument('--metric', help='Metric name of benchmark test')
451 group.add_argument(
452 '--old_value',
453 type=float,
454 help='For benchmark test, old value of metric')
455 group.add_argument(
456 '--new_value',
457 type=float,
458 help='For benchmark test, new value of metric')
459
Kuang-che Wua41525a2018-10-17 23:52:24 +0800460 group = parser_init.add_argument_group(title='Options passed to test_that')
461 group.add_argument(
462 '--args',
463 help='Extra args passed to "test_that --args"; Overrides the default')
464
465 group = parser_init.add_argument_group(title='Bisect behavior options')
466 group.add_argument(
467 '--noisy',
468 help='Enable noisy binary search. Example value: "old=1/10,new=2/3"')
469 group.add_argument(
470 '--always_reflash',
471 action='store_true',
472 help='Do not trust ChromeOS version number of DUT and always reflash. '
473 'This is usually only needed when resume because previous bisect was '
474 'interrupted and the DUT may be in an unexpected state')
475 parser_init.set_defaults(func=self.cmd_init)
476
477 parser_run = subparsers.add_parser('run', help='Start auto bisection')
478 parser_run.set_defaults(func=self.cmd_run)
479
Kuang-che Wu8b654092018-11-09 17:56:25 +0800480 parser_log = subparsers.add_parser(
481 'log', help='Prints what has been done so far')
482 parser_log.add_argument(
483 '--json', action='store_true', help='Machine readable output')
484 parser_log.set_defaults(func=self.cmd_log)
485
Kuang-che Wue80bb872018-11-15 19:45:25 +0800486 parser_view = subparsers.add_parser(
487 'view', help='Prints summary of current status')
488 parser_view.add_argument('--verbose', '-v', action='store_true')
489 parser_view.add_argument(
490 '--json', action='store_true', help='Machine readable output')
491 parser_view.set_defaults(func=self.cmd_view)
492
Kuang-che Wua41525a2018-10-17 23:52:24 +0800493 return parser
494
495 def main(self, args=None):
496 opts = self.argument_parser.parse_args(args)
497 common.config_logging(opts)
498
499 session_base = configure.get('SESSION_BASE', common.DEFAULT_SESSION_BASE)
500 session_file = os.path.join(session_base, opts.session,
501 self.__class__.__name__)
Kuang-che Wu8b654092018-11-09 17:56:25 +0800502 self.states = diagnoser_cros.DiagnoseStates(session_file)
Kuang-che Wua41525a2018-10-17 23:52:24 +0800503 opts.func(opts)
Kuang-che Wu1fcc0222018-07-07 16:43:22 +0800504
505
506if __name__ == '__main__':
Kuang-che Wua41525a2018-10-17 23:52:24 +0800507 DiagnoseCommandLine().main()