v8/tools/testrunner/base_runner.py
Michal Majewski f5c6daf186 Deprecate multiple arch/mode options in the testrunner
Bug: v8:6917
Change-Id: I45a3a683388341d61989473ebd0b761a1ad8a9e3
Reviewed-on: https://chromium-review.googlesource.com/721659
Commit-Queue: Michał Majewski <majeski@google.com>
Reviewed-by: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48676}
2017-10-18 10:58:36 +00:00

356 lines
11 KiB
Python

# Copyright 2017 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import optparse
import os
import sys
# Add testrunner to the path.
sys.path.insert(
0,
os.path.dirname(
os.path.dirname(os.path.abspath(__file__))))
from local import utils
BASE_DIR = (
os.path.dirname(
os.path.dirname(
os.path.dirname(
os.path.abspath(__file__)))))
DEFAULT_OUT_GN = 'out.gn'
ARCH_GUESS = utils.DefaultArch()
DEBUG_FLAGS = ["--nohard-abort", "--enable-slow-asserts", "--verify-heap"]
RELEASE_FLAGS = ["--nohard-abort"]
MODES = {
"debug": {
"flags": DEBUG_FLAGS,
"timeout_scalefactor": 4,
"status_mode": "debug",
"execution_mode": "debug",
"output_folder": "debug",
},
"optdebug": {
"flags": DEBUG_FLAGS,
"timeout_scalefactor": 4,
"status_mode": "debug",
"execution_mode": "debug",
"output_folder": "optdebug",
},
"release": {
"flags": RELEASE_FLAGS,
"timeout_scalefactor": 1,
"status_mode": "release",
"execution_mode": "release",
"output_folder": "release",
},
# Normal trybot release configuration. There, dchecks are always on which
# implies debug is set. Hence, the status file needs to assume debug-like
# behavior/timeouts.
"tryrelease": {
"flags": RELEASE_FLAGS,
"timeout_scalefactor": 1,
"status_mode": "debug",
"execution_mode": "release",
"output_folder": "release",
},
# This mode requires v8 to be compiled with dchecks and slow dchecks.
"slowrelease": {
"flags": RELEASE_FLAGS + ["--enable-slow-asserts"],
"timeout_scalefactor": 2,
"status_mode": "debug",
"execution_mode": "release",
"output_folder": "release",
},
}
SUPPORTED_ARCHS = [
"android_arm",
"android_arm64",
"android_ia32",
"android_x64",
"arm",
"ia32",
"mips",
"mipsel",
"mips64",
"mips64el",
"s390",
"s390x",
"ppc",
"ppc64",
"x64",
"x32",
"arm64",
]
# Map of test name synonyms to lists of test suites. Should be ordered by
# expected runtimes (suites with slow test cases first). These groups are
# invoked in separate steps on the bots.
TEST_MAP = {
# This needs to stay in sync with test/bot_default.isolate.
"bot_default": [
"debugger",
"mjsunit",
"cctest",
"wasm-spec-tests",
"inspector",
"webkit",
"mkgrokdump",
"fuzzer",
"message",
"preparser",
"intl",
"unittests",
],
# This needs to stay in sync with test/default.isolate.
"default": [
"debugger",
"mjsunit",
"cctest",
"wasm-spec-tests",
"inspector",
"mkgrokdump",
"fuzzer",
"message",
"preparser",
"intl",
"unittests",
],
# This needs to stay in sync with test/optimize_for_size.isolate.
"optimize_for_size": [
"debugger",
"mjsunit",
"cctest",
"inspector",
"webkit",
"intl",
],
"unittests": [
"unittests",
],
}
class TestRunnerError(Exception):
pass
class BaseTestRunner(object):
def __init__(self):
self.outdir = None
self.arch = None
self.mode = None
self.auto_detect = None
def execute(self):
try:
options, args = self._parse_args()
return self._do_execute(options, args)
except TestRunnerError:
return 1
def _parse_args(self):
parser = optparse.OptionParser()
parser.usage = '%prog [options] [tests]'
parser.description = """TESTS: %s""" % (TEST_MAP["default"])
self._add_parser_default_options(parser)
self._add_parser_options(parser)
options, args = parser.parse_args()
try:
self._process_default_options(options)
self._process_options(options)
except TestRunnerError:
parser.print_help()
raise
return options, args
def _add_parser_default_options(self, parser):
parser.add_option("--gn", help="Scan out.gn for the last built"
" configuration",
default=False, action="store_true")
parser.add_option("--outdir", help="Base directory with compile output",
default="out")
parser.add_option("--buildbot",
help="Adapt to path structure used on buildbots",
default=False, action="store_true")
parser.add_option("--arch",
help=("The architecture to run tests for, "
"'auto' or 'native' for auto-detect: %s" %
SUPPORTED_ARCHS))
parser.add_option("-m", "--mode",
help="The test mode in which to run (uppercase for ninja"
" and buildbot builds): %s" % MODES.keys())
def _add_parser_options(self, parser):
pass
def _process_default_options(self, options):
# Try to autodetect configuration based on the build if GN was used.
# This can't be ovveridden by cmd-line arguments.
if options.gn:
outdir = self._get_gn_outdir()
else:
outdir = options.outdir
self.auto_detect = self._read_build_config(outdir, options)
if not self.auto_detect:
if any(map(lambda v: v and ',' in v,
[options.arch, options.mode])):
print 'Multiple arch/mode are deprecated'
raise TestRunnerError()
self.outdir = outdir
if not options.arch or not options.mode:
print('Autodetect mode is not available and therefore '
'--arch and --mode options are required')
raise TestRunnerError()
self.arch = options.arch
self.mode = options.mode
if not self._buildbot_to_v8_mode(self.mode) in MODES:
print "Unknown mode %s" % self.mode
raise TestRunnerError()
if self.arch in ["auto", "native"]:
self.arch = ARCH_GUESS
if not self.arch in SUPPORTED_ARCHS:
print "Unknown architecture %s" % self.arch
raise TestRunnerError()
def _get_gn_outdir(self):
gn_out_dir = os.path.join(BASE_DIR, DEFAULT_OUT_GN)
latest_timestamp = -1
latest_config = None
for gn_config in os.listdir(gn_out_dir):
gn_config_dir = os.path.join(gn_out_dir, gn_config)
if not os.path.isdir(gn_config_dir):
continue
if os.path.getmtime(gn_config_dir) > latest_timestamp:
latest_timestamp = os.path.getmtime(gn_config_dir)
latest_config = gn_config
if latest_config:
print(">>> Latest GN build found: %s" % latest_config)
return os.path.join(DEFAULT_OUT_GN, latest_config)
# Auto-detect test configurations based on the build (GN only).
# sets:
# - arch
# - mode
# - outdir
def _read_build_config(self, outdir, options):
if options.buildbot:
build_config_path = os.path.join(
BASE_DIR, outdir, options.mode, "v8_build_config.json")
else:
build_config_path = os.path.join(
BASE_DIR, outdir, "v8_build_config.json")
if not os.path.exists(build_config_path):
return False
with open(build_config_path) as f:
try:
build_config = json.load(f)
except Exception:
print("%s exists but contains invalid json. Is your build up-to-date?"
% build_config_path)
raise TestRunnerError()
# In auto-detect mode the outdir is always where we found the build
# config.
# This ensures that we'll also take the build products from there.
self.outdir = os.path.dirname(build_config_path)
# In V8 land, GN's x86 is called ia32.
if build_config["v8_target_cpu"] == "x86":
build_config["v8_target_cpu"] = "ia32"
if options.mode:
# In auto-detect mode we don't use the mode for more path-magic.
# Therefore transform the buildbot mode here to fit to the GN build
# config.
options.mode = self._buildbot_to_v8_mode(options.mode)
# TODO(majeski): merge next two loops and put everything in self.
# Get options from the build config. Sanity check that we're not trying to
# use inconsistent options.
for param, value in (
('arch', build_config["v8_target_cpu"]),
('mode', 'debug' if build_config["is_debug"] else 'release'),
):
cmd_line_value = getattr(options, param)
if (cmd_line_value not in [None, True, False] and
cmd_line_value != value):
# TODO(machenbach): This is for string options only. Requires
# options to not have default values. We should make this more
# modular and implement it in our own version of the option parser.
print("Attempted to set %s to %s, while build is %s." %
(param, cmd_line_value, value))
raise TestRunnerError()
if cmd_line_value == True and value == False:
print("Attempted to turn on %s, but it's not available." % param)
raise TestRunnerError()
if cmd_line_value != value:
print(">>> Auto-detected %s=%s" % (param, value))
setattr(self, param, value)
# Update options based on the build config. Sanity check that we're not
# trying to use inconsistent options.
for param, value in (
('asan', build_config["is_asan"]),
('dcheck_always_on', build_config["dcheck_always_on"]),
('gcov_coverage', build_config["is_gcov_coverage"]),
('msan', build_config["is_msan"]),
('no_i18n', not build_config["v8_enable_i18n_support"]),
('no_snap', not build_config["v8_use_snapshot"]),
('tsan', build_config["is_tsan"]),
('ubsan_vptr', build_config["is_ubsan_vptr"]),
):
cmd_line_value = getattr(options, param)
if (cmd_line_value not in [None, True, False] and
cmd_line_value != value):
# TODO(machenbach): This is for string options only. Requires
# options to not have default values. We should make this more
# modular and implement it in our own version of the option parser.
print("Attempted to set %s to %s, while build is %s." %
(param, cmd_line_value, value))
raise TestRunnerError()
if cmd_line_value == True and value == False:
print("Attempted to turn on %s, but it's not available." % param)
raise TestRunnerError()
if cmd_line_value != value:
print(">>> Auto-detected %s=%s" % (param, value))
setattr(options, param, value)
return True
def _buildbot_to_v8_mode(self, config):
"""Convert buildbot build configs to configs understood by the v8 runner.
V8 configs are always lower case and without the additional _x64 suffix
for 64 bit builds on windows with ninja.
"""
mode = config[:-4] if config.endswith('_x64') else config
return mode.lower()
def _process_options(self, options):
pass
# TODO(majeski): remove options & args parameters
def _do_execute(self, options, args):
raise NotImplementedError()