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

llvm-svn: 355944
This commit is contained in:
George Burgess IV 2019-03-12 17:48:53 +00:00
parent 38d4a6c496
commit 87565fe734
1 changed files with 118 additions and 0 deletions

View File

@ -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())