summaryrefslogtreecommitdiffstats
path: root/clang/utils/creduce-clang-crash.py
blob: bdba790ad65674768ec7f85d93df98fb6c737c36 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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())
OpenPOWER on IntegriCloud