| OLD | NEW | 
|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python | 
| 2 # Copyright 2016 the V8 project authors. All rights reserved. | 2 # Copyright 2016 the V8 project authors. All rights reserved. | 
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be | 
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. | 
| 5 | 5 | 
| 6 """ | 6 """ | 
| 7 V8 correctness fuzzer launcher script. | 7 V8 correctness fuzzer launcher script. | 
| 8 """ | 8 """ | 
| 9 | 9 | 
| 10 import argparse | 10 import argparse | 
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 44 | 44 | 
| 45 FLAGS = ['--abort_on_stack_overflow', '--expose-gc', '--allow-natives-syntax', | 45 FLAGS = ['--abort_on_stack_overflow', '--expose-gc', '--allow-natives-syntax', | 
| 46          '--invoke-weak-callbacks', '--omit-quit', '--es-staging'] | 46          '--invoke-weak-callbacks', '--omit-quit', '--es-staging'] | 
| 47 | 47 | 
| 48 SUPPORTED_ARCHS = ['ia32', 'x64', 'arm', 'arm64'] | 48 SUPPORTED_ARCHS = ['ia32', 'x64', 'arm', 'arm64'] | 
| 49 | 49 | 
| 50 # Output for suppressed failure case. | 50 # Output for suppressed failure case. | 
| 51 FAILURE_HEADER_TEMPLATE = """# | 51 FAILURE_HEADER_TEMPLATE = """# | 
| 52 # V8 correctness failure | 52 # V8 correctness failure | 
| 53 # V8 correctness configs: %(configs)s | 53 # V8 correctness configs: %(configs)s | 
| 54 # V8 correctness sources: %(sources)s | 54 # V8 correctness sources: %(source_key)s | 
| 55 # V8 correctness suppression: %(suppression)s | 55 # V8 correctness suppression: %(suppression)s | 
| 56 """ | 56 """ | 
| 57 | 57 | 
| 58 # Extended output for failure case. The 'CHECK' is for the minimizer. | 58 # Extended output for failure case. The 'CHECK' is for the minimizer. | 
| 59 FAILURE_TEMPLATE = FAILURE_HEADER_TEMPLATE + """# | 59 FAILURE_TEMPLATE = FAILURE_HEADER_TEMPLATE + """# | 
| 60 # CHECK | 60 # CHECK | 
| 61 # | 61 # | 
| 62 # Compared %(first_config_label)s with %(second_config_label)s | 62 # Compared %(first_config_label)s with %(second_config_label)s | 
| 63 # | 63 # | 
| 64 # Flags of %(first_config_label)s: | 64 # Flags of %(first_config_label)s: | 
| 65 %(first_config_flags)s | 65 %(first_config_flags)s | 
| 66 # Flags of %(second_config_label)s: | 66 # Flags of %(second_config_label)s: | 
| 67 %(second_config_flags)s | 67 %(second_config_flags)s | 
| 68 # | 68 # | 
| 69 # Difference: | 69 # Difference: | 
| 70 %(difference)s | 70 %(difference)s | 
| 71 # | 71 # | 
|  | 72 # Source file: | 
|  | 73 %(source)s | 
|  | 74 # | 
| 72 ### Start of configuration %(first_config_label)s: | 75 ### Start of configuration %(first_config_label)s: | 
| 73 %(first_config_output)s | 76 %(first_config_output)s | 
| 74 ### End of configuration %(first_config_label)s | 77 ### End of configuration %(first_config_label)s | 
| 75 # | 78 # | 
| 76 ### Start of configuration %(second_config_label)s: | 79 ### Start of configuration %(second_config_label)s: | 
| 77 %(second_config_output)s | 80 %(second_config_output)s | 
| 78 ### End of configuration %(second_config_label)s | 81 ### End of configuration %(second_config_label)s | 
| 79 """ | 82 """ | 
| 80 | 83 | 
| 81 FUZZ_TEST_RE = re.compile(r'.*fuzz(-\d+\.js)') | 84 FUZZ_TEST_RE = re.compile(r'.*fuzz(-\d+\.js)') | 
|  | 85 SOURCE_RE = re.compile(r'print\("v8-foozzie source: (.*)"\);') | 
| 82 | 86 | 
| 83 def parse_args(): | 87 def parse_args(): | 
| 84   parser = argparse.ArgumentParser() | 88   parser = argparse.ArgumentParser() | 
| 85   parser.add_argument( | 89   parser.add_argument( | 
| 86     '--random-seed', type=int, required=True, | 90     '--random-seed', type=int, required=True, | 
| 87     help='random seed passed to both runs') | 91     help='random seed passed to both runs') | 
| 88   parser.add_argument( | 92   parser.add_argument( | 
| 89       '--first-arch', help='first architecture', default='x64') | 93       '--first-arch', help='first architecture', default='x64') | 
| 90   parser.add_argument( | 94   parser.add_argument( | 
| 91       '--second-arch', help='second architecture', default='x64') | 95       '--second-arch', help='second architecture', default='x64') | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 110   assert options.first_arch in SUPPORTED_ARCHS | 114   assert options.first_arch in SUPPORTED_ARCHS | 
| 111   assert options.second_arch in SUPPORTED_ARCHS | 115   assert options.second_arch in SUPPORTED_ARCHS | 
| 112   assert options.first_config in CONFIGS | 116   assert options.first_config in CONFIGS | 
| 113   assert options.second_config in CONFIGS | 117   assert options.second_config in CONFIGS | 
| 114 | 118 | 
| 115   # Ensure we have a test case. | 119   # Ensure we have a test case. | 
| 116   assert (os.path.exists(options.testcase) and | 120   assert (os.path.exists(options.testcase) and | 
| 117           os.path.isfile(options.testcase)), ( | 121           os.path.isfile(options.testcase)), ( | 
| 118       'Test case %s doesn\'t exist' % options.testcase) | 122       'Test case %s doesn\'t exist' % options.testcase) | 
| 119 | 123 | 
| 120   # Deduce metadata file name from test case. This also removes |  | 
| 121   # the prefix the test case might get during minimization. |  | 
| 122   suffix = FUZZ_TEST_RE.match(os.path.basename(options.testcase)).group(1) |  | 
| 123   options.meta_data_path = os.path.join( |  | 
| 124       os.path.dirname(options.testcase), 'meta' + suffix) |  | 
| 125   assert os.path.exists(options.meta_data_path), ( |  | 
| 126       'Metadata %s doesn\'t exist' % options.meta_data_path) |  | 
| 127 |  | 
| 128   # Use first d8 as default for second d8. | 124   # Use first d8 as default for second d8. | 
| 129   options.second_d8 = options.second_d8 or options.first_d8 | 125   options.second_d8 = options.second_d8 or options.first_d8 | 
| 130 | 126 | 
| 131   # Ensure absolute paths. | 127   # Ensure absolute paths. | 
| 132   if not os.path.isabs(options.first_d8): | 128   if not os.path.isabs(options.first_d8): | 
| 133     options.first_d8 = os.path.join(BASE_PATH, options.first_d8) | 129     options.first_d8 = os.path.join(BASE_PATH, options.first_d8) | 
| 134   if not os.path.isabs(options.second_d8): | 130   if not os.path.isabs(options.second_d8): | 
| 135     options.second_d8 = os.path.join(BASE_PATH, options.second_d8) | 131     options.second_d8 = os.path.join(BASE_PATH, options.second_d8) | 
| 136 | 132 | 
| 137   # Ensure executables exist. | 133   # Ensure executables exist. | 
| 138   assert os.path.exists(options.first_d8) | 134   assert os.path.exists(options.first_d8) | 
| 139   assert os.path.exists(options.second_d8) | 135   assert os.path.exists(options.second_d8) | 
| 140 | 136 | 
| 141   # Ensure we use different executables when we claim we compare | 137   # Ensure we use different executables when we claim we compare | 
| 142   # different architectures. | 138   # different architectures. | 
| 143   # TODO(machenbach): Infer arch from gn's build output. | 139   # TODO(machenbach): Infer arch from gn's build output. | 
| 144   if options.first_arch != options.second_arch: | 140   if options.first_arch != options.second_arch: | 
| 145     assert options.first_d8 != options.second_d8 | 141     assert options.first_d8 != options.second_d8 | 
| 146 | 142 | 
| 147   return options | 143   return options | 
| 148 | 144 | 
| 149 | 145 | 
| 150 def metadata_bailout(metadata, ignore_fun): | 146 def get_meta_data(content): | 
| 151   """Print failure state and return if ignore_fun matches metadata.""" | 147   """Extracts original-source-file paths from test case content.""" | 
| 152   bug = (ignore_fun(metadata) or '').strip() | 148   sources = [] | 
|  | 149   for line in content.splitlines(): | 
|  | 150     match = SOURCE_RE.match(line) | 
|  | 151     if match: | 
|  | 152       sources.append(match.group(1)) | 
|  | 153   return {'sources': sources} | 
|  | 154 | 
|  | 155 | 
|  | 156 def content_bailout(content, ignore_fun): | 
|  | 157   """Print failure state and return if ignore_fun matches content.""" | 
|  | 158   bug = (ignore_fun(content) or '').strip() | 
| 153   if bug: | 159   if bug: | 
| 154     print FAILURE_HEADER_TEMPLATE % dict( | 160     print FAILURE_HEADER_TEMPLATE % dict( | 
| 155         configs='', sources='', suppression=bug) | 161         configs='', source_key='', suppression=bug) | 
| 156     return True | 162     return True | 
| 157   return False | 163   return False | 
| 158 | 164 | 
| 159 | 165 | 
| 160 def test_pattern_bailout(testcase, ignore_fun): |  | 
| 161   """Print failure state and return if ignore_fun matches testcase.""" |  | 
| 162   with open(testcase) as f: |  | 
| 163     bug = (ignore_fun(f.read()) or '').strip() |  | 
| 164     if bug: |  | 
| 165       print FAILURE_HEADER_TEMPLATE % dict( |  | 
| 166           configs='', sources='', suppression=bug) |  | 
| 167       return True |  | 
| 168   return False |  | 
| 169 |  | 
| 170 |  | 
| 171 def pass_bailout(output, step_number): | 166 def pass_bailout(output, step_number): | 
| 172   """Print info and return if in timeout or crash pass states.""" | 167   """Print info and return if in timeout or crash pass states.""" | 
| 173   if output.HasTimedOut(): | 168   if output.HasTimedOut(): | 
| 174     # Dashed output, so that no other clusterfuzz tools can match the | 169     # Dashed output, so that no other clusterfuzz tools can match the | 
| 175     # words timeout or crash. | 170     # words timeout or crash. | 
| 176     print '# V8 correctness - T-I-M-E-O-U-T %d' % step_number | 171     print '# V8 correctness - T-I-M-E-O-U-T %d' % step_number | 
| 177     return True | 172     return True | 
| 178   if output.HasCrashed(): | 173   if output.HasCrashed(): | 
| 179     print '# V8 correctness - C-R-A-S-H %d' % step_number | 174     print '# V8 correctness - C-R-A-S-H %d' % step_number | 
| 180     return True | 175     return True | 
| 181   return False | 176   return False | 
| 182 | 177 | 
| 183 | 178 | 
| 184 def fail_bailout(output, ignore_by_output_fun): | 179 def fail_bailout(output, ignore_by_output_fun): | 
| 185   """Print failure state and return if ignore_by_output_fun matches output.""" | 180   """Print failure state and return if ignore_by_output_fun matches output.""" | 
| 186   bug = (ignore_by_output_fun(output.stdout) or '').strip() | 181   bug = (ignore_by_output_fun(output.stdout) or '').strip() | 
| 187   if bug: | 182   if bug: | 
| 188     print FAILURE_HEADER_TEMPLATE % dict( | 183     print FAILURE_HEADER_TEMPLATE % dict( | 
| 189         configs='', sources='', suppression=bug) | 184         configs='', source_key='', suppression=bug) | 
| 190     return True | 185     return True | 
| 191   return False | 186   return False | 
| 192 | 187 | 
| 193 | 188 | 
| 194 def main(): | 189 def main(): | 
| 195   options = parse_args() | 190   options = parse_args() | 
| 196 | 191 | 
| 197   # Suppressions are architecture and configuration specific. | 192   # Suppressions are architecture and configuration specific. | 
| 198   suppress = v8_suppressions.get_suppression( | 193   suppress = v8_suppressions.get_suppression( | 
| 199       options.first_arch, options.first_config, | 194       options.first_arch, options.first_config, | 
| 200       options.second_arch, options.second_config, | 195       options.second_arch, options.second_config, | 
| 201   ) | 196   ) | 
| 202 | 197 | 
| 203   # Get metadata. | 198   # Static bailout based on test case content or metadata. | 
| 204   # TODO(machenbach): We probably don't need the metadata file anymore | 199   with open(options.testcase) as f: | 
| 205   # now that the metadata is printed in the test cases. | 200     content = f.read() | 
| 206   with open(options.meta_data_path) as f: | 201   if content_bailout(get_meta_data(content), suppress.ignore_by_metadata): | 
| 207     metadata = json.load(f) | 202     return RETURN_FAIL | 
| 208 | 203   if content_bailout(content, suppress.ignore_by_content): | 
| 209   if metadata_bailout(metadata, suppress.ignore_by_metadata): |  | 
| 210     return RETURN_FAIL | 204     return RETURN_FAIL | 
| 211 | 205 | 
| 212   if test_pattern_bailout(options.testcase, suppress.ignore_by_content): | 206   # Set up runtime arguments. | 
| 213     return RETURN_FAIL |  | 
| 214 |  | 
| 215   common_flags = FLAGS + ['--random-seed', str(options.random_seed)] | 207   common_flags = FLAGS + ['--random-seed', str(options.random_seed)] | 
| 216   first_config_flags = common_flags + CONFIGS[options.first_config] | 208   first_config_flags = common_flags + CONFIGS[options.first_config] | 
| 217   second_config_flags = common_flags + CONFIGS[options.second_config] | 209   second_config_flags = common_flags + CONFIGS[options.second_config] | 
| 218 | 210 | 
| 219   def run_d8(d8, config_flags): | 211   def run_d8(d8, config_flags): | 
| 220     args = [d8] + config_flags + PREAMBLE + [options.testcase] | 212     args = [d8] + config_flags + PREAMBLE + [options.testcase] | 
| 221     if d8.endswith('.py'): | 213     if d8.endswith('.py'): | 
| 222       # Wrap with python in tests. | 214       # Wrap with python in tests. | 
| 223       args = [sys.executable] + args | 215       args = [sys.executable] + args | 
| 224     return v8_commands.Execute( | 216     return v8_commands.Execute( | 
| (...skipping 11 matching lines...) Expand all  Loading... | 
| 236     return RETURN_FAIL | 228     return RETURN_FAIL | 
| 237 | 229 | 
| 238   second_config_output = run_d8(options.second_d8, second_config_flags) | 230   second_config_output = run_d8(options.second_d8, second_config_flags) | 
| 239 | 231 | 
| 240   # Bailout based on second run's output. | 232   # Bailout based on second run's output. | 
| 241   if pass_bailout(second_config_output, 2): | 233   if pass_bailout(second_config_output, 2): | 
| 242     return RETURN_PASS | 234     return RETURN_PASS | 
| 243   if fail_bailout(second_config_output, suppress.ignore_by_output2): | 235   if fail_bailout(second_config_output, suppress.ignore_by_output2): | 
| 244     return RETURN_FAIL | 236     return RETURN_FAIL | 
| 245 | 237 | 
| 246   difference, source_key = suppress.diff( | 238   difference, source, source_key = suppress.diff( | 
| 247       first_config_output.stdout, second_config_output.stdout) | 239       first_config_output.stdout, second_config_output.stdout) | 
| 248   if difference: | 240   if difference: | 
| 249     # The first three entries will be parsed by clusterfuzz. Format changes | 241     # The first three entries will be parsed by clusterfuzz. Format changes | 
| 250     # will require changes on the clusterfuzz side. | 242     # will require changes on the clusterfuzz side. | 
| 251     first_config_label = '%s,%s' % (options.first_arch, options.first_config) | 243     first_config_label = '%s,%s' % (options.first_arch, options.first_config) | 
| 252     second_config_label = '%s,%s' % (options.second_arch, options.second_config) | 244     second_config_label = '%s,%s' % (options.second_arch, options.second_config) | 
| 253     print FAILURE_TEMPLATE % dict( | 245     print FAILURE_TEMPLATE % dict( | 
| 254         configs='%s:%s' % (first_config_label, second_config_label), | 246         configs='%s:%s' % (first_config_label, second_config_label), | 
| 255         sources=source_key, | 247         source_key=source_key, | 
| 256         suppression='', # We can't tie bugs to differences. | 248         suppression='', # We can't tie bugs to differences. | 
| 257         first_config_label=first_config_label, | 249         first_config_label=first_config_label, | 
| 258         second_config_label=second_config_label, | 250         second_config_label=second_config_label, | 
| 259         first_config_flags=' '.join(first_config_flags), | 251         first_config_flags=' '.join(first_config_flags), | 
| 260         second_config_flags=' '.join(second_config_flags), | 252         second_config_flags=' '.join(second_config_flags), | 
| 261         first_config_output=first_config_output.stdout, | 253         first_config_output=first_config_output.stdout, | 
| 262         second_config_output=second_config_output.stdout, | 254         second_config_output=second_config_output.stdout, | 
|  | 255         source=source, | 
| 263         difference=difference, | 256         difference=difference, | 
| 264     ) | 257     ) | 
| 265     return RETURN_FAIL | 258     return RETURN_FAIL | 
| 266 | 259 | 
| 267   # TODO(machenbach): Figure out if we could also return a bug in case there's | 260   # TODO(machenbach): Figure out if we could also return a bug in case there's | 
| 268   # no difference, but one of the line suppressions has matched - and without | 261   # no difference, but one of the line suppressions has matched - and without | 
| 269   # the match there would be a difference. | 262   # the match there would be a difference. | 
| 270 | 263 | 
| 271   print '# V8 correctness - pass' | 264   print '# V8 correctness - pass' | 
| 272   return RETURN_PASS | 265   return RETURN_PASS | 
| 273 | 266 | 
| 274 | 267 | 
| 275 if __name__ == "__main__": | 268 if __name__ == "__main__": | 
| 276   try: | 269   try: | 
| 277     result = main() | 270     result = main() | 
| 278   except SystemExit: | 271   except SystemExit: | 
| 279     # Make sure clusterfuzz reports internal errors and wrong usage. | 272     # Make sure clusterfuzz reports internal errors and wrong usage. | 
| 280     # Use one label for all internal and usage errors. | 273     # Use one label for all internal and usage errors. | 
| 281     print FAILURE_HEADER_TEMPLATE % dict( | 274     print FAILURE_HEADER_TEMPLATE % dict( | 
| 282         configs='', sources='', suppression='wrong_usage') | 275         configs='', source_key='', suppression='wrong_usage') | 
| 283     result = RETURN_FAIL | 276     result = RETURN_FAIL | 
| 284   except Exception as e: | 277   except Exception as e: | 
| 285     print FAILURE_HEADER_TEMPLATE % dict( | 278     print FAILURE_HEADER_TEMPLATE % dict( | 
| 286         configs='', sources='', suppression='internal_error') | 279         configs='', source_key='', suppression='internal_error') | 
| 287     print '# Internal error: %s' % e | 280     print '# Internal error: %s' % e | 
| 288     traceback.print_exc(file=sys.stdout) | 281     traceback.print_exc(file=sys.stdout) | 
| 289     result = RETURN_FAIL | 282     result = RETURN_FAIL | 
| 290 | 283 | 
| 291   sys.exit(result) | 284   sys.exit(result) | 
| OLD | NEW | 
|---|