xref: /netbsd-src/external/apache2/llvm/dist/clang/utils/creduce-clang-crash.py (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg#!/usr/bin/env python
27330f729Sjoerg"""Calls C-Reduce to create a minimal reproducer for clang crashes.
37330f729Sjoerg
47330f729SjoergOutput files:
57330f729Sjoerg  *.reduced.sh -- crash reproducer with minimal arguments
67330f729Sjoerg  *.reduced.cpp -- the reduced file
77330f729Sjoerg  *.test.sh -- interestingness test for C-Reduce
87330f729Sjoerg"""
97330f729Sjoerg
107330f729Sjoergfrom __future__ import print_function
117330f729Sjoergfrom argparse import ArgumentParser, RawTextHelpFormatter
127330f729Sjoergimport os
137330f729Sjoergimport re
147330f729Sjoergimport stat
157330f729Sjoergimport sys
167330f729Sjoergimport subprocess
177330f729Sjoergimport pipes
187330f729Sjoergimport shlex
197330f729Sjoergimport tempfile
207330f729Sjoergimport shutil
217330f729Sjoergfrom distutils.spawn import find_executable
22*e038c9c4Sjoergimport multiprocessing
237330f729Sjoerg
247330f729Sjoergverbose = False
257330f729Sjoergcreduce_cmd = None
267330f729Sjoergclang_cmd = None
277330f729Sjoerg
287330f729Sjoergdef verbose_print(*args, **kwargs):
297330f729Sjoerg  if verbose:
307330f729Sjoerg    print(*args, **kwargs)
317330f729Sjoerg
327330f729Sjoergdef check_file(fname):
33*e038c9c4Sjoerg  fname = os.path.normpath(fname)
347330f729Sjoerg  if not os.path.isfile(fname):
357330f729Sjoerg    sys.exit("ERROR: %s does not exist" % (fname))
367330f729Sjoerg  return fname
377330f729Sjoerg
387330f729Sjoergdef check_cmd(cmd_name, cmd_dir, cmd_path=None):
397330f729Sjoerg  """
407330f729Sjoerg  Returns absolute path to cmd_path if it is given,
417330f729Sjoerg  or absolute path to cmd_dir/cmd_name.
427330f729Sjoerg  """
437330f729Sjoerg  if cmd_path:
44*e038c9c4Sjoerg    # Make the path absolute so the creduce test can be run from any directory.
45*e038c9c4Sjoerg    cmd_path = os.path.abspath(cmd_path)
467330f729Sjoerg    cmd = find_executable(cmd_path)
477330f729Sjoerg    if cmd:
487330f729Sjoerg      return cmd
497330f729Sjoerg    sys.exit("ERROR: executable `%s` not found" % (cmd_path))
507330f729Sjoerg
517330f729Sjoerg  cmd = find_executable(cmd_name, path=cmd_dir)
527330f729Sjoerg  if cmd:
537330f729Sjoerg    return cmd
547330f729Sjoerg
557330f729Sjoerg  if not cmd_dir:
567330f729Sjoerg    cmd_dir = "$PATH"
577330f729Sjoerg  sys.exit("ERROR: `%s` not found in %s" % (cmd_name, cmd_dir))
587330f729Sjoerg
597330f729Sjoergdef quote_cmd(cmd):
607330f729Sjoerg  return ' '.join(pipes.quote(arg) for arg in cmd)
617330f729Sjoerg
627330f729Sjoergdef write_to_script(text, filename):
637330f729Sjoerg  with open(filename, 'w') as f:
647330f729Sjoerg    f.write(text)
657330f729Sjoerg  os.chmod(filename, os.stat(filename).st_mode | stat.S_IEXEC)
667330f729Sjoerg
677330f729Sjoergclass Reduce(object):
68*e038c9c4Sjoerg  def __init__(self, crash_script, file_to_reduce, core_number):
697330f729Sjoerg    crash_script_name, crash_script_ext = os.path.splitext(crash_script)
707330f729Sjoerg    file_reduce_name, file_reduce_ext = os.path.splitext(file_to_reduce)
717330f729Sjoerg
727330f729Sjoerg    self.testfile = file_reduce_name + '.test.sh'
737330f729Sjoerg    self.crash_script = crash_script_name + '.reduced' + crash_script_ext
747330f729Sjoerg    self.file_to_reduce = file_reduce_name + '.reduced' + file_reduce_ext
757330f729Sjoerg    shutil.copy(file_to_reduce, self.file_to_reduce)
767330f729Sjoerg
777330f729Sjoerg    self.clang = clang_cmd
787330f729Sjoerg    self.clang_args = []
797330f729Sjoerg    self.expected_output = []
80*e038c9c4Sjoerg    self.needs_stack_trace = False
817330f729Sjoerg    self.creduce_flags = ["--tidy"]
82*e038c9c4Sjoerg    self.creduce_flags = ["--n", str(core_number)]
837330f729Sjoerg
847330f729Sjoerg    self.read_clang_args(crash_script, file_to_reduce)
857330f729Sjoerg    self.read_expected_output()
867330f729Sjoerg
877330f729Sjoerg  def get_crash_cmd(self, cmd=None, args=None, filename=None):
887330f729Sjoerg    if not cmd:
897330f729Sjoerg      cmd = self.clang
907330f729Sjoerg    if not args:
917330f729Sjoerg      args = self.clang_args
927330f729Sjoerg    if not filename:
937330f729Sjoerg      filename = self.file_to_reduce
947330f729Sjoerg
957330f729Sjoerg    return [cmd] + args + [filename]
967330f729Sjoerg
977330f729Sjoerg  def read_clang_args(self, crash_script, filename):
987330f729Sjoerg    print("\nReading arguments from crash script...")
997330f729Sjoerg    with open(crash_script) as f:
1007330f729Sjoerg      # Assume clang call is the first non comment line.
1017330f729Sjoerg      cmd = []
1027330f729Sjoerg      for line in f:
1037330f729Sjoerg        if not line.lstrip().startswith('#'):
1047330f729Sjoerg          cmd = shlex.split(line)
1057330f729Sjoerg          break
1067330f729Sjoerg    if not cmd:
1077330f729Sjoerg      sys.exit("Could not find command in the crash script.");
1087330f729Sjoerg
1097330f729Sjoerg    # Remove clang and filename from the command
1107330f729Sjoerg    # Assume the last occurrence of the filename is the clang input file
1117330f729Sjoerg    del cmd[0]
1127330f729Sjoerg    for i in range(len(cmd)-1, -1, -1):
1137330f729Sjoerg      if cmd[i] == filename:
1147330f729Sjoerg        del cmd[i]
1157330f729Sjoerg        break
1167330f729Sjoerg    self.clang_args = cmd
1177330f729Sjoerg    verbose_print("Clang arguments:", quote_cmd(self.clang_args))
1187330f729Sjoerg
1197330f729Sjoerg  def read_expected_output(self):
1207330f729Sjoerg    print("\nGetting expected crash output...")
1217330f729Sjoerg    p = subprocess.Popen(self.get_crash_cmd(),
1227330f729Sjoerg                         stdout=subprocess.PIPE,
1237330f729Sjoerg                         stderr=subprocess.STDOUT)
1247330f729Sjoerg    crash_output, _ = p.communicate()
1257330f729Sjoerg    result = []
1267330f729Sjoerg
1277330f729Sjoerg    # Remove color codes
1287330f729Sjoerg    ansi_escape = r'\x1b\[[0-?]*m'
1297330f729Sjoerg    crash_output = re.sub(ansi_escape, '', crash_output.decode('utf-8'))
1307330f729Sjoerg
1317330f729Sjoerg    # Look for specific error messages
132*e038c9c4Sjoerg    regexes = [r"Assertion .+ failed", # Linux assert()
133*e038c9c4Sjoerg               r"Assertion failed: .+,", # FreeBSD/Mac assert()
134*e038c9c4Sjoerg               r"fatal error: error in backend: .+",
135*e038c9c4Sjoerg               r"LLVM ERROR: .+",
136*e038c9c4Sjoerg               r"UNREACHABLE executed at .+?!",
137*e038c9c4Sjoerg               r"LLVM IR generation of declaration '.+'",
138*e038c9c4Sjoerg               r"Generating code for declaration '.+'",
139*e038c9c4Sjoerg               r"\*\*\* Bad machine code: .+ \*\*\*",
140*e038c9c4Sjoerg               r"ERROR: .*Sanitizer: [^ ]+ "]
1417330f729Sjoerg    for msg_re in regexes:
1427330f729Sjoerg      match = re.search(msg_re, crash_output)
1437330f729Sjoerg      if match:
144*e038c9c4Sjoerg        msg = match.group(0)
1457330f729Sjoerg        result = [msg]
1467330f729Sjoerg        print("Found message:", msg)
1477330f729Sjoerg        break
1487330f729Sjoerg
1497330f729Sjoerg    # If no message was found, use the top five stack trace functions,
1507330f729Sjoerg    # ignoring some common functions
1517330f729Sjoerg    # Five is a somewhat arbitrary number; the goal is to get a small number
1527330f729Sjoerg    # of identifying functions with some leeway for common functions
1537330f729Sjoerg    if not result:
154*e038c9c4Sjoerg      self.needs_stack_trace = True
1557330f729Sjoerg      stacktrace_re = r'[0-9]+\s+0[xX][0-9a-fA-F]+\s*([^(]+)\('
156*e038c9c4Sjoerg      filters = ["PrintStackTrace", "RunSignalHandlers", "CleanupOnSignal",
157*e038c9c4Sjoerg                 "HandleCrash", "SignalHandler", "__restore_rt", "gsignal", "abort"]
158*e038c9c4Sjoerg      def skip_function(func_name):
159*e038c9c4Sjoerg        return any(name in func_name for name in filters)
160*e038c9c4Sjoerg
1617330f729Sjoerg      matches = re.findall(stacktrace_re, crash_output)
162*e038c9c4Sjoerg      result = [x for x in matches if x and not skip_function(x)][:5]
1637330f729Sjoerg      for msg in result:
1647330f729Sjoerg        print("Found stack trace function:", msg)
1657330f729Sjoerg
1667330f729Sjoerg    if not result:
1677330f729Sjoerg      print("ERROR: no crash was found")
1687330f729Sjoerg      print("The crash output was:\n========\n%s========" % crash_output)
1697330f729Sjoerg      sys.exit(1)
1707330f729Sjoerg
1717330f729Sjoerg    self.expected_output = result
1727330f729Sjoerg
1737330f729Sjoerg  def check_expected_output(self, args=None, filename=None):
1747330f729Sjoerg    if not args:
1757330f729Sjoerg      args = self.clang_args
1767330f729Sjoerg    if not filename:
1777330f729Sjoerg      filename = self.file_to_reduce
1787330f729Sjoerg
1797330f729Sjoerg    p = subprocess.Popen(self.get_crash_cmd(args=args, filename=filename),
1807330f729Sjoerg                         stdout=subprocess.PIPE,
1817330f729Sjoerg                         stderr=subprocess.STDOUT)
1827330f729Sjoerg    crash_output, _ = p.communicate()
1837330f729Sjoerg    return all(msg in crash_output.decode('utf-8') for msg in
1847330f729Sjoerg               self.expected_output)
1857330f729Sjoerg
1867330f729Sjoerg  def write_interestingness_test(self):
1877330f729Sjoerg    print("\nCreating the interestingness test...")
1887330f729Sjoerg
189*e038c9c4Sjoerg    # Disable symbolization if it's not required to avoid slow symbolization.
190*e038c9c4Sjoerg    disable_symbolization = ''
191*e038c9c4Sjoerg    if not self.needs_stack_trace:
192*e038c9c4Sjoerg      disable_symbolization = 'export LLVM_DISABLE_SYMBOLIZATION=1'
1937330f729Sjoerg
194*e038c9c4Sjoerg    output = """#!/bin/bash
195*e038c9c4Sjoerg%s
196*e038c9c4Sjoergif %s >& t.log ; then
197*e038c9c4Sjoerg  exit 1
198*e038c9c4Sjoergfi
199*e038c9c4Sjoerg""" % (disable_symbolization, quote_cmd(self.get_crash_cmd()))
2007330f729Sjoerg
2017330f729Sjoerg    for msg in self.expected_output:
2027330f729Sjoerg      output += 'grep -F %s t.log || exit 1\n' % pipes.quote(msg)
2037330f729Sjoerg
2047330f729Sjoerg    write_to_script(output, self.testfile)
2057330f729Sjoerg    self.check_interestingness()
2067330f729Sjoerg
2077330f729Sjoerg  def check_interestingness(self):
2087330f729Sjoerg    testfile = os.path.abspath(self.testfile)
2097330f729Sjoerg
2107330f729Sjoerg    # Check that the test considers the original file interesting
2117330f729Sjoerg    with open(os.devnull, 'w') as devnull:
2127330f729Sjoerg      returncode = subprocess.call(testfile, stdout=devnull)
2137330f729Sjoerg    if returncode:
2147330f729Sjoerg      sys.exit("The interestingness test does not pass for the original file.")
2157330f729Sjoerg
2167330f729Sjoerg    # Check that an empty file is not interesting
2177330f729Sjoerg    # Instead of modifying the filename in the test file, just run the command
2187330f729Sjoerg    with tempfile.NamedTemporaryFile() as empty_file:
2197330f729Sjoerg      is_interesting = self.check_expected_output(filename=empty_file.name)
2207330f729Sjoerg    if is_interesting:
2217330f729Sjoerg      sys.exit("The interestingness test passes for an empty file.")
2227330f729Sjoerg
2237330f729Sjoerg  def clang_preprocess(self):
2247330f729Sjoerg    print("\nTrying to preprocess the source file...")
2257330f729Sjoerg    with tempfile.NamedTemporaryFile() as tmpfile:
2267330f729Sjoerg      cmd_preprocess = self.get_crash_cmd() + ['-E', '-o', tmpfile.name]
2277330f729Sjoerg      cmd_preprocess_no_lines = cmd_preprocess + ['-P']
2287330f729Sjoerg      try:
2297330f729Sjoerg        subprocess.check_call(cmd_preprocess_no_lines)
2307330f729Sjoerg        if self.check_expected_output(filename=tmpfile.name):
2317330f729Sjoerg          print("Successfully preprocessed with line markers removed")
2327330f729Sjoerg          shutil.copy(tmpfile.name, self.file_to_reduce)
2337330f729Sjoerg        else:
2347330f729Sjoerg          subprocess.check_call(cmd_preprocess)
2357330f729Sjoerg          if self.check_expected_output(filename=tmpfile.name):
2367330f729Sjoerg            print("Successfully preprocessed without removing line markers")
2377330f729Sjoerg            shutil.copy(tmpfile.name, self.file_to_reduce)
2387330f729Sjoerg          else:
2397330f729Sjoerg            print("No longer crashes after preprocessing -- "
2407330f729Sjoerg                  "using original source")
2417330f729Sjoerg      except subprocess.CalledProcessError:
2427330f729Sjoerg        print("Preprocessing failed")
2437330f729Sjoerg
2447330f729Sjoerg  @staticmethod
2457330f729Sjoerg  def filter_args(args, opts_equal=[], opts_startswith=[],
2467330f729Sjoerg                  opts_one_arg_startswith=[]):
2477330f729Sjoerg    result = []
2487330f729Sjoerg    skip_next = False
2497330f729Sjoerg    for arg in args:
2507330f729Sjoerg      if skip_next:
2517330f729Sjoerg        skip_next = False
2527330f729Sjoerg        continue
2537330f729Sjoerg      if any(arg == a for a in opts_equal):
2547330f729Sjoerg        continue
2557330f729Sjoerg      if any(arg.startswith(a) for a in opts_startswith):
2567330f729Sjoerg        continue
2577330f729Sjoerg      if any(arg.startswith(a) for a in opts_one_arg_startswith):
2587330f729Sjoerg        skip_next = True
2597330f729Sjoerg        continue
2607330f729Sjoerg      result.append(arg)
2617330f729Sjoerg    return result
2627330f729Sjoerg
2637330f729Sjoerg  def try_remove_args(self, args, msg=None, extra_arg=None, **kwargs):
2647330f729Sjoerg    new_args = self.filter_args(args, **kwargs)
2657330f729Sjoerg
2667330f729Sjoerg    if extra_arg:
2677330f729Sjoerg      if extra_arg in new_args:
2687330f729Sjoerg        new_args.remove(extra_arg)
2697330f729Sjoerg      new_args.append(extra_arg)
2707330f729Sjoerg
2717330f729Sjoerg    if (new_args != args and
2727330f729Sjoerg        self.check_expected_output(args=new_args)):
2737330f729Sjoerg      if msg:
2747330f729Sjoerg        verbose_print(msg)
2757330f729Sjoerg      return new_args
2767330f729Sjoerg    return args
2777330f729Sjoerg
2787330f729Sjoerg  def try_remove_arg_by_index(self, args, index):
2797330f729Sjoerg    new_args = args[:index] + args[index+1:]
2807330f729Sjoerg    removed_arg = args[index]
2817330f729Sjoerg
2827330f729Sjoerg    # Heuristic for grouping arguments:
2837330f729Sjoerg    # remove next argument if it doesn't start with "-"
2847330f729Sjoerg    if index < len(new_args) and not new_args[index].startswith('-'):
2857330f729Sjoerg      del new_args[index]
2867330f729Sjoerg      removed_arg += ' ' + args[index+1]
2877330f729Sjoerg
2887330f729Sjoerg    if self.check_expected_output(args=new_args):
2897330f729Sjoerg      verbose_print("Removed", removed_arg)
2907330f729Sjoerg      return new_args, index
2917330f729Sjoerg    return args, index+1
2927330f729Sjoerg
2937330f729Sjoerg  def simplify_clang_args(self):
2947330f729Sjoerg    """Simplify clang arguments before running C-Reduce to reduce the time the
2957330f729Sjoerg    interestingness test takes to run.
2967330f729Sjoerg    """
2977330f729Sjoerg    print("\nSimplifying the clang command...")
2987330f729Sjoerg
2997330f729Sjoerg    # Remove some clang arguments to speed up the interestingness test
3007330f729Sjoerg    new_args = self.clang_args
3017330f729Sjoerg    new_args = self.try_remove_args(new_args,
3027330f729Sjoerg                                    msg="Removed debug info options",
3037330f729Sjoerg                                    opts_startswith=["-gcodeview",
3047330f729Sjoerg                                                     "-debug-info-kind=",
3057330f729Sjoerg                                                     "-debugger-tuning="])
3067330f729Sjoerg
3077330f729Sjoerg    new_args = self.try_remove_args(new_args,
3087330f729Sjoerg                                    msg="Removed --show-includes",
3097330f729Sjoerg                                    opts_startswith=["--show-includes"])
3107330f729Sjoerg    # Not suppressing warnings (-w) sometimes prevents the crash from occurring
3117330f729Sjoerg    # after preprocessing
3127330f729Sjoerg    new_args = self.try_remove_args(new_args,
3137330f729Sjoerg                                    msg="Replaced -W options with -w",
3147330f729Sjoerg                                    extra_arg='-w',
3157330f729Sjoerg                                    opts_startswith=["-W"])
3167330f729Sjoerg    new_args = self.try_remove_args(new_args,
3177330f729Sjoerg                                    msg="Replaced optimization level with -O0",
3187330f729Sjoerg                                    extra_arg="-O0",
3197330f729Sjoerg                                    opts_startswith=["-O"])
3207330f729Sjoerg
3217330f729Sjoerg    # Try to remove compilation steps
3227330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Added -emit-llvm",
3237330f729Sjoerg                                    extra_arg="-emit-llvm")
3247330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Added -fsyntax-only",
3257330f729Sjoerg                                    extra_arg="-fsyntax-only")
3267330f729Sjoerg
3277330f729Sjoerg    # Try to make implicit int an error for more sensible test output
3287330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Added -Werror=implicit-int",
3297330f729Sjoerg                                    opts_equal=["-w"],
3307330f729Sjoerg                                    extra_arg="-Werror=implicit-int")
3317330f729Sjoerg
3327330f729Sjoerg    self.clang_args = new_args
3337330f729Sjoerg    verbose_print("Simplified command:", quote_cmd(self.get_crash_cmd()))
3347330f729Sjoerg
3357330f729Sjoerg  def reduce_clang_args(self):
3367330f729Sjoerg    """Minimize the clang arguments after running C-Reduce, to get the smallest
3377330f729Sjoerg    command that reproduces the crash on the reduced file.
3387330f729Sjoerg    """
3397330f729Sjoerg    print("\nReducing the clang crash command...")
3407330f729Sjoerg
3417330f729Sjoerg    new_args = self.clang_args
3427330f729Sjoerg
3437330f729Sjoerg    # Remove some often occurring args
3447330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Removed -D options",
3457330f729Sjoerg                                    opts_startswith=["-D"])
3467330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Removed -D options",
3477330f729Sjoerg                                    opts_one_arg_startswith=["-D"])
3487330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Removed -I options",
3497330f729Sjoerg                                    opts_startswith=["-I"])
3507330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Removed -I options",
3517330f729Sjoerg                                    opts_one_arg_startswith=["-I"])
3527330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Removed -W options",
3537330f729Sjoerg                                    opts_startswith=["-W"])
3547330f729Sjoerg
3557330f729Sjoerg    # Remove other cases that aren't covered by the heuristic
3567330f729Sjoerg    new_args = self.try_remove_args(new_args, msg="Removed -mllvm",
3577330f729Sjoerg                                    opts_one_arg_startswith=["-mllvm"])
3587330f729Sjoerg
3597330f729Sjoerg    i = 0
3607330f729Sjoerg    while i < len(new_args):
3617330f729Sjoerg      new_args, i = self.try_remove_arg_by_index(new_args, i)
3627330f729Sjoerg
3637330f729Sjoerg    self.clang_args = new_args
3647330f729Sjoerg
3657330f729Sjoerg    reduced_cmd = quote_cmd(self.get_crash_cmd())
3667330f729Sjoerg    write_to_script(reduced_cmd, self.crash_script)
3677330f729Sjoerg    print("Reduced command:", reduced_cmd)
3687330f729Sjoerg
3697330f729Sjoerg  def run_creduce(self):
3707330f729Sjoerg    print("\nRunning C-Reduce...")
3717330f729Sjoerg    try:
3727330f729Sjoerg      p = subprocess.Popen([creduce_cmd] + self.creduce_flags +
3737330f729Sjoerg                           [self.testfile, self.file_to_reduce])
3747330f729Sjoerg      p.communicate()
3757330f729Sjoerg    except KeyboardInterrupt:
3767330f729Sjoerg      # Hack to kill C-Reduce because it jumps into its own pgid
3777330f729Sjoerg      print('\n\nctrl-c detected, killed creduce')
3787330f729Sjoerg      p.kill()
3797330f729Sjoerg
3807330f729Sjoergdef main():
3817330f729Sjoerg  global verbose
3827330f729Sjoerg  global creduce_cmd
3837330f729Sjoerg  global clang_cmd
3847330f729Sjoerg
3857330f729Sjoerg  parser = ArgumentParser(description=__doc__,
3867330f729Sjoerg                          formatter_class=RawTextHelpFormatter)
3877330f729Sjoerg  parser.add_argument('crash_script', type=str, nargs=1,
3887330f729Sjoerg                      help="Name of the script that generates the crash.")
3897330f729Sjoerg  parser.add_argument('file_to_reduce', type=str, nargs=1,
3907330f729Sjoerg                      help="Name of the file to be reduced.")
3917330f729Sjoerg  parser.add_argument('--llvm-bin', dest='llvm_bin', type=str,
3927330f729Sjoerg                      help="Path to the LLVM bin directory.")
3937330f729Sjoerg  parser.add_argument('--clang', dest='clang', type=str,
3947330f729Sjoerg                      help="The path to the `clang` executable. "
3957330f729Sjoerg                      "By default uses the llvm-bin directory.")
3967330f729Sjoerg  parser.add_argument('--creduce', dest='creduce', type=str,
3977330f729Sjoerg                      help="The path to the `creduce` executable. "
3987330f729Sjoerg                      "Required if `creduce` is not in PATH environment.")
399*e038c9c4Sjoerg  parser.add_argument('--n', dest='core_number', type=int,
400*e038c9c4Sjoerg                      default=max(4, multiprocessing.cpu_count() / 2),
401*e038c9c4Sjoerg                      help="Number of cores to use.")
4027330f729Sjoerg  parser.add_argument('-v', '--verbose', action='store_true')
4037330f729Sjoerg  args = parser.parse_args()
4047330f729Sjoerg
4057330f729Sjoerg  verbose = args.verbose
4067330f729Sjoerg  llvm_bin = os.path.abspath(args.llvm_bin) if args.llvm_bin else None
4077330f729Sjoerg  creduce_cmd = check_cmd('creduce', None, args.creduce)
4087330f729Sjoerg  clang_cmd = check_cmd('clang', llvm_bin, args.clang)
409*e038c9c4Sjoerg  core_number = args.core_number
4107330f729Sjoerg
4117330f729Sjoerg  crash_script = check_file(args.crash_script[0])
4127330f729Sjoerg  file_to_reduce = check_file(args.file_to_reduce[0])
4137330f729Sjoerg
414*e038c9c4Sjoerg  r = Reduce(crash_script, file_to_reduce, core_number)
4157330f729Sjoerg
4167330f729Sjoerg  r.simplify_clang_args()
4177330f729Sjoerg  r.write_interestingness_test()
4187330f729Sjoerg  r.clang_preprocess()
4197330f729Sjoerg  r.run_creduce()
4207330f729Sjoerg  r.reduce_clang_args()
4217330f729Sjoerg
4227330f729Sjoergif __name__ == '__main__':
4237330f729Sjoerg  main()
424