v8/tools/clusterfuzz/v8_commands.py
Michael Achenbach c220a05ca9 [foozzie] Refactoring - several code clean-ups
This simplifies the lengthy main method by extracting some code and
by replacing the scattered returns with exceptions.

We introduce two exceptions for early bail-out. This enables helper
methods on multiple layers. The early bail-out on time-out is
moved to the point where it is detected.

Previously on timeout and crash we also printed out the step number.
Clusterfuzz doesn't parse this, it was only for statistical purposes,
and the latest version of the experimental workbench only parses
crashes and timeouts, not the step in which they happened. Hence,
this CL removes those step numbers.

Except the change described in the last paragraph, this CL doesn't
intend to change behavior.

No-Try: true
Bug: chromium:1100114
Change-Id: Ie8c18f183e4fc538577f3eb49aaf6df1acd1e4e1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2270547
Reviewed-by: Liviu Rau <liviurau@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68576}
2020-06-29 12:29:39 +00:00

156 lines
4.0 KiB
Python

# Copyright 2016 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.
# Fork from commands.py and output.py in v8 test driver.
import os
import signal
import subprocess
import sys
from threading import Event, Timer
import v8_fuzz_config
PYTHON3 = sys.version_info >= (3, 0)
# List of default flags passed to each d8 run.
DEFAULT_FLAGS = [
'--correctness-fuzzer-suppressions',
'--expose-gc',
'--fuzzing',
'--allow-natives-for-differential-fuzzing',
'--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_MOCK = os.path.join(BASE_PATH, 'v8_mock.js')
# Suppressions on JavaScript level for known issues.
JS_SUPPRESSIONS = os.path.join(BASE_PATH, 'v8_suppressions.js')
# Config-specific mock files.
ARCH_MOCKS = os.path.join(BASE_PATH, 'v8_mock_archs.js')
WEBASSEMBLY_MOCKS = os.path.join(BASE_PATH, 'v8_mock_webassembly.js')
def _startup_files(options):
"""Default files and optional config-specific mock files."""
files = [DEFAULT_MOCK]
if not options.skip_suppressions:
files.append(JS_SUPPRESSIONS)
if options.first.arch != options.second.arch:
files.append(ARCH_MOCKS)
# Mock out WebAssembly when comparing with jitless mode.
if '--jitless' in options.first.flags + options.second.flags:
files.append(WEBASSEMBLY_MOCKS)
return files
class BaseException(Exception):
"""Used to abort the comparison workflow and print the given message."""
def __init__(self, message):
self.message = message
class PassException(BaseException):
"""Represents an early abort making the overall run pass."""
pass
class FailException(BaseException):
"""Represents an early abort making the overall run fail."""
pass
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, timeout, 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):
def __init__(self, exit_code, stdout, pid):
self.exit_code = exit_code
self.stdout = stdout
self.pid = pid
def HasCrashed(self):
return (self.exit_code < 0 and
self.exit_code != -signal.SIGABRT)
def Execute(args, cwd, timeout=None):
popen_args = [c for c in args if c != ""]
kwargs = {}
if PYTHON3:
kwargs['encoding'] = 'utf-8'
try:
process = subprocess.Popen(
args=popen_args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=cwd,
**kwargs
)
except Exception as e:
sys.stderr.write("Error executing: %s\n" % popen_args)
raise e
timeout_event = Event()
def kill_process():
timeout_event.set()
try:
process.kill()
except OSError:
sys.stderr.write('Error: Process %s already ended.\n' % process.pid)
timer = Timer(timeout, kill_process)
timer.start()
stdout, _ = process.communicate()
timer.cancel()
if timeout_event.is_set():
raise PassException('# V8 correctness - T-I-M-E-O-U-T')
return Output(
process.returncode,
stdout,
process.pid,
)