1*12c85518Srobert#!/usr/bin/env python3 2e5dd7070Spatrick"""Calls C-Reduce to create a minimal reproducer for clang crashes. 3e5dd7070Spatrick 4e5dd7070SpatrickOutput files: 5e5dd7070Spatrick *.reduced.sh -- crash reproducer with minimal arguments 6e5dd7070Spatrick *.reduced.cpp -- the reduced file 7e5dd7070Spatrick *.test.sh -- interestingness test for C-Reduce 8e5dd7070Spatrick""" 9e5dd7070Spatrick 10e5dd7070Spatrickfrom __future__ import print_function 11e5dd7070Spatrickfrom argparse import ArgumentParser, RawTextHelpFormatter 12e5dd7070Spatrickimport os 13e5dd7070Spatrickimport re 14e5dd7070Spatrickimport stat 15e5dd7070Spatrickimport sys 16e5dd7070Spatrickimport subprocess 17e5dd7070Spatrickimport pipes 18e5dd7070Spatrickimport shlex 19e5dd7070Spatrickimport tempfile 20e5dd7070Spatrickimport shutil 21e5dd7070Spatrickfrom distutils.spawn import find_executable 22a9ac8606Spatrickimport multiprocessing 23e5dd7070Spatrick 24e5dd7070Spatrickverbose = False 25e5dd7070Spatrickcreduce_cmd = None 26e5dd7070Spatrickclang_cmd = None 27e5dd7070Spatrick 28e5dd7070Spatrickdef verbose_print(*args, **kwargs): 29e5dd7070Spatrick if verbose: 30e5dd7070Spatrick print(*args, **kwargs) 31e5dd7070Spatrick 32e5dd7070Spatrickdef check_file(fname): 33e5dd7070Spatrick fname = os.path.normpath(fname) 34e5dd7070Spatrick if not os.path.isfile(fname): 35e5dd7070Spatrick sys.exit("ERROR: %s does not exist" % (fname)) 36e5dd7070Spatrick return fname 37e5dd7070Spatrick 38e5dd7070Spatrickdef check_cmd(cmd_name, cmd_dir, cmd_path=None): 39e5dd7070Spatrick """ 40e5dd7070Spatrick Returns absolute path to cmd_path if it is given, 41e5dd7070Spatrick or absolute path to cmd_dir/cmd_name. 42e5dd7070Spatrick """ 43e5dd7070Spatrick if cmd_path: 44e5dd7070Spatrick # Make the path absolute so the creduce test can be run from any directory. 45e5dd7070Spatrick cmd_path = os.path.abspath(cmd_path) 46e5dd7070Spatrick cmd = find_executable(cmd_path) 47e5dd7070Spatrick if cmd: 48e5dd7070Spatrick return cmd 49e5dd7070Spatrick sys.exit("ERROR: executable `%s` not found" % (cmd_path)) 50e5dd7070Spatrick 51e5dd7070Spatrick cmd = find_executable(cmd_name, path=cmd_dir) 52e5dd7070Spatrick if cmd: 53e5dd7070Spatrick return cmd 54e5dd7070Spatrick 55e5dd7070Spatrick if not cmd_dir: 56e5dd7070Spatrick cmd_dir = "$PATH" 57e5dd7070Spatrick sys.exit("ERROR: `%s` not found in %s" % (cmd_name, cmd_dir)) 58e5dd7070Spatrick 59e5dd7070Spatrickdef quote_cmd(cmd): 60e5dd7070Spatrick return ' '.join(pipes.quote(arg) for arg in cmd) 61e5dd7070Spatrick 62e5dd7070Spatrickdef write_to_script(text, filename): 63e5dd7070Spatrick with open(filename, 'w') as f: 64e5dd7070Spatrick f.write(text) 65e5dd7070Spatrick os.chmod(filename, os.stat(filename).st_mode | stat.S_IEXEC) 66e5dd7070Spatrick 67e5dd7070Spatrickclass Reduce(object): 68a9ac8606Spatrick def __init__(self, crash_script, file_to_reduce, core_number): 69e5dd7070Spatrick crash_script_name, crash_script_ext = os.path.splitext(crash_script) 70e5dd7070Spatrick file_reduce_name, file_reduce_ext = os.path.splitext(file_to_reduce) 71e5dd7070Spatrick 72e5dd7070Spatrick self.testfile = file_reduce_name + '.test.sh' 73e5dd7070Spatrick self.crash_script = crash_script_name + '.reduced' + crash_script_ext 74e5dd7070Spatrick self.file_to_reduce = file_reduce_name + '.reduced' + file_reduce_ext 75e5dd7070Spatrick shutil.copy(file_to_reduce, self.file_to_reduce) 76e5dd7070Spatrick 77e5dd7070Spatrick self.clang = clang_cmd 78e5dd7070Spatrick self.clang_args = [] 79e5dd7070Spatrick self.expected_output = [] 80a9ac8606Spatrick self.needs_stack_trace = False 81e5dd7070Spatrick self.creduce_flags = ["--tidy"] 82a9ac8606Spatrick self.creduce_flags = ["--n", str(core_number)] 83e5dd7070Spatrick 84e5dd7070Spatrick self.read_clang_args(crash_script, file_to_reduce) 85e5dd7070Spatrick self.read_expected_output() 86e5dd7070Spatrick 87e5dd7070Spatrick def get_crash_cmd(self, cmd=None, args=None, filename=None): 88e5dd7070Spatrick if not cmd: 89e5dd7070Spatrick cmd = self.clang 90e5dd7070Spatrick if not args: 91e5dd7070Spatrick args = self.clang_args 92e5dd7070Spatrick if not filename: 93e5dd7070Spatrick filename = self.file_to_reduce 94e5dd7070Spatrick 95e5dd7070Spatrick return [cmd] + args + [filename] 96e5dd7070Spatrick 97e5dd7070Spatrick def read_clang_args(self, crash_script, filename): 98e5dd7070Spatrick print("\nReading arguments from crash script...") 99e5dd7070Spatrick with open(crash_script) as f: 100e5dd7070Spatrick # Assume clang call is the first non comment line. 101e5dd7070Spatrick cmd = [] 102e5dd7070Spatrick for line in f: 103e5dd7070Spatrick if not line.lstrip().startswith('#'): 104e5dd7070Spatrick cmd = shlex.split(line) 105e5dd7070Spatrick break 106e5dd7070Spatrick if not cmd: 107e5dd7070Spatrick sys.exit("Could not find command in the crash script."); 108e5dd7070Spatrick 109e5dd7070Spatrick # Remove clang and filename from the command 110e5dd7070Spatrick # Assume the last occurrence of the filename is the clang input file 111e5dd7070Spatrick del cmd[0] 112e5dd7070Spatrick for i in range(len(cmd)-1, -1, -1): 113e5dd7070Spatrick if cmd[i] == filename: 114e5dd7070Spatrick del cmd[i] 115e5dd7070Spatrick break 116e5dd7070Spatrick self.clang_args = cmd 117e5dd7070Spatrick verbose_print("Clang arguments:", quote_cmd(self.clang_args)) 118e5dd7070Spatrick 119e5dd7070Spatrick def read_expected_output(self): 120e5dd7070Spatrick print("\nGetting expected crash output...") 121e5dd7070Spatrick p = subprocess.Popen(self.get_crash_cmd(), 122e5dd7070Spatrick stdout=subprocess.PIPE, 123e5dd7070Spatrick stderr=subprocess.STDOUT) 124e5dd7070Spatrick crash_output, _ = p.communicate() 125e5dd7070Spatrick result = [] 126e5dd7070Spatrick 127e5dd7070Spatrick # Remove color codes 128e5dd7070Spatrick ansi_escape = r'\x1b\[[0-?]*m' 129e5dd7070Spatrick crash_output = re.sub(ansi_escape, '', crash_output.decode('utf-8')) 130e5dd7070Spatrick 131e5dd7070Spatrick # Look for specific error messages 132a9ac8606Spatrick regexes = [r"Assertion .+ failed", # Linux assert() 133a9ac8606Spatrick r"Assertion failed: .+,", # FreeBSD/Mac assert() 134a9ac8606Spatrick r"fatal error: error in backend: .+", 135a9ac8606Spatrick r"LLVM ERROR: .+", 136a9ac8606Spatrick r"UNREACHABLE executed at .+?!", 137a9ac8606Spatrick r"LLVM IR generation of declaration '.+'", 138a9ac8606Spatrick r"Generating code for declaration '.+'", 139a9ac8606Spatrick r"\*\*\* Bad machine code: .+ \*\*\*", 140a9ac8606Spatrick r"ERROR: .*Sanitizer: [^ ]+ "] 141e5dd7070Spatrick for msg_re in regexes: 142e5dd7070Spatrick match = re.search(msg_re, crash_output) 143e5dd7070Spatrick if match: 144a9ac8606Spatrick msg = match.group(0) 145e5dd7070Spatrick result = [msg] 146e5dd7070Spatrick print("Found message:", msg) 147e5dd7070Spatrick break 148e5dd7070Spatrick 149e5dd7070Spatrick # If no message was found, use the top five stack trace functions, 150e5dd7070Spatrick # ignoring some common functions 151e5dd7070Spatrick # Five is a somewhat arbitrary number; the goal is to get a small number 152e5dd7070Spatrick # of identifying functions with some leeway for common functions 153e5dd7070Spatrick if not result: 154a9ac8606Spatrick self.needs_stack_trace = True 155e5dd7070Spatrick stacktrace_re = r'[0-9]+\s+0[xX][0-9a-fA-F]+\s*([^(]+)\(' 156a9ac8606Spatrick filters = ["PrintStackTrace", "RunSignalHandlers", "CleanupOnSignal", 157a9ac8606Spatrick "HandleCrash", "SignalHandler", "__restore_rt", "gsignal", "abort"] 158a9ac8606Spatrick def skip_function(func_name): 159a9ac8606Spatrick return any(name in func_name for name in filters) 160a9ac8606Spatrick 161e5dd7070Spatrick matches = re.findall(stacktrace_re, crash_output) 162a9ac8606Spatrick result = [x for x in matches if x and not skip_function(x)][:5] 163e5dd7070Spatrick for msg in result: 164e5dd7070Spatrick print("Found stack trace function:", msg) 165e5dd7070Spatrick 166e5dd7070Spatrick if not result: 167e5dd7070Spatrick print("ERROR: no crash was found") 168e5dd7070Spatrick print("The crash output was:\n========\n%s========" % crash_output) 169e5dd7070Spatrick sys.exit(1) 170e5dd7070Spatrick 171e5dd7070Spatrick self.expected_output = result 172e5dd7070Spatrick 173e5dd7070Spatrick def check_expected_output(self, args=None, filename=None): 174e5dd7070Spatrick if not args: 175e5dd7070Spatrick args = self.clang_args 176e5dd7070Spatrick if not filename: 177e5dd7070Spatrick filename = self.file_to_reduce 178e5dd7070Spatrick 179e5dd7070Spatrick p = subprocess.Popen(self.get_crash_cmd(args=args, filename=filename), 180e5dd7070Spatrick stdout=subprocess.PIPE, 181e5dd7070Spatrick stderr=subprocess.STDOUT) 182e5dd7070Spatrick crash_output, _ = p.communicate() 183e5dd7070Spatrick return all(msg in crash_output.decode('utf-8') for msg in 184e5dd7070Spatrick self.expected_output) 185e5dd7070Spatrick 186e5dd7070Spatrick def write_interestingness_test(self): 187e5dd7070Spatrick print("\nCreating the interestingness test...") 188e5dd7070Spatrick 189a9ac8606Spatrick # Disable symbolization if it's not required to avoid slow symbolization. 190a9ac8606Spatrick disable_symbolization = '' 191a9ac8606Spatrick if not self.needs_stack_trace: 192a9ac8606Spatrick disable_symbolization = 'export LLVM_DISABLE_SYMBOLIZATION=1' 193e5dd7070Spatrick 194a9ac8606Spatrick output = """#!/bin/bash 195a9ac8606Spatrick%s 196a9ac8606Spatrickif %s >& t.log ; then 197a9ac8606Spatrick exit 1 198a9ac8606Spatrickfi 199a9ac8606Spatrick""" % (disable_symbolization, quote_cmd(self.get_crash_cmd())) 200e5dd7070Spatrick 201e5dd7070Spatrick for msg in self.expected_output: 202e5dd7070Spatrick output += 'grep -F %s t.log || exit 1\n' % pipes.quote(msg) 203e5dd7070Spatrick 204e5dd7070Spatrick write_to_script(output, self.testfile) 205e5dd7070Spatrick self.check_interestingness() 206e5dd7070Spatrick 207e5dd7070Spatrick def check_interestingness(self): 208e5dd7070Spatrick testfile = os.path.abspath(self.testfile) 209e5dd7070Spatrick 210e5dd7070Spatrick # Check that the test considers the original file interesting 211e5dd7070Spatrick with open(os.devnull, 'w') as devnull: 212e5dd7070Spatrick returncode = subprocess.call(testfile, stdout=devnull) 213e5dd7070Spatrick if returncode: 214e5dd7070Spatrick sys.exit("The interestingness test does not pass for the original file.") 215e5dd7070Spatrick 216e5dd7070Spatrick # Check that an empty file is not interesting 217e5dd7070Spatrick # Instead of modifying the filename in the test file, just run the command 218e5dd7070Spatrick with tempfile.NamedTemporaryFile() as empty_file: 219e5dd7070Spatrick is_interesting = self.check_expected_output(filename=empty_file.name) 220e5dd7070Spatrick if is_interesting: 221e5dd7070Spatrick sys.exit("The interestingness test passes for an empty file.") 222e5dd7070Spatrick 223e5dd7070Spatrick def clang_preprocess(self): 224e5dd7070Spatrick print("\nTrying to preprocess the source file...") 225e5dd7070Spatrick with tempfile.NamedTemporaryFile() as tmpfile: 226e5dd7070Spatrick cmd_preprocess = self.get_crash_cmd() + ['-E', '-o', tmpfile.name] 227e5dd7070Spatrick cmd_preprocess_no_lines = cmd_preprocess + ['-P'] 228e5dd7070Spatrick try: 229e5dd7070Spatrick subprocess.check_call(cmd_preprocess_no_lines) 230e5dd7070Spatrick if self.check_expected_output(filename=tmpfile.name): 231e5dd7070Spatrick print("Successfully preprocessed with line markers removed") 232e5dd7070Spatrick shutil.copy(tmpfile.name, self.file_to_reduce) 233e5dd7070Spatrick else: 234e5dd7070Spatrick subprocess.check_call(cmd_preprocess) 235e5dd7070Spatrick if self.check_expected_output(filename=tmpfile.name): 236e5dd7070Spatrick print("Successfully preprocessed without removing line markers") 237e5dd7070Spatrick shutil.copy(tmpfile.name, self.file_to_reduce) 238e5dd7070Spatrick else: 239e5dd7070Spatrick print("No longer crashes after preprocessing -- " 240e5dd7070Spatrick "using original source") 241e5dd7070Spatrick except subprocess.CalledProcessError: 242e5dd7070Spatrick print("Preprocessing failed") 243e5dd7070Spatrick 244e5dd7070Spatrick @staticmethod 245e5dd7070Spatrick def filter_args(args, opts_equal=[], opts_startswith=[], 246e5dd7070Spatrick opts_one_arg_startswith=[]): 247e5dd7070Spatrick result = [] 248e5dd7070Spatrick skip_next = False 249e5dd7070Spatrick for arg in args: 250e5dd7070Spatrick if skip_next: 251e5dd7070Spatrick skip_next = False 252e5dd7070Spatrick continue 253e5dd7070Spatrick if any(arg == a for a in opts_equal): 254e5dd7070Spatrick continue 255e5dd7070Spatrick if any(arg.startswith(a) for a in opts_startswith): 256e5dd7070Spatrick continue 257e5dd7070Spatrick if any(arg.startswith(a) for a in opts_one_arg_startswith): 258e5dd7070Spatrick skip_next = True 259e5dd7070Spatrick continue 260e5dd7070Spatrick result.append(arg) 261e5dd7070Spatrick return result 262e5dd7070Spatrick 263e5dd7070Spatrick def try_remove_args(self, args, msg=None, extra_arg=None, **kwargs): 264e5dd7070Spatrick new_args = self.filter_args(args, **kwargs) 265e5dd7070Spatrick 266e5dd7070Spatrick if extra_arg: 267e5dd7070Spatrick if extra_arg in new_args: 268e5dd7070Spatrick new_args.remove(extra_arg) 269e5dd7070Spatrick new_args.append(extra_arg) 270e5dd7070Spatrick 271e5dd7070Spatrick if (new_args != args and 272e5dd7070Spatrick self.check_expected_output(args=new_args)): 273e5dd7070Spatrick if msg: 274e5dd7070Spatrick verbose_print(msg) 275e5dd7070Spatrick return new_args 276e5dd7070Spatrick return args 277e5dd7070Spatrick 278e5dd7070Spatrick def try_remove_arg_by_index(self, args, index): 279e5dd7070Spatrick new_args = args[:index] + args[index+1:] 280e5dd7070Spatrick removed_arg = args[index] 281e5dd7070Spatrick 282e5dd7070Spatrick # Heuristic for grouping arguments: 283e5dd7070Spatrick # remove next argument if it doesn't start with "-" 284e5dd7070Spatrick if index < len(new_args) and not new_args[index].startswith('-'): 285e5dd7070Spatrick del new_args[index] 286e5dd7070Spatrick removed_arg += ' ' + args[index+1] 287e5dd7070Spatrick 288e5dd7070Spatrick if self.check_expected_output(args=new_args): 289e5dd7070Spatrick verbose_print("Removed", removed_arg) 290e5dd7070Spatrick return new_args, index 291e5dd7070Spatrick return args, index+1 292e5dd7070Spatrick 293e5dd7070Spatrick def simplify_clang_args(self): 294e5dd7070Spatrick """Simplify clang arguments before running C-Reduce to reduce the time the 295e5dd7070Spatrick interestingness test takes to run. 296e5dd7070Spatrick """ 297e5dd7070Spatrick print("\nSimplifying the clang command...") 298e5dd7070Spatrick 299e5dd7070Spatrick # Remove some clang arguments to speed up the interestingness test 300e5dd7070Spatrick new_args = self.clang_args 301e5dd7070Spatrick new_args = self.try_remove_args(new_args, 302e5dd7070Spatrick msg="Removed debug info options", 303e5dd7070Spatrick opts_startswith=["-gcodeview", 304e5dd7070Spatrick "-debug-info-kind=", 305e5dd7070Spatrick "-debugger-tuning="]) 306e5dd7070Spatrick 307e5dd7070Spatrick new_args = self.try_remove_args(new_args, 308e5dd7070Spatrick msg="Removed --show-includes", 309e5dd7070Spatrick opts_startswith=["--show-includes"]) 310e5dd7070Spatrick # Not suppressing warnings (-w) sometimes prevents the crash from occurring 311e5dd7070Spatrick # after preprocessing 312e5dd7070Spatrick new_args = self.try_remove_args(new_args, 313e5dd7070Spatrick msg="Replaced -W options with -w", 314e5dd7070Spatrick extra_arg='-w', 315e5dd7070Spatrick opts_startswith=["-W"]) 316e5dd7070Spatrick new_args = self.try_remove_args(new_args, 317e5dd7070Spatrick msg="Replaced optimization level with -O0", 318e5dd7070Spatrick extra_arg="-O0", 319e5dd7070Spatrick opts_startswith=["-O"]) 320e5dd7070Spatrick 321e5dd7070Spatrick # Try to remove compilation steps 322e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Added -emit-llvm", 323e5dd7070Spatrick extra_arg="-emit-llvm") 324e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Added -fsyntax-only", 325e5dd7070Spatrick extra_arg="-fsyntax-only") 326e5dd7070Spatrick 327e5dd7070Spatrick # Try to make implicit int an error for more sensible test output 328e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Added -Werror=implicit-int", 329e5dd7070Spatrick opts_equal=["-w"], 330e5dd7070Spatrick extra_arg="-Werror=implicit-int") 331e5dd7070Spatrick 332e5dd7070Spatrick self.clang_args = new_args 333e5dd7070Spatrick verbose_print("Simplified command:", quote_cmd(self.get_crash_cmd())) 334e5dd7070Spatrick 335e5dd7070Spatrick def reduce_clang_args(self): 336e5dd7070Spatrick """Minimize the clang arguments after running C-Reduce, to get the smallest 337e5dd7070Spatrick command that reproduces the crash on the reduced file. 338e5dd7070Spatrick """ 339e5dd7070Spatrick print("\nReducing the clang crash command...") 340e5dd7070Spatrick 341e5dd7070Spatrick new_args = self.clang_args 342e5dd7070Spatrick 343e5dd7070Spatrick # Remove some often occurring args 344e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Removed -D options", 345e5dd7070Spatrick opts_startswith=["-D"]) 346e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Removed -D options", 347e5dd7070Spatrick opts_one_arg_startswith=["-D"]) 348e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Removed -I options", 349e5dd7070Spatrick opts_startswith=["-I"]) 350e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Removed -I options", 351e5dd7070Spatrick opts_one_arg_startswith=["-I"]) 352e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Removed -W options", 353e5dd7070Spatrick opts_startswith=["-W"]) 354e5dd7070Spatrick 355e5dd7070Spatrick # Remove other cases that aren't covered by the heuristic 356e5dd7070Spatrick new_args = self.try_remove_args(new_args, msg="Removed -mllvm", 357e5dd7070Spatrick opts_one_arg_startswith=["-mllvm"]) 358e5dd7070Spatrick 359e5dd7070Spatrick i = 0 360e5dd7070Spatrick while i < len(new_args): 361e5dd7070Spatrick new_args, i = self.try_remove_arg_by_index(new_args, i) 362e5dd7070Spatrick 363e5dd7070Spatrick self.clang_args = new_args 364e5dd7070Spatrick 365e5dd7070Spatrick reduced_cmd = quote_cmd(self.get_crash_cmd()) 366e5dd7070Spatrick write_to_script(reduced_cmd, self.crash_script) 367e5dd7070Spatrick print("Reduced command:", reduced_cmd) 368e5dd7070Spatrick 369e5dd7070Spatrick def run_creduce(self): 370e5dd7070Spatrick print("\nRunning C-Reduce...") 371e5dd7070Spatrick try: 372e5dd7070Spatrick p = subprocess.Popen([creduce_cmd] + self.creduce_flags + 373e5dd7070Spatrick [self.testfile, self.file_to_reduce]) 374e5dd7070Spatrick p.communicate() 375e5dd7070Spatrick except KeyboardInterrupt: 376e5dd7070Spatrick # Hack to kill C-Reduce because it jumps into its own pgid 377e5dd7070Spatrick print('\n\nctrl-c detected, killed creduce') 378e5dd7070Spatrick p.kill() 379e5dd7070Spatrick 380e5dd7070Spatrickdef main(): 381e5dd7070Spatrick global verbose 382e5dd7070Spatrick global creduce_cmd 383e5dd7070Spatrick global clang_cmd 384e5dd7070Spatrick 385e5dd7070Spatrick parser = ArgumentParser(description=__doc__, 386e5dd7070Spatrick formatter_class=RawTextHelpFormatter) 387e5dd7070Spatrick parser.add_argument('crash_script', type=str, nargs=1, 388e5dd7070Spatrick help="Name of the script that generates the crash.") 389e5dd7070Spatrick parser.add_argument('file_to_reduce', type=str, nargs=1, 390e5dd7070Spatrick help="Name of the file to be reduced.") 391e5dd7070Spatrick parser.add_argument('--llvm-bin', dest='llvm_bin', type=str, 392e5dd7070Spatrick help="Path to the LLVM bin directory.") 393e5dd7070Spatrick parser.add_argument('--clang', dest='clang', type=str, 394e5dd7070Spatrick help="The path to the `clang` executable. " 395e5dd7070Spatrick "By default uses the llvm-bin directory.") 396e5dd7070Spatrick parser.add_argument('--creduce', dest='creduce', type=str, 397e5dd7070Spatrick help="The path to the `creduce` executable. " 398e5dd7070Spatrick "Required if `creduce` is not in PATH environment.") 399a9ac8606Spatrick parser.add_argument('--n', dest='core_number', type=int, 400*12c85518Srobert default=max(4, multiprocessing.cpu_count() // 2), 401a9ac8606Spatrick help="Number of cores to use.") 402e5dd7070Spatrick parser.add_argument('-v', '--verbose', action='store_true') 403e5dd7070Spatrick args = parser.parse_args() 404e5dd7070Spatrick 405e5dd7070Spatrick verbose = args.verbose 406e5dd7070Spatrick llvm_bin = os.path.abspath(args.llvm_bin) if args.llvm_bin else None 407e5dd7070Spatrick creduce_cmd = check_cmd('creduce', None, args.creduce) 408e5dd7070Spatrick clang_cmd = check_cmd('clang', llvm_bin, args.clang) 409a9ac8606Spatrick core_number = args.core_number 410e5dd7070Spatrick 411e5dd7070Spatrick crash_script = check_file(args.crash_script[0]) 412e5dd7070Spatrick file_to_reduce = check_file(args.file_to_reduce[0]) 413e5dd7070Spatrick 414a9ac8606Spatrick r = Reduce(crash_script, file_to_reduce, core_number) 415e5dd7070Spatrick 416e5dd7070Spatrick r.simplify_clang_args() 417e5dd7070Spatrick r.write_interestingness_test() 418e5dd7070Spatrick r.clang_preprocess() 419e5dd7070Spatrick r.run_creduce() 420e5dd7070Spatrick r.reduce_clang_args() 421e5dd7070Spatrick 422e5dd7070Spatrickif __name__ == '__main__': 423e5dd7070Spatrick main() 424