Commit dc210a95 authored by Michael Achenbach's avatar Michael Achenbach Committed by Commit Bot

[test] Add interrupt-budget fuzzer

This adds back an option for interrupt budget available in no-snap
builds. This also adds a fuzzer configuration for numfuzz that enables
fuzzing the interrupt budget option. A new flag --disable-analysis
allows to generally skip the fuzzer's analysis phase, which can be
chosen for interrupt budget, which doesn't support an analysis phase.

Bug: v8:6917
Change-Id: I546dd9ee41c3e0fb027108ef4606a34514f230d4
Reviewed-on: https://chromium-review.googlesource.com/885805Reviewed-by: 's avatarMichael Achenbach <machenbach@chromium.org>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50929}
parent 2407b2bd
......@@ -319,6 +319,18 @@ DEFINE_VALUE_IMPLICATION(optimize_for_size, max_semi_space_size, 1)
DEFINE_BOOL(unbox_double_arrays, true, "automatically unbox arrays of doubles")
DEFINE_BOOL_READONLY(string_slices, true, "use string slices")
// Flags for Ignition for no-snapshot builds.
#undef FLAG
#ifndef V8_USE_SNAPSHOT
#define FLAG FLAG_FULL
#else
#define FLAG FLAG_READONLY
#endif
DEFINE_INT(interrupt_budget, 144 * KB,
"interrupt budget which should be used for the profiler counter")
#undef FLAG
#define FLAG FLAG_FULL
// Flags for Ignition.
DEFINE_BOOL(ignition_elide_noneffectful_bytecodes, true,
"elide bytecodes which won't have any external effect")
......
......@@ -2932,7 +2932,7 @@ AllocationResult Heap::AllocateBytecodeArray(int length,
instance->set_parameter_count(parameter_count);
instance->set_incoming_new_target_or_generator_register(
interpreter::Register::invalid_value());
instance->set_interrupt_budget(interpreter::Interpreter::kInterruptBudget);
instance->set_interrupt_budget(interpreter::Interpreter::InterruptBudget());
instance->set_osr_loop_nesting_level(0);
instance->set_bytecode_age(BytecodeArray::kNoAgeBytecodeAge);
instance->set_constant_pool(constant_pool);
......
......@@ -1304,7 +1304,7 @@ void InterpreterAssembler::UpdateInterruptBudget(Node* weight, bool backward) {
BIND(&interrupt_check);
{
CallRuntime(Runtime::kInterrupt, GetContext());
new_budget.Bind(Int32Constant(Interpreter::kInterruptBudget));
new_budget.Bind(Int32Constant(Interpreter::InterruptBudget()));
Goto(&ok);
}
......
......@@ -131,6 +131,10 @@ void Interpreter::IterateDispatchTable(RootVisitor* v) {
}
}
int Interpreter::InterruptBudget() {
return FLAG_interrupt_budget;
}
namespace {
void MaybePrintAst(ParseInfo* parse_info, CompilationInfo* compilation_info) {
......
......@@ -39,6 +39,9 @@ class Interpreter {
explicit Interpreter(Isolate* isolate);
virtual ~Interpreter() {}
// Returns the interrupt budget which should be used for the profiler counter.
static int InterruptBudget();
// Creates a compilation job which will generate bytecode for |literal|.
// Additionally, if |eager_inner_literals| is not null, adds any eagerly
// compilable inner FunctionLiterals to this list.
......@@ -77,9 +80,6 @@ class Interpreter {
return reinterpret_cast<Address>(bytecode_dispatch_counters_table_.get());
}
// The interrupt budget which should be used for the profiler counter.
static const int kInterruptBudget = 144 * KB;
private:
friend class SetupInterpreter;
friend class v8::internal::SetupIsolateDelegate;
......
......@@ -246,7 +246,7 @@ HeapObject* Deserializer<AllocatorT>::PostProcessNewObject(HeapObject* obj,
// fields in the serializer.
BytecodeArray* bytecode_array = BytecodeArray::cast(obj);
bytecode_array->set_interrupt_budget(
interpreter::Interpreter::kInterruptBudget);
interpreter::Interpreter::InterruptBudget());
bytecode_array->set_osr_loop_nesting_level(0);
}
// Check alignment.
......
......@@ -2040,7 +2040,7 @@ TEST(CodeSerializerAfterExecute) {
CHECK(sfi->HasBytecodeArray());
BytecodeArray* bytecode = sfi->bytecode_array();
CHECK_EQ(bytecode->interrupt_budget(),
interpreter::Interpreter::kInterruptBudget);
interpreter::Interpreter::InterruptBudget());
CHECK_EQ(bytecode->osr_loop_nesting_level(), 0);
{
......
// Copyright 2018 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.
// This turns all mjsunit asserts into no-ops used for fuzzing.
(function () {
failWithMessage = function () {}
})();
......@@ -28,6 +28,7 @@
import os
import re
from testrunner.local import statusfile
from testrunner.local import testsuite
from testrunner.objects import testcase
from testrunner.outproc import base as outproc
......@@ -48,7 +49,9 @@ class TestSuite(testsuite.TestSuite):
dirs.sort()
files.sort()
for filename in files:
if filename.endswith(".js") and filename != "mjsunit.js":
if (filename.endswith(".js") and
filename != "mjsunit.js" and
filename != "mjsunit_suppressions.js"):
fullpath = os.path.join(dirname, filename)
relpath = fullpath[len(self.root) + 1 : -3]
testname = relpath.replace(os.path.sep, "/")
......@@ -62,6 +65,9 @@ class TestSuite(testsuite.TestSuite):
def _test_class(self):
return TestCase
def _suppressed_test_class(self):
return SuppressedTestCase
class TestCase(testcase.TestCase):
def __init__(self, *args, **kwargs):
......@@ -208,5 +214,28 @@ class CombinedTest(testcase.TestCase):
return self._tests[0]._get_statusfile_flags()
class SuppressedTestCase(TestCase):
"""The same as a standard mjsunit test case with all asserts as no-ops."""
def __init__(self, *args, **kwargs):
super(SuppressedTestCase, self).__init__(*args, **kwargs)
self._mjsunit_files.append(
os.path.join(self.suite.root, "mjsunit_suppressions.js"))
def _prepare_outcomes(self, *args, **kwargs):
super(SuppressedTestCase, self)._prepare_outcomes(*args, **kwargs)
# Skip tests expected to fail. We suppress all asserts anyways, but some
# tests are expected to fail with type errors or even dchecks, and we
# can't differentiate that.
if (statusfile.FAIL in self._statusfile_outcomes and
not statusfile.SKIP in self._statusfile_outcomes):
self._statusfile_outcomes.append(statusfile.SKIP)
def _get_extra_flags(self, *args, **kwargs):
return (
super(SuppressedTestCase, self)._get_extra_flags(*args, **kwargs) +
['--disable-abortjs']
)
def GetSuite(name, root):
return TestSuite(name, root)
......@@ -117,10 +117,19 @@ class TestSuite(object):
self.root = root # string containing path
self.tests = None # list of TestCase objects
self.statusfile = None
self.suppress_internals = False
def status_file(self):
return "%s/%s.status" % (self.root, self.name)
def do_suppress_internals(self):
"""Specifies if this test suite should suppress asserts based on internals.
Internals are e.g. testing against the outcome of native runtime functions.
This is switched off on some fuzzers that violate these contracts.
"""
self.suppress_internals = True
def ListTests(self, context):
raise NotImplementedError
......@@ -229,9 +238,20 @@ class TestSuite(object):
self.tests = filtered
def _create_test(self, path, **kwargs):
test = self._test_class()(self, path, self._path_to_name(path), **kwargs)
if self.suppress_internals:
test_class = self._suppressed_test_class()
else:
test_class = self._test_class()
test = test_class(self, path, self._path_to_name(path), **kwargs)
return test
def _suppressed_test_class(self):
"""Optional testcase that suppresses assertions. Used by fuzzers that are
only interested in dchecks or tsan and that might violate the assertions
through fuzzing.
"""
return self._test_class()
def _test_class(self):
raise NotImplementedError
......
......@@ -108,6 +108,11 @@ class NumFuzzer(base_runner.BaseTestRunner):
help="extends --stress-deopt to have minimum interval "
"between deopt points")
# Stress interrupt budget
parser.add_option("--stress-interrupt-budget", default=0, type="int",
help="probability [0-10] of adding --interrupt-budget "
"flag to the test")
# Combine multiple tests
parser.add_option("--combine-tests", default=False, action="store_true",
help="Combine multiple tests as one and run with "
......@@ -151,6 +156,7 @@ class NumFuzzer(base_runner.BaseTestRunner):
self.mode_name))
ctx = self._create_context(options)
self._setup_suites(options, suites)
tests = self._load_tests(options, suites, ctx)
progress_indicator = progress.IndicatorNotifier()
progress_indicator.Register(
......@@ -233,6 +239,13 @@ class NumFuzzer(base_runner.BaseTestRunner):
False) # Coverage not supported.
return ctx
def _setup_suites(self, options, suites):
"""Sets additional configurations on test suites based on options."""
if options.stress_interrupt_budget:
# Changing interrupt budget forces us to suppress certain test assertions.
for suite in suites:
suite.do_suppress_internals()
def _load_tests(self, options, suites, ctx):
if options.combine_tests:
suites = [s for s in suites if s.test_combiner_available()]
......@@ -289,19 +302,22 @@ class NumFuzzer(base_runner.BaseTestRunner):
options.tests_count)
def _create_fuzzer(self, rng, options):
if options.combine_tests:
count = 1
disable_analysis = True
else:
count = options.tests_count
disable_analysis = False
return fuzzer.FuzzerProc(
rng,
count,
self._tests_count(options),
self._create_fuzzer_configs(options),
disable_analysis,
self._disable_analysis(options),
)
def _tests_count(self, options):
if options.combine_tests:
return 1
return options.tests_count
def _disable_analysis(self, options):
"""Disable analysis phase when options are used that don't support it."""
return options.combine_tests or options.stress_interrupt_budget
def _create_fuzzer_configs(self, options):
fuzzers = []
def add(name, prob, *args):
......@@ -312,6 +328,7 @@ class NumFuzzer(base_runner.BaseTestRunner):
add('marking', options.stress_marking)
add('scavenge', options.stress_scavenge)
add('gc_interval', options.stress_gc)
add('interrupt_budget', options.stress_interrupt_budget)
add('deopt', options.stress_deopt, options.stress_deopt_min)
return fuzzers
......
......@@ -217,6 +217,12 @@ class CompactionFuzzer(Fuzzer):
yield ['--stress-compaction-random']
class InterruptBudgetFuzzer(Fuzzer):
def create_flags_generator(self, rng, test, analysis_value):
while True:
yield ['--interrupt-budget=%d' % rng.randint(1, 144 * 1024)]
class DeoptAnalyzer(Analyzer):
MAX_DEOPT=1000000000
......@@ -255,13 +261,15 @@ class DeoptFuzzer(Fuzzer):
FUZZERS = {
'scavenge': (ScavengeAnalyzer, ScavengeFuzzer),
'marking': (MarkingAnalyzer, MarkingFuzzer),
'gc_interval': (GcIntervalAnalyzer, GcIntervalFuzzer),
'compaction': (None, CompactionFuzzer),
'deopt': (DeoptAnalyzer, DeoptFuzzer),
'gc_interval': (GcIntervalAnalyzer, GcIntervalFuzzer),
'interrupt_budget': (None, InterruptBudgetFuzzer),
'marking': (MarkingAnalyzer, MarkingFuzzer),
'scavenge': (ScavengeAnalyzer, ScavengeFuzzer),
}
def create_fuzzer_config(name, probability, *args, **kwargs):
analyzer_class, fuzzer_class = FUZZERS[name]
return FuzzerConfig(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment