r355944 - Add a creduce script for clang crashes

George Burgess IV via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 12 10:48:53 PDT 2019


Author: gbiv
Date: Tue Mar 12 10:48:53 2019
New Revision: 355944

URL: http://llvm.org/viewvc/llvm-project?rev=355944&view=rev
Log:
Add a creduce script for clang crashes

This CL adds a script that calls C-Reduce on an input file and given the
clang crash script, which is used to generate an interestingness test
for C-Reduce.

Patch by Amy Huang!

Differential Revision: https://reviews.llvm.org/D59118

Added:
    cfe/trunk/utils/creduce-clang-crash.py

Added: cfe/trunk/utils/creduce-clang-crash.py
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/creduce-clang-crash.py?rev=355944&view=auto
==============================================================================
--- cfe/trunk/utils/creduce-clang-crash.py (added)
+++ cfe/trunk/utils/creduce-clang-crash.py Tue Mar 12 10:48:53 2019
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+"""Calls C-Reduce to create a minimal reproducer for clang crashes.
+
+Requires C-Reduce and not (part of LLVM utils) to be installed.
+"""
+
+from argparse import ArgumentParser
+import os
+import re
+import stat
+import sys
+import subprocess
+import pipes
+from distutils.spawn import find_executable
+
+def create_test(build_script, llvm_not):
+  """
+  Create an interestingness test from the crash output.
+  Return as a string.
+  """
+  # Get clang call from build script
+  # Assumes the call is the last line of the script
+  with open(build_script) as f:
+    cmd = f.readlines()[-1].rstrip('\n\r')
+
+  # Get crash output
+  p = subprocess.Popen(build_script,
+                       stdout=subprocess.PIPE,
+                       stderr=subprocess.STDOUT)
+  crash_output, _ = p.communicate()
+
+  output = ['#!/bin/bash']
+  output.append('%s --crash %s >& t.log || exit 1' % (pipes.quote(llvm_not),
+                                                      cmd))
+
+  # Add messages from crash output to the test
+  # If there is an Assertion failure, use that; otherwise use the
+  # last five stack trace functions
+  assertion_re = r'Assertion `([^\']+)\' failed'
+  assertion_match = re.search(assertion_re, crash_output)
+  if assertion_match:
+    msg = assertion_match.group(1)
+    output.append('grep %s t.log || exit 1' % pipes.quote(msg))
+  else:
+    stacktrace_re = r'#[0-9]+\s+0[xX][0-9a-fA-F]+\s*([^(]+)\('
+    matches = re.findall(stacktrace_re, crash_output)
+    del matches[:-5]
+    output += ['grep %s t.log || exit 1' % pipes.quote(msg) for msg in matches]
+
+  return output
+
+def main():
+  parser = ArgumentParser(description=__doc__)
+  parser.add_argument('build_script', type=str, nargs=1,
+                      help='Name of the script that generates the crash.')
+  parser.add_argument('file_to_reduce', type=str, nargs=1,
+                      help='Name of the file to be reduced.')
+  parser.add_argument('-o', '--output', dest='output', type=str,
+                      help='Name of the output file for the reduction. Optional.')
+  parser.add_argument('--llvm-not', dest='llvm_not', type=str,
+                      help="The path to the llvm-not executable. "
+                      "Required if 'not' is not in PATH environment.");
+  parser.add_argument('--creduce', dest='creduce', type=str,
+                      help="The path to the C-Reduce executable. "
+                      "Required if 'creduce' is not in PATH environment.");
+  args = parser.parse_args()
+
+  build_script = os.path.abspath(args.build_script[0])
+  file_to_reduce = os.path.abspath(args.file_to_reduce[0])
+  llvm_not = (find_executable(args.llvm_not) if args.llvm_not else
+              find_executable('not'))
+  creduce = (find_executable(args.creduce) if args.creduce else
+             find_executable('creduce'))
+
+  if not os.path.isfile(build_script):
+    print(("ERROR: input file '%s' does not exist") % build_script)
+    return 1
+
+  if not os.path.isfile(file_to_reduce):
+    print(("ERROR: input file '%s' does not exist") % file_to_reduce)
+    return 1
+
+  if not llvm_not:
+    parser.print_help()
+    return 1
+
+  if not creduce:
+    parser.print_help()
+    return 1
+
+  # Write interestingness test to file
+  test_contents = create_test(build_script, llvm_not)
+  testname, _ = os.path.splitext(file_to_reduce)
+  testfile = testname + '.test.sh'
+  with open(testfile, 'w') as f:
+    f.write('\n'.join(test_contents))
+  os.chmod(testfile, os.stat(testfile).st_mode | stat.S_IEXEC)
+
+  # Confirm that the interestingness test passes
+  try:
+    with open(os.devnull, 'w') as devnull:
+      subprocess.check_call(testfile, stdout=devnull)
+  except subprocess.CalledProcessError:
+    print("For some reason the interestingness test does not return zero")
+    return 1
+
+  # FIXME: try running clang preprocessor first
+
+  try:
+    p = subprocess.Popen([creduce, testfile, file_to_reduce])
+    p.communicate()
+  except KeyboardInterrupt:
+    # Hack to kill C-Reduce because it jumps into its own pgid
+    print('\n\nctrl-c detected, killed creduce')
+    p.kill()
+
+if __name__ == '__main__':
+  sys.exit(main())




More information about the cfe-commits mailing list