diff options
author | George Burgess IV <george.burgess.iv@gmail.com> | 2019-03-12 17:48:53 +0000 |
---|---|---|
committer | George Burgess IV <george.burgess.iv@gmail.com> | 2019-03-12 17:48:53 +0000 |
commit | e77c4ca4a3d47530ca65446400002fe525f6fbe5 (patch) | |
tree | bb34c5ca5ab7a396a81f031b45f8ce7bb12d20cc /utils | |
parent | af2aeae15746c39ffdd0dbaa9cce8d6c05e6b888 (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.py | 118 |
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()) |