Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2016 the V8 project authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """ | |
| 6 Suppressions for V8 correctness fuzzer failures. | |
| 7 | |
| 8 We support three types of suppressions: | |
| 9 1. Ignore test case by pattern. | |
| 10 Map a regular expression to a bug entry. A new failure will be reported | |
| 11 when the pattern matches a JS test case. | |
| 12 Subsequent matches will be recoreded under the first failure. | |
| 13 | |
| 14 2. Ignore test run by output pattern: | |
| 15 Map a regular expression to a bug entry. A new failure will be reported | |
| 16 when the pattern matches the output of a particular run. | |
| 17 Subsequent matches will be recoreded under the first failure. | |
| 18 | |
| 19 3. Relax line-to-line comparisons with expressions of lines to ignore and | |
| 20 lines to be normalized (i.e. ignore only portions of lines). | |
| 21 These are not tied to bugs, be careful to not silently switch off this tool! | |
| 22 | |
| 23 Alternatively, think about adding a behavior change to v8_suppressions.js | |
| 24 to silence a particular class of problems. | |
| 25 """ | |
| 26 | |
| 27 import itertools | |
| 28 import re | |
| 29 | |
| 30 # Max line length for regular experessions checking for lines to ignore. | |
| 31 MAX_LINE_LENGTH = 512 | |
| 32 | |
| 33 # For ignoring lines before carets and to ignore caret positions. | |
| 34 CARET_RE = re.compile(r'^\s*\^\s*$') | |
| 35 | |
| 36 # Ignore by test case pattern. Map from bug->regexp. | |
| 37 # Regular expressions are assumed to be compiled. We use regexp.match. | |
| 38 IGNORE_TEST_CASES = { | |
| 39 'crbug.com/662907': | |
| 40 re.compile(r'.*new Array.*\[\d+\] =.*' | |
| 41 r'((Array)|(Object)).prototype.__defineSetter__.*', re.S), | |
| 42 | |
| 43 'crbug.com/663340': | |
| 44 re.compile(r'.*\.shift\(\).*', re.S), | |
| 45 | |
| 46 'crbug.com/666308': | |
| 47 re.compile(r'.*End stripped down and modified version.*' | |
| 48 r'\.prototype.*instanceof.*.*', re.S), | |
| 49 } | |
| 50 | |
| 51 # Ignore by output pattern. Map from config->bug->regexp. Config '' is used | |
| 52 # to match all configurations. Otherwise use either a compiler configuration, | |
| 53 # e.g. fullcode or validate_asm or an architecture, e.g. x64 or ia32 or a | |
| 54 # comma-separated combination, e.g. x64,fullcode, for more specific | |
| 55 # suppressions. | |
| 56 # Bug is preferred to be a crbug.com/XYZ, but can be any short distinguishable | |
| 57 # label. | |
| 58 # Regular expressions are assumed to be compiled. We use regexp.search. | |
| 59 IGNORE_OUTPUT = { | |
| 60 '': { | |
| 61 'crbug.com/664068': | |
| 62 re.compile(r'RangeError', re.S), | |
| 63 | |
| 64 'crbug.com/669017': | |
| 65 re.compile(r'SyntaxError', re.S), | |
| 66 }, | |
| 67 'validate_asm': { | |
| 68 'validate_asm': | |
| 69 re.compile(r'TypeError'), | |
| 70 }, | |
| 71 } | |
| 72 | |
| 73 # Lines matching any of the following regular expressions will be ignored | |
| 74 # if appearing on both sides. The capturing groups need to match exactly. | |
| 75 # Use uncompiled regular expressions. | |
|
tandrii(chromium)
2016/12/19 08:43:33
why? ah, i guessed correctly; maybe:
Use uncompile
Michael Achenbach
2016/12/19 09:42:19
Done.
| |
| 76 ALLOWED_LINE_DIFFS = [ | |
| 77 # Ignore caret position in stack traces. | |
| 78 r'^\s*\^\s*$', | |
| 79 | |
| 80 # Ignore some stack trace headers as messages might not match. | |
| 81 r'^(.*)TypeError: .* is not a function$', | |
| 82 r'^(.*)TypeError: .* is not a constructor$', | |
| 83 r'^(.*)TypeError: (.*) is not .*$', | |
| 84 r'^(.*)ReferenceError: .* is not defined$', | |
| 85 r'^(.*):\d+: ReferenceError: .* is not defined$', | |
| 86 | |
| 87 # These are rarely needed. It includes some cases above. | |
| 88 r'^\w*Error: .* is not .*$', | |
| 89 r'^(.*) \w*Error: .* is not .*$', | |
| 90 r'^(.*):\d+: \w*Error: .* is not .*$', | |
| 91 | |
| 92 # Some test cases just print the message. | |
| 93 r'^.* is not a function(.*)$', | |
| 94 r'^(.*) is not a .*$', | |
| 95 | |
| 96 # crbug.com/669017 | |
| 97 r'^(.*)SyntaxError: .*$', | |
| 98 | |
| 99 # Ignore lines of stack traces as character positions might not match. | |
| 100 r'^ at (?:new )?([^:]*):\d+:\d+(.*)$', | |
| 101 r'^(.*):\d+:(.*)$', | |
| 102 | |
| 103 # crbug.com/662840 | |
| 104 r"^.*(?:Trying to access ')?(\w*)(?:(?:' through proxy)|" | |
| 105 r"(?: is not defined))$", | |
| 106 ] | |
| 107 | |
| 108 # Lines matching any of the following regular expressions will be ignored. | |
| 109 # Use uncompiled regular expressions. | |
| 110 IGNORE_LINES = [ | |
| 111 r'^Validation of asm\.js module failed: .+$', | |
| 112 r'^.*:\d+: Invalid asm.js: .*$', | |
| 113 r'^Warning: unknown flag .*$', | |
| 114 r'^Warning: .+ is deprecated.*$', | |
| 115 r'^Try --help for options$', | |
| 116 ] | |
| 117 | |
| 118 | |
| 119 ############################################################################### | |
| 120 # Implementation - you should not need to change anything below this point. | |
| 121 | |
| 122 # Compile regular expressions. | |
| 123 ALLOWED_LINE_DIFFS = [re.compile(exp) for exp in ALLOWED_LINE_DIFFS] | |
| 124 IGNORE_LINES = [re.compile(exp) for exp in IGNORE_LINES] | |
| 125 | |
| 126 | |
| 127 def line_pairs(lines): | |
| 128 for i in range(len(lines) - 1): | |
|
tandrii(chromium)
2016/12/19 08:43:32
xrange
but i'd do this:
for i, l in enumerate(li
Michael Achenbach
2016/12/19 09:42:19
Done, even nicer :)
| |
| 129 yield lines[i], lines[i + 1] | |
| 130 if lines: | |
| 131 yield lines[-1], None | |
| 132 | |
| 133 | |
| 134 def filter_error_pos(lines): | |
| 135 for line, lookahead in line_pairs(lines): | |
| 136 # Look ahead. If next line is a caret, ignore this line. | |
| 137 try: | |
|
tandrii(chromium)
2016/12/19 08:43:33
# shorter, and no need to catch KeyboardInterrupt
Michael Achenbach
2016/12/19 09:42:19
Done. Removed the dead code :)
| |
| 138 if CARET_RE.match(lookahead): | |
| 139 continue | |
| 140 except Exception: | |
| 141 # To ignore sentinel on last line. | |
| 142 pass | |
| 143 if CARET_RE.match(line): | |
| 144 # Ignore the caret too. | |
| 145 continue | |
| 146 yield line | |
| 147 | |
| 148 | |
| 149 def caret_match(line1, line2): | |
| 150 if (not line1 or | |
| 151 not line2 or | |
| 152 len(line1) > MAX_LINE_LENGTH or | |
| 153 len(line2) > MAX_LINE_LENGTH): | |
| 154 return False | |
| 155 return bool(CARET_RE.match(line1) and CARET_RE.match(line2)) | |
| 156 | |
| 157 | |
| 158 def short_line_output(line): | |
| 159 if len(line) <= MAX_LINE_LENGTH: | |
| 160 # Avoid copying. | |
| 161 return line | |
| 162 return line[0:MAX_LINE_LENGTH] + '...' | |
| 163 | |
| 164 | |
| 165 def ignore_by_regexp(line1, line2, allowed): | |
| 166 if len(line1) > MAX_LINE_LENGTH or len(line2) > MAX_LINE_LENGTH: | |
| 167 return False | |
| 168 for exp in allowed: | |
| 169 match1 = exp.match(line1) | |
| 170 match2 = exp.match(line2) | |
| 171 if match1 and match2: | |
| 172 # If there are groups in the regexp, ensure the groups matched the same | |
| 173 # things. | |
| 174 for j, g in enumerate(match1.groups()): | |
|
tandrii(chromium)
2016/12/19 08:43:33
return match1.groups() == match2.groups() # tuple
Michael Achenbach
2016/12/19 09:42:19
Done. Except we can't just return - only in True c
| |
| 175 if g != match2.groups()[j]: | |
| 176 break | |
| 177 else: | |
| 178 # Regexp matched and all groups are equal. Ignore these lines. | |
| 179 return True | |
| 180 return False | |
| 181 | |
| 182 | |
| 183 def diff_output(output1, output2, allowed, ignore1, ignore2): | |
| 184 def useful_line(ignore): | |
| 185 def fun(line): | |
| 186 for exp in ignore: | |
| 187 if exp.match(line): | |
| 188 return False | |
| 189 return True | |
|
tandrii(chromium)
2016/12/19 08:43:33
return all(not e.match(line) for e in ignore)
Michael Achenbach
2016/12/19 09:42:19
Done.
| |
| 190 return fun | |
| 191 | |
| 192 lines1 = filter(useful_line(ignore1), output1) | |
| 193 lines2 = filter(useful_line(ignore2), output2) | |
| 194 | |
| 195 for ((line1, lookahead1), (line2, lookahead2)) in itertools.izip_longest( | |
| 196 line_pairs(lines1), line_pairs(lines2), fillvalue=(None, None)): | |
| 197 | |
| 198 # Only one of the two iterators should run out. | |
| 199 assert not (line1 is None and line2 is None) | |
| 200 | |
| 201 # One iterator ends earlier. | |
| 202 if line1 is None: | |
| 203 return '+ %s' % short_line_output(line2) | |
| 204 if line2 is None: | |
| 205 return '- %s' % short_line_output(line1) | |
| 206 | |
| 207 # If lines are equal, no further checks are necessary. | |
| 208 if line1 == line2: | |
| 209 continue | |
| 210 | |
| 211 # Look ahead. If next line is a caret, ignore this line. | |
| 212 if caret_match(lookahead1, lookahead2): | |
| 213 continue | |
| 214 | |
| 215 # Check if a regexp allows these lines to be different. | |
| 216 if ignore_by_regexp(line1, line2, allowed): | |
| 217 continue | |
| 218 | |
| 219 # Lines are different. | |
| 220 return '- %s\n+ %s' % (short_line_output(line1), short_line_output(line2)) | |
| 221 | |
| 222 # No difference found. | |
| 223 return None | |
| 224 | |
| 225 | |
| 226 def get_suppression(arch1, config1, arch2, config2): | |
| 227 return V8Suppression(arch1, config1, arch2, config2) | |
| 228 | |
| 229 | |
| 230 class Suppression(object): | |
| 231 def diff(self, output1, output2): | |
| 232 return None | |
| 233 | |
| 234 def ignore(self, testcase): | |
| 235 return False | |
| 236 | |
| 237 def ignore_by_output1(self, output): | |
| 238 return False | |
| 239 | |
| 240 def ignore_by_output2(self, output): | |
| 241 return False | |
| 242 | |
| 243 | |
| 244 class V8Suppression(Suppression): | |
| 245 def __init__(self, arch1, config1, arch2, config2): | |
| 246 self.arch1 = arch1 | |
| 247 self.config1 = config1 | |
| 248 self.arch2 = arch2 | |
| 249 self.config2 = config2 | |
| 250 | |
| 251 def diff(self, output1, output2): | |
| 252 return diff_output( | |
| 253 output1.splitlines(), | |
| 254 output2.splitlines(), | |
| 255 ALLOWED_LINE_DIFFS, | |
| 256 IGNORE_LINES, | |
| 257 IGNORE_LINES, | |
| 258 ) | |
| 259 | |
| 260 def ignore(self, testcase): | |
| 261 for bug, exp in IGNORE_TEST_CASES.iteritems(): | |
| 262 if exp.match(testcase): | |
| 263 return bug | |
| 264 return False | |
| 265 | |
| 266 def ignore_by_output1(self, output): | |
| 267 return self.ignore_by_output(output, self.arch1, self.config1) | |
| 268 | |
| 269 def ignore_by_output2(self, output): | |
| 270 return self.ignore_by_output(output, self.arch2, self.config2) | |
| 271 | |
| 272 def ignore_by_output(self, output, arch, config): | |
| 273 def check(mapping): | |
| 274 for bug, exp in mapping.iteritems(): | |
| 275 if exp.search(output): | |
| 276 return bug | |
| 277 return None | |
| 278 bug = check(IGNORE_OUTPUT.get('', {})) | |
| 279 if bug: | |
| 280 return bug | |
| 281 bug = check(IGNORE_OUTPUT.get(arch, {})) | |
| 282 if bug: | |
| 283 return bug | |
| 284 bug = check(IGNORE_OUTPUT.get(config, {})) | |
| 285 if bug: | |
| 286 return bug | |
| 287 bug = check(IGNORE_OUTPUT.get('%s,%s' % (arch, config), {})) | |
| 288 if bug: | |
| 289 return bug | |
| 290 return None | |
| 291 | |
| OLD | NEW |