Commit 9036662f authored by Michael Achenbach's avatar Michael Achenbach Committed by Commit Bot

[foozzie] Defeat the CrashTests loop

This prepares using ochang_js_fuzzer with foozzie. The fuzzer uses
tests from CrashTests in the corpus. This leads to a loop when
used with differential fuzzing, as foozzie dedupes failures based
on the original file path. Foozzie finds a new failure for the
existing failure in CrashTests, for which clusterfuzz creates a new
crash test and so on.

This subsumes all failures from CrashTests under the same key.
Once such a failure is reported, a developer can add it to a
mapping in foozzie.py, after which the global key can be used
again by clusterfuzz to report another failure.

No-Try: true
Bug: chromium:1044942
Change-Id: I801a23faeb0c672d6ad64b4100c463f53e36cbc2
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2214837
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68053}
parent 97ae1010
...@@ -143,6 +143,25 @@ ORIGINAL_SOURCE_HASH_LENGTH = 3 ...@@ -143,6 +143,25 @@ ORIGINAL_SOURCE_HASH_LENGTH = 3
# Placeholder string if no original source file could be determined. # Placeholder string if no original source file could be determined.
ORIGINAL_SOURCE_DEFAULT = 'none' ORIGINAL_SOURCE_DEFAULT = 'none'
# Placeholder string for failures from crash tests. If a failure is found with
# this signature, the matching sources should be moved to the mapping below.
ORIGINAL_SOURCE_CRASHTESTS = 'placeholder for CrashTests'
# Mapping from relative original source path (e.g. CrashTests/path/to/file.js)
# to a string key. Map to the same key for duplicate issues. The key should
# have more than 3 characters to not collide with other existing hashes.
# If a symptom from a particular original source file is known to map to a
# known failure, it can be added to this mapping. This should be done for all
# failures from CrashTests, as those by default map to the placeholder above.
KNOWN_FAILURES = {
# Foo.caller with asm.js: https://crbug.com/1042556
'CrashTests/5712410200899584/04483.js': '.caller',
'CrashTests/5703451898085376/02176.js': '.caller',
'CrashTests/4846282433495040/04342.js': '.caller',
# Flaky issue that almost never repros.
'CrashTests/5694376231632896/1033966.js': 'flaky',
}
def infer_arch(d8): def infer_arch(d8):
"""Infer the V8 architecture from the build configuration next to the """Infer the V8 architecture from the build configuration next to the
...@@ -313,6 +332,31 @@ def print_difference( ...@@ -313,6 +332,31 @@ def print_difference(
print(text.encode('utf-8', 'replace')) print(text.encode('utf-8', 'replace'))
def cluster_failures(source, known_failures=None):
"""Returns a string key for clustering duplicate failures.
Args:
source: The original source path where the failure happened.
known_failures: Mapping from original source path to failure key.
"""
known_failures = known_failures or KNOWN_FAILURES
# No source known. Typical for manually uploaded issues. This
# requires also manual issue creation.
if not source:
return ORIGINAL_SOURCE_DEFAULT
# Source is known to produce a particular failure.
if source in known_failures:
return known_failures[source]
# Subsume all other sources from CrashTests under one key. Otherwise
# failures lead to new crash tests which in turn lead to new failures.
if source.startswith('CrashTests'):
return ORIGINAL_SOURCE_CRASHTESTS
# We map all remaining failures to a short hash of the original source.
long_key = hashlib.sha1(source.encode('utf-8')).hexdigest()
return long_key[:ORIGINAL_SOURCE_HASH_LENGTH]
def main(): def main():
options = parse_args() options = parse_args()
...@@ -368,12 +412,6 @@ def main(): ...@@ -368,12 +412,6 @@ def main():
difference, source = suppress.diff(first_config_output, second_config_output) difference, source = suppress.diff(first_config_output, second_config_output)
if source:
long_key = hashlib.sha1(source.encode('utf-8')).hexdigest()
source_key = long_key[:ORIGINAL_SOURCE_HASH_LENGTH]
else:
source_key = ORIGINAL_SOURCE_DEFAULT
if difference: if difference:
# Only bail out due to suppressed output if there was a difference. If a # Only bail out due to suppressed output if there was a difference. If a
# suppression doesn't show up anymore in the statistics, we might want to # suppression doesn't show up anymore in the statistics, we might want to
...@@ -383,6 +421,7 @@ def main(): ...@@ -383,6 +421,7 @@ def main():
if fail_bailout(second_config_output, suppress.ignore_by_output2): if fail_bailout(second_config_output, suppress.ignore_by_output2):
return RETURN_FAIL return RETURN_FAIL
source_key = cluster_failures(source)
print_difference( print_difference(
options, source_key, first_cmd, second_cmd, options, source_key, first_cmd, second_cmd,
first_config_output, second_config_output, difference, source) first_config_output, second_config_output, difference, source)
......
...@@ -101,6 +101,23 @@ class ConfigTest(unittest.TestCase): ...@@ -101,6 +101,23 @@ class ConfigTest(unittest.TestCase):
class UnitTest(unittest.TestCase): class UnitTest(unittest.TestCase):
def testCluster(self):
crash_test_example_path = 'CrashTests/path/to/file.js'
self.assertEqual(
v8_foozzie.ORIGINAL_SOURCE_DEFAULT,
v8_foozzie.cluster_failures(''))
self.assertEqual(
v8_foozzie.ORIGINAL_SOURCE_CRASHTESTS,
v8_foozzie.cluster_failures(crash_test_example_path))
self.assertEqual(
'_o_O_',
v8_foozzie.cluster_failures(
crash_test_example_path,
known_failures={crash_test_example_path: '_o_O_'}))
self.assertEqual(
'980',
v8_foozzie.cluster_failures('v8/test/mjsunit/apply.js'))
def testDiff(self): def testDiff(self):
def diff_fun(one, two, skip=False): def diff_fun(one, two, skip=False):
suppress = v8_suppressions.get_suppression( suppress = v8_suppressions.get_suppression(
......
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