Reland "[foozzie] Refactor command abstraction"

This is a reland of 1d493d31ce

Original change's description:
> [foozzie] Refactor command abstraction
> 
> This moves code for running d8 into its own class. No functional
> changes intended.
> 
> No-Try: true
> Bug: chromium:1023091
> Change-Id: I7cbfeebd2911dc758322f89cf93666550f2956d9
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1906378
> Commit-Queue: Michael Achenbach <machenbach@chromium.org>
> Reviewed-by: Tamer Tas <tmrts@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#64928}

Bug: chromium:1023091
Change-Id: I7df6e12084e20510a400ce209827c2bba8325f86
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1914209
Reviewed-by: Tamer Tas <tmrts@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64966}
This commit is contained in:
Michael Achenbach 2019-11-14 15:56:02 +01:00 committed by Commit Bot
parent 72d440d97d
commit 2475c91bb3
2 changed files with 86 additions and 45 deletions

View File

@ -4,11 +4,82 @@
# Fork from commands.py and output.py in v8 test driver. # Fork from commands.py and output.py in v8 test driver.
import os
import signal import signal
import subprocess import subprocess
import sys import sys
from threading import Event, Timer from threading import Event, Timer
import v8_fuzz_config
# List of default flags passed to each d8 run.
DEFAULT_FLAGS = [
'--correctness-fuzzer-suppressions',
'--expose-gc',
'--allow-natives-syntax',
'--invoke-weak-callbacks',
'--omit-quit',
'--es-staging',
'--wasm-staging',
'--no-wasm-async-compilation',
'--suppress-asm-messages',
]
BASE_PATH = os.path.dirname(os.path.abspath(__file__))
# List of files passed to each d8 run before the testcase.
DEFAULT_FILES = [
os.path.join(BASE_PATH, 'v8_mock.js'),
os.path.join(BASE_PATH, 'v8_suppressions.js'),
]
# Architecture-specific mock file
ARCH_MOCKS = os.path.join(BASE_PATH, 'v8_mock_archs.js')
# Timeout in seconds for one d8 run.
TIMEOUT = 3
def _startup_files(options):
"""Default files and optional architecture-specific mock file."""
files = DEFAULT_FILES[:]
if options.first_arch != options.second_arch:
files.append(ARCH_MOCKS)
return files
class Command(object):
"""Represents a configuration for running V8 multiple times with certain
flags and files.
"""
def __init__(self, options, label, executable, config_flags):
self.label = label
self.executable = executable
self.config_flags = config_flags
self.common_flags = DEFAULT_FLAGS[:]
self.common_flags.extend(['--random-seed', str(options.random_seed)])
self.files = _startup_files(options)
def run(self, testcase, verbose=False):
"""Run the executable with a specific testcase."""
args = [self.executable] + self.flags + self.files + [testcase]
if verbose:
print('# Command line for %s comparison:' % self.label)
print(' '.join(args))
if self.executable.endswith('.py'):
# Wrap with python in tests.
args = [sys.executable] + args
return Execute(
args,
cwd=os.path.dirname(os.path.abspath(testcase)),
timeout=TIMEOUT,
)
@property
def flags(self):
return self.common_flags + self.config_flags
class Output(object): class Output(object):
def __init__(self, exit_code, timed_out, stdout, pid): def __init__(self, exit_code, timed_out, stdout, pid):
@ -50,7 +121,6 @@ def Execute(args, cwd, timeout=None):
except OSError: except OSError:
sys.stderr.write('Error: Process %s already ended.\n' % process.pid) sys.stderr.write('Error: Process %s already ended.\n' % process.pid)
timer = Timer(timeout, kill_process) timer = Timer(timeout, kill_process)
timer.start() timer.start()
stdout, _ = process.communicate() stdout, _ = process.communicate()

View File

@ -88,26 +88,13 @@ CONFIGS = dict(
], ],
) )
# Timeout in seconds for one d8 run.
TIMEOUT = 3
# Return codes. # Return codes.
RETURN_PASS = 0 RETURN_PASS = 0
RETURN_FAIL = 2 RETURN_FAIL = 2
BASE_PATH = os.path.dirname(os.path.abspath(__file__)) BASE_PATH = os.path.dirname(os.path.abspath(__file__))
PREAMBLE = [
os.path.join(BASE_PATH, 'v8_mock.js'),
os.path.join(BASE_PATH, 'v8_suppressions.js'),
]
ARCH_MOCKS = os.path.join(BASE_PATH, 'v8_mock_archs.js')
SANITY_CHECKS = os.path.join(BASE_PATH, 'v8_sanity_checks.js') SANITY_CHECKS = os.path.join(BASE_PATH, 'v8_sanity_checks.js')
FLAGS = ['--correctness-fuzzer-suppressions', '--expose-gc',
'--allow-natives-syntax', '--invoke-weak-callbacks', '--omit-quit',
'--es-staging', '--wasm-staging', '--no-wasm-async-compilation',
'--suppress-asm-messages']
SUPPORTED_ARCHS = ['ia32', 'x64', 'arm', 'arm64'] SUPPORTED_ARCHS = ['ia32', 'x64', 'arm', 'arm64']
# Output for suppressed failure case. # Output for suppressed failure case.
@ -274,7 +261,7 @@ def fail_bailout(output, ignore_by_output_fun):
def print_difference( def print_difference(
options, source_key, first_config_flags, second_config_flags, options, source_key, first_command, second_command,
first_config_output, second_config_output, difference, source=None): first_config_output, second_config_output, difference, source=None):
# The first three entries will be parsed by clusterfuzz. Format changes # The first three entries will be parsed by clusterfuzz. Format changes
# will require changes on the clusterfuzz side. # will require changes on the clusterfuzz side.
@ -288,8 +275,8 @@ def print_difference(
suppression='', # We can't tie bugs to differences. suppression='', # We can't tie bugs to differences.
first_config_label=first_config_label, first_config_label=first_config_label,
second_config_label=second_config_label, second_config_label=second_config_label,
first_config_flags=' '.join(first_config_flags), first_config_flags=' '.join(first_command.flags),
second_config_flags=' '.join(second_config_flags), second_config_flags=' '.join(second_command.flags),
first_config_output= first_config_output=
first_config_output.stdout.decode('utf-8', 'replace'), first_config_output.stdout.decode('utf-8', 'replace'),
second_config_output= second_config_output=
@ -317,36 +304,21 @@ def main():
return RETURN_FAIL return RETURN_FAIL
# Set up runtime arguments. # Set up runtime arguments.
common_flags = FLAGS + ['--random-seed', str(options.random_seed)] first_config_flags = (CONFIGS[options.first_config] +
first_config_flags = (common_flags + CONFIGS[options.first_config] +
options.first_config_extra_flags) options.first_config_extra_flags)
second_config_flags = (common_flags + CONFIGS[options.second_config] + second_config_flags = (CONFIGS[options.second_config] +
options.second_config_extra_flags) options.second_config_extra_flags)
def run_d8(d8, config_flags, config_label=None, testcase=options.testcase): first_cmd = v8_commands.Command(
preamble = PREAMBLE[:] options,'first', options.first_d8, first_config_flags)
if options.first_arch != options.second_arch: second_cmd = v8_commands.Command(
preamble.append(ARCH_MOCKS) options, 'second', options.second_d8, second_config_flags)
args = [d8] + config_flags + preamble + [testcase]
if config_label:
print('# Command line for %s comparison:' % config_label)
print(' '.join(args))
if d8.endswith('.py'):
# Wrap with python in tests.
args = [sys.executable] + args
return v8_commands.Execute(
args,
cwd=os.path.dirname(os.path.abspath(testcase)),
timeout=TIMEOUT,
)
# Sanity checks. Run both configurations with the sanity-checks file only and # Sanity checks. Run both configurations with the sanity-checks file only and
# bail out early if different. # bail out early if different.
if not options.skip_sanity_checks: if not options.skip_sanity_checks:
first_config_output = run_d8( first_config_output = first_cmd.run(SANITY_CHECKS)
options.first_d8, first_config_flags, testcase=SANITY_CHECKS) second_config_output = second_cmd.run(SANITY_CHECKS)
second_config_output = run_d8(
options.second_d8, second_config_flags, testcase=SANITY_CHECKS)
difference, _ = suppress.diff( difference, _ = suppress.diff(
first_config_output.stdout, second_config_output.stdout) first_config_output.stdout, second_config_output.stdout)
if difference: if difference:
@ -354,18 +326,17 @@ def main():
# cases on this in case it's hit. # cases on this in case it's hit.
source_key = 'sanity check failed' source_key = 'sanity check failed'
print_difference( print_difference(
options, source_key, first_config_flags, second_config_flags, options, source_key, first_cmd, second_cmd,
first_config_output, second_config_output, difference) first_config_output, second_config_output, difference)
return RETURN_FAIL return RETURN_FAIL
first_config_output = run_d8(options.first_d8, first_config_flags, 'first') first_config_output = first_cmd.run(options.testcase, verbose=True)
# Early bailout based on first run's output. # Early bailout based on first run's output.
if pass_bailout(first_config_output, 1): if pass_bailout(first_config_output, 1):
return RETURN_PASS return RETURN_PASS
second_config_output = run_d8( second_config_output = second_cmd.run(options.testcase, verbose=True)
options.second_d8, second_config_flags, 'second')
# Bailout based on second run's output. # Bailout based on second run's output.
if pass_bailout(second_config_output, 2): if pass_bailout(second_config_output, 2):
@ -389,7 +360,7 @@ def main():
return RETURN_FAIL return RETURN_FAIL
print_difference( print_difference(
options, source_key, first_config_flags, second_config_flags, options, source_key, first_cmd, second_cmd,
first_config_output, second_config_output, difference, source) first_config_output, second_config_output, difference, source)
return RETURN_FAIL return RETURN_FAIL