| Index: tools/foozzie/v8_suppressions.py
|
| diff --git a/tools/foozzie/v8_suppressions.py b/tools/foozzie/v8_suppressions.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f5b736cdc3c1b60f93869ec1490674d82a2ce0f3
|
| --- /dev/null
|
| +++ b/tools/foozzie/v8_suppressions.py
|
| @@ -0,0 +1,266 @@
|
| +# 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.
|
| +
|
| +"""
|
| +Suppressions for V8 correctness fuzzer failures.
|
| +
|
| +We support three types of suppressions:
|
| +1. Ignore test case by pattern.
|
| +Map a regular expression to a bug entry. A new failure will be reported
|
| +when the pattern matches a JS test case.
|
| +Subsequent matches will be recoreded under the first failure.
|
| +
|
| +2. Ignore test run by output pattern:
|
| +Map a regular expression to a bug entry. A new failure will be reported
|
| +when the pattern matches the output of a particular run.
|
| +Subsequent matches will be recoreded under the first failure.
|
| +
|
| +3. Relax line-to-line comparisons with expressions of lines to ignore and
|
| +lines to be normalized (i.e. ignore only portions of lines).
|
| +These are not tied to bugs, be careful to not silently switch off this tool!
|
| +
|
| +Alternatively, think about adding a behavior change to v8_suppressions.js
|
| +to silence a particular class of problems.
|
| +"""
|
| +
|
| +import itertools
|
| +import re
|
| +
|
| +# Max line length for regular experessions checking for lines to ignore.
|
| +MAX_LINE_LENGTH = 512
|
| +
|
| +# For ignoring lines before carets and to ignore caret positions.
|
| +CARET_RE = re.compile(r'^\s*\^\s*$')
|
| +
|
| +# Ignore by test case pattern. Map from bug->regexp.
|
| +# Regular expressions are assumed to be compiled. We use regexp.match.
|
| +IGNORE_TEST_CASES = {
|
| + 'crbug.com/662907':
|
| + re.compile(r'.*new Array.*\[\d+\] =.*'
|
| + r'((Array)|(Object)).prototype.__defineSetter__.*', re.S),
|
| +
|
| + 'crbug.com/663340':
|
| + re.compile(r'.*\.shift\(\).*', re.S),
|
| +
|
| + 'crbug.com/666308':
|
| + re.compile(r'.*End stripped down and modified version.*'
|
| + r'\.prototype.*instanceof.*.*', re.S),
|
| +}
|
| +
|
| +# Ignore by output pattern. Map from config->bug->regexp. Config '' is used
|
| +# to match all configurations. Otherwise use either a compiler configuration,
|
| +# e.g. fullcode or validate_asm or an architecture, e.g. x64 or ia32 or a
|
| +# comma-separated combination, e.g. x64,fullcode, for more specific
|
| +# suppressions.
|
| +# Bug is preferred to be a crbug.com/XYZ, but can be any short distinguishable
|
| +# label.
|
| +# Regular expressions are assumed to be compiled. We use regexp.search.
|
| +IGNORE_OUTPUT = {
|
| + '': {
|
| + 'crbug.com/664068':
|
| + re.compile(r'RangeError', re.S),
|
| +
|
| + 'crbug.com/669017':
|
| + re.compile(r'SyntaxError', re.S),
|
| + },
|
| + 'validate_asm': {
|
| + 'validate_asm':
|
| + re.compile(r'TypeError'),
|
| + },
|
| +}
|
| +
|
| +# Lines matching any of the following regular expressions will be ignored
|
| +# if appearing on both sides. The capturing groups need to match exactly.
|
| +# Use uncompiled regular expressions - they'll be compiled later.
|
| +ALLOWED_LINE_DIFFS = [
|
| + # Ignore caret position in stack traces.
|
| + r'^\s*\^\s*$',
|
| +
|
| + # Ignore some stack trace headers as messages might not match.
|
| + r'^(.*)TypeError: .* is not a function$',
|
| + r'^(.*)TypeError: .* is not a constructor$',
|
| + r'^(.*)TypeError: (.*) is not .*$',
|
| + r'^(.*)ReferenceError: .* is not defined$',
|
| + r'^(.*):\d+: ReferenceError: .* is not defined$',
|
| +
|
| + # These are rarely needed. It includes some cases above.
|
| + r'^\w*Error: .* is not .*$',
|
| + r'^(.*) \w*Error: .* is not .*$',
|
| + r'^(.*):\d+: \w*Error: .* is not .*$',
|
| +
|
| + # Some test cases just print the message.
|
| + r'^.* is not a function(.*)$',
|
| + r'^(.*) is not a .*$',
|
| +
|
| + # crbug.com/669017
|
| + r'^(.*)SyntaxError: .*$',
|
| +
|
| + # Ignore lines of stack traces as character positions might not match.
|
| + r'^ at (?:new )?([^:]*):\d+:\d+(.*)$',
|
| + r'^(.*):\d+:(.*)$',
|
| +
|
| + # crbug.com/662840
|
| + r"^.*(?:Trying to access ')?(\w*)(?:(?:' through proxy)|"
|
| + r"(?: is not defined))$",
|
| +]
|
| +
|
| +# Lines matching any of the following regular expressions will be ignored.
|
| +# Use uncompiled regular expressions - they'll be compiled later.
|
| +IGNORE_LINES = [
|
| + r'^Validation of asm\.js module failed: .+$',
|
| + r'^.*:\d+: Invalid asm.js: .*$',
|
| + r'^Warning: unknown flag .*$',
|
| + r'^Warning: .+ is deprecated.*$',
|
| + r'^Try --help for options$',
|
| +]
|
| +
|
| +
|
| +###############################################################################
|
| +# Implementation - you should not need to change anything below this point.
|
| +
|
| +# Compile regular expressions.
|
| +ALLOWED_LINE_DIFFS = [re.compile(exp) for exp in ALLOWED_LINE_DIFFS]
|
| +IGNORE_LINES = [re.compile(exp) for exp in IGNORE_LINES]
|
| +
|
| +
|
| +def line_pairs(lines):
|
| + return itertools.izip_longest(
|
| + lines, itertools.islice(lines, 1, None), fillvalue=None)
|
| +
|
| +
|
| +def caret_match(line1, line2):
|
| + if (not line1 or
|
| + not line2 or
|
| + len(line1) > MAX_LINE_LENGTH or
|
| + len(line2) > MAX_LINE_LENGTH):
|
| + return False
|
| + return bool(CARET_RE.match(line1) and CARET_RE.match(line2))
|
| +
|
| +
|
| +def short_line_output(line):
|
| + if len(line) <= MAX_LINE_LENGTH:
|
| + # Avoid copying.
|
| + return line
|
| + return line[0:MAX_LINE_LENGTH] + '...'
|
| +
|
| +
|
| +def ignore_by_regexp(line1, line2, allowed):
|
| + if len(line1) > MAX_LINE_LENGTH or len(line2) > MAX_LINE_LENGTH:
|
| + return False
|
| + for exp in allowed:
|
| + match1 = exp.match(line1)
|
| + match2 = exp.match(line2)
|
| + if match1 and match2:
|
| + # If there are groups in the regexp, ensure the groups matched the same
|
| + # things.
|
| + if match1.groups() == match2.groups(): # tuple comparison
|
| + return True
|
| + return False
|
| +
|
| +
|
| +def diff_output(output1, output2, allowed, ignore1, ignore2):
|
| + def useful_line(ignore):
|
| + def fun(line):
|
| + return all(not e.match(line) for e in ignore)
|
| + return fun
|
| +
|
| + lines1 = filter(useful_line(ignore1), output1)
|
| + lines2 = filter(useful_line(ignore2), output2)
|
| +
|
| + for ((line1, lookahead1), (line2, lookahead2)) in itertools.izip_longest(
|
| + line_pairs(lines1), line_pairs(lines2), fillvalue=(None, None)):
|
| +
|
| + # Only one of the two iterators should run out.
|
| + assert not (line1 is None and line2 is None)
|
| +
|
| + # One iterator ends earlier.
|
| + if line1 is None:
|
| + return '+ %s' % short_line_output(line2)
|
| + if line2 is None:
|
| + return '- %s' % short_line_output(line1)
|
| +
|
| + # If lines are equal, no further checks are necessary.
|
| + if line1 == line2:
|
| + continue
|
| +
|
| + # Look ahead. If next line is a caret, ignore this line.
|
| + if caret_match(lookahead1, lookahead2):
|
| + continue
|
| +
|
| + # Check if a regexp allows these lines to be different.
|
| + if ignore_by_regexp(line1, line2, allowed):
|
| + continue
|
| +
|
| + # Lines are different.
|
| + return '- %s\n+ %s' % (short_line_output(line1), short_line_output(line2))
|
| +
|
| + # No difference found.
|
| + return None
|
| +
|
| +
|
| +def get_suppression(arch1, config1, arch2, config2):
|
| + return V8Suppression(arch1, config1, arch2, config2)
|
| +
|
| +
|
| +class Suppression(object):
|
| + def diff(self, output1, output2):
|
| + return None
|
| +
|
| + def ignore(self, testcase):
|
| + return False
|
| +
|
| + def ignore_by_output1(self, output):
|
| + return False
|
| +
|
| + def ignore_by_output2(self, output):
|
| + return False
|
| +
|
| +
|
| +class V8Suppression(Suppression):
|
| + def __init__(self, arch1, config1, arch2, config2):
|
| + self.arch1 = arch1
|
| + self.config1 = config1
|
| + self.arch2 = arch2
|
| + self.config2 = config2
|
| +
|
| + def diff(self, output1, output2):
|
| + return diff_output(
|
| + output1.splitlines(),
|
| + output2.splitlines(),
|
| + ALLOWED_LINE_DIFFS,
|
| + IGNORE_LINES,
|
| + IGNORE_LINES,
|
| + )
|
| +
|
| + def ignore(self, testcase):
|
| + for bug, exp in IGNORE_TEST_CASES.iteritems():
|
| + if exp.match(testcase):
|
| + return bug
|
| + return False
|
| +
|
| + def ignore_by_output1(self, output):
|
| + return self.ignore_by_output(output, self.arch1, self.config1)
|
| +
|
| + def ignore_by_output2(self, output):
|
| + return self.ignore_by_output(output, self.arch2, self.config2)
|
| +
|
| + def ignore_by_output(self, output, arch, config):
|
| + def check(mapping):
|
| + for bug, exp in mapping.iteritems():
|
| + if exp.search(output):
|
| + return bug
|
| + return None
|
| + bug = check(IGNORE_OUTPUT.get('', {}))
|
| + if bug:
|
| + return bug
|
| + bug = check(IGNORE_OUTPUT.get(arch, {}))
|
| + if bug:
|
| + return bug
|
| + bug = check(IGNORE_OUTPUT.get(config, {}))
|
| + if bug:
|
| + return bug
|
| + bug = check(IGNORE_OUTPUT.get('%s,%s' % (arch, config), {}))
|
| + if bug:
|
| + return bug
|
| + return None
|
|
|