Index: .arcconfig =================================================================== --- .arcconfig +++ .arcconfig @@ -1,4 +1,4 @@ { - "repository.callsign" : "G", - "conduit_uri" : "https://reviews.llvm.org/" + "repository.callsign" : "G", + "conduit_uri" : "https://reviews.llvm.org/" } Index: clang/utils/creduce-clang-crash.py =================================================================== --- /dev/null +++ clang/utils/creduce-clang-crash.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# A tool that 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 + +def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + """ + Return the full path to a program or return None. + """ + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + +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='Runs C-Reduce on the input file') + 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 = which(args.llvm_not) if args.llvm_not else which('not') + creduce = which(args.creduce) if args.creduce else which('creduce') + + if not os.path.isfile(build_script): + print(("ERROR: input file '%s' does not exist") % build_script) + sys.exit(1) + + if not os.path.isfile(file_to_reduce): + print(("ERROR: input file '%s' does not exist") % file_to_reduce) + sys.exit(1) + + if not llvm_not: + parser.print_help() + sys.exit(1) + + if not creduce: + parser.print_help() + sys.exit(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) + + sys.exit(subprocess.call([creduce, testfile, file_to_reduce])) + +if __name__ == '__main__': + main()