summaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
authorGeorge Burgess IV <george.burgess.iv@gmail.com>2019-03-12 17:48:53 +0000
committerGeorge Burgess IV <george.burgess.iv@gmail.com>2019-03-12 17:48:53 +0000
commite77c4ca4a3d47530ca65446400002fe525f6fbe5 (patch)
treebb34c5ca5ab7a396a81f031b45f8ce7bb12d20cc /utils
parentaf2aeae15746c39ffdd0dbaa9cce8d6c05e6b888 (diff)
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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@355944 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'utils')
-rw-r--r--utils/creduce-clang-crash.py118
1 files changed, 118 insertions, 0 deletions
diff --git a/utils/creduce-clang-crash.py b/utils/creduce-clang-crash.py
new file mode 100644
index 0000000000..bdba790ad6
--- /dev/null
+++ b/utils/creduce-clang-crash.py
@@ -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())