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 |