OLD | NEW |
1 # Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """ | 5 """ |
6 Suppressions for V8 correctness fuzzer failures. | 6 Suppressions for V8 correctness fuzzer failures. |
7 | 7 |
8 We support three types of suppressions: | 8 We support three types of suppressions: |
9 1. Ignore test case by pattern. | 9 1. Ignore test case by pattern. |
10 Map a regular expression to a bug entry. A new failure will be reported | 10 Map a regular expression to a bug entry. A new failure will be reported |
11 when the pattern matches a JS test case. | 11 when the pattern matches a JS test case. |
12 Subsequent matches will be recoreded under the first failure. | 12 Subsequent matches will be recoreded under the first failure. |
13 | 13 |
14 2. Ignore test run by output pattern: | 14 2. Ignore test run by output pattern: |
15 Map a regular expression to a bug entry. A new failure will be reported | 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. | 16 when the pattern matches the output of a particular run. |
17 Subsequent matches will be recoreded under the first failure. | 17 Subsequent matches will be recoreded under the first failure. |
18 | 18 |
19 3. Relax line-to-line comparisons with expressions of lines to ignore and | 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). | 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! | 21 These are not tied to bugs, be careful to not silently switch off this tool! |
22 | 22 |
23 Alternatively, think about adding a behavior change to v8_suppressions.js | 23 Alternatively, think about adding a behavior change to v8_suppressions.js |
24 to silence a particular class of problems. | 24 to silence a particular class of problems. |
25 """ | 25 """ |
26 | 26 |
27 import hashlib | |
28 import itertools | 27 import itertools |
29 import re | 28 import re |
30 | 29 |
31 # Max line length for regular experessions checking for lines to ignore. | 30 # Max line length for regular experessions checking for lines to ignore. |
32 MAX_LINE_LENGTH = 512 | 31 MAX_LINE_LENGTH = 512 |
33 | 32 |
34 # For ignoring lines before carets and to ignore caret positions. | 33 # For ignoring lines before carets and to ignore caret positions. |
35 CARET_RE = re.compile(r'^\s*\^\s*$') | 34 CARET_RE = re.compile(r'^\s*\^\s*$') |
36 | 35 |
37 # Ignore by original source files. Map from bug->relative file paths in V8, | 36 # Ignore by original source files. Map from bug->relative file paths in V8, |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
133 ] | 132 ] |
134 | 133 |
135 | 134 |
136 ############################################################################### | 135 ############################################################################### |
137 # Implementation - you should not need to change anything below this point. | 136 # Implementation - you should not need to change anything below this point. |
138 | 137 |
139 # Compile regular expressions. | 138 # Compile regular expressions. |
140 ALLOWED_LINE_DIFFS = [re.compile(exp) for exp in ALLOWED_LINE_DIFFS] | 139 ALLOWED_LINE_DIFFS = [re.compile(exp) for exp in ALLOWED_LINE_DIFFS] |
141 IGNORE_LINES = [re.compile(exp) for exp in IGNORE_LINES] | 140 IGNORE_LINES = [re.compile(exp) for exp in IGNORE_LINES] |
142 | 141 |
143 # The number of hex digits used from the hash of the original source file path. | |
144 # Keep the number small to avoid duplicate explosion. | |
145 SOURCE_HASH_LENGTH = 3 | |
146 | |
147 ORIGINAL_SOURCE_PREFIX = 'v8-foozzie source: ' | 142 ORIGINAL_SOURCE_PREFIX = 'v8-foozzie source: ' |
148 ORIGINAL_SOURCE_DEFAULT = 'none' | |
149 | 143 |
150 def line_pairs(lines): | 144 def line_pairs(lines): |
151 return itertools.izip_longest( | 145 return itertools.izip_longest( |
152 lines, itertools.islice(lines, 1, None), fillvalue=None) | 146 lines, itertools.islice(lines, 1, None), fillvalue=None) |
153 | 147 |
154 | 148 |
155 def caret_match(line1, line2): | 149 def caret_match(line1, line2): |
156 if (not line1 or | 150 if (not line1 or |
157 not line2 or | 151 not line2 or |
158 len(line1) > MAX_LINE_LENGTH or | 152 len(line1) > MAX_LINE_LENGTH or |
(...skipping 17 matching lines...) Expand all Loading... |
176 match2 = exp.match(line2) | 170 match2 = exp.match(line2) |
177 if match1 and match2: | 171 if match1 and match2: |
178 # If there are groups in the regexp, ensure the groups matched the same | 172 # If there are groups in the regexp, ensure the groups matched the same |
179 # things. | 173 # things. |
180 if match1.groups() == match2.groups(): # tuple comparison | 174 if match1.groups() == match2.groups(): # tuple comparison |
181 return True | 175 return True |
182 return False | 176 return False |
183 | 177 |
184 | 178 |
185 def diff_output(output1, output2, allowed, ignore1, ignore2): | 179 def diff_output(output1, output2, allowed, ignore1, ignore2): |
186 """Returns a tuple (difference, source, source_key). | 180 """Returns a tuple (difference, source). |
187 | 181 |
188 The difference is None if there's no difference, otherwise a string | 182 The difference is None if there's no difference, otherwise a string |
189 with a readable diff. | 183 with a readable diff. |
190 | 184 |
191 The source is the last source output within the test case. It is the string | 185 The source is the last source output within the test case, or None if no |
192 'none' if no such output existed. | 186 such output existed. |
193 | |
194 The source_key is a short hash of source or 'none'. | |
195 """ | 187 """ |
196 def useful_line(ignore): | 188 def useful_line(ignore): |
197 def fun(line): | 189 def fun(line): |
198 return all(not e.match(line) for e in ignore) | 190 return all(not e.match(line) for e in ignore) |
199 return fun | 191 return fun |
200 | 192 |
201 lines1 = filter(useful_line(ignore1), output1) | 193 lines1 = filter(useful_line(ignore1), output1) |
202 lines2 = filter(useful_line(ignore2), output2) | 194 lines2 = filter(useful_line(ignore2), output2) |
203 | 195 |
204 # This keeps track where we are in the original source file of the fuzz | 196 # This keeps track where we are in the original source file of the fuzz |
205 # test case. | 197 # test case. |
206 source = ORIGINAL_SOURCE_DEFAULT | 198 source = None |
207 source_key = ORIGINAL_SOURCE_DEFAULT | |
208 | 199 |
209 for ((line1, lookahead1), (line2, lookahead2)) in itertools.izip_longest( | 200 for ((line1, lookahead1), (line2, lookahead2)) in itertools.izip_longest( |
210 line_pairs(lines1), line_pairs(lines2), fillvalue=(None, None)): | 201 line_pairs(lines1), line_pairs(lines2), fillvalue=(None, None)): |
211 | 202 |
212 # Only one of the two iterators should run out. | 203 # Only one of the two iterators should run out. |
213 assert not (line1 is None and line2 is None) | 204 assert not (line1 is None and line2 is None) |
214 | 205 |
215 # One iterator ends earlier. | 206 # One iterator ends earlier. |
216 if line1 is None: | 207 if line1 is None: |
217 return '+ %s' % short_line_output(line2), source, source_key | 208 return '+ %s' % short_line_output(line2), source |
218 if line2 is None: | 209 if line2 is None: |
219 return '- %s' % short_line_output(line1), source, source_key | 210 return '- %s' % short_line_output(line1), source |
220 | 211 |
221 # If lines are equal, no further checks are necessary. | 212 # If lines are equal, no further checks are necessary. |
222 if line1 == line2: | 213 if line1 == line2: |
223 # Instrumented original-source-file output must be equal in both | 214 # Instrumented original-source-file output must be equal in both |
224 # versions. It only makes sense to update it here when both lines | 215 # versions. It only makes sense to update it here when both lines |
225 # are equal. | 216 # are equal. |
226 if line1.startswith(ORIGINAL_SOURCE_PREFIX): | 217 if line1.startswith(ORIGINAL_SOURCE_PREFIX): |
227 source = line1[len(ORIGINAL_SOURCE_PREFIX):] | 218 source = line1[len(ORIGINAL_SOURCE_PREFIX):] |
228 source_key = hashlib.sha1(source).hexdigest()[:SOURCE_HASH_LENGTH] | |
229 continue | 219 continue |
230 | 220 |
231 # Look ahead. If next line is a caret, ignore this line. | 221 # Look ahead. If next line is a caret, ignore this line. |
232 if caret_match(lookahead1, lookahead2): | 222 if caret_match(lookahead1, lookahead2): |
233 continue | 223 continue |
234 | 224 |
235 # Check if a regexp allows these lines to be different. | 225 # Check if a regexp allows these lines to be different. |
236 if ignore_by_regexp(line1, line2, allowed): | 226 if ignore_by_regexp(line1, line2, allowed): |
237 continue | 227 continue |
238 | 228 |
239 # Lines are different. | 229 # Lines are different. |
240 return ( | 230 return ( |
241 '- %s\n+ %s' % (short_line_output(line1), short_line_output(line2)), | 231 '- %s\n+ %s' % (short_line_output(line1), short_line_output(line2)), |
242 source, | 232 source, |
243 source_key, | |
244 ) | 233 ) |
245 | 234 |
246 # No difference found. | 235 # No difference found. |
247 return None, source, source_key | 236 return None, source |
248 | 237 |
249 | 238 |
250 def get_suppression(arch1, config1, arch2, config2): | 239 def get_suppression(arch1, config1, arch2, config2): |
251 return V8Suppression(arch1, config1, arch2, config2) | 240 return V8Suppression(arch1, config1, arch2, config2) |
252 | 241 |
253 | 242 |
254 class Suppression(object): | 243 class Suppression(object): |
255 def diff(self, output1, output2): | 244 def diff(self, output1, output2): |
256 return None | 245 return None |
257 | 246 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
314 bug = check(IGNORE_OUTPUT.get(arch, {})) | 303 bug = check(IGNORE_OUTPUT.get(arch, {})) |
315 if bug: | 304 if bug: |
316 return bug | 305 return bug |
317 bug = check(IGNORE_OUTPUT.get(config, {})) | 306 bug = check(IGNORE_OUTPUT.get(config, {})) |
318 if bug: | 307 if bug: |
319 return bug | 308 return bug |
320 bug = check(IGNORE_OUTPUT.get('%s,%s' % (arch, config), {})) | 309 bug = check(IGNORE_OUTPUT.get('%s,%s' % (arch, config), {})) |
321 if bug: | 310 if bug: |
322 return bug | 311 return bug |
323 return None | 312 return None |
OLD | NEW |