]> granicus.if.org Git - clang/blob - utils/modfuzz.py
clang/test/CodeGenObjC/ivar-type-encoding.m: Tweak to satisfy -m32.
[clang] / utils / modfuzz.py
1 #! /usr/bin/env python
2
3 # To use:
4 #  1) Update the 'decls' list below with your fuzzing configuration.
5 #  2) Run with the clang binary as the command-line argument.
6
7 import random
8 import subprocess
9 import sys
10 import os
11
12 clang = sys.argv[1]
13 none_opts = 0.3
14
15 class Decl:
16   def __init__(self, text, depends=[], provides=[], conflicts=[]):
17     self.text = text
18     self.depends = depends
19     self.provides = provides
20     self.conflicts = conflicts
21
22   def valid(self, model):
23     for i in self.depends:
24       if i not in model.decls:
25         return False
26     for i in self.conflicts:
27       if i in model.decls:
28         return False
29     return True
30
31   def apply(self, model, name):
32     for i in self.provides:
33       model.decls[i] = True
34     model.source += self.text % {'name': name}
35
36 decls = [
37   Decl('struct X { int n; };\n', provides=['X'], conflicts=['X']),
38   Decl('static_assert(X{.n=1}.n == 1, "");\n', depends=['X']),
39   Decl('X %(name)s;\n', depends=['X']),
40 ]
41
42 class FS:
43   def __init__(self):
44     self.fs = {}
45     self.prevfs = {}
46
47   def write(self, path, contents):
48     self.fs[path] = contents
49
50   def done(self):
51     for f, s in self.fs.items():
52       if self.prevfs.get(f) != s:
53         f = file(f, 'w')
54         f.write(s)
55         f.close()
56
57     for f in self.prevfs:
58       if f not in self.fs:
59         os.remove(f)
60
61     self.prevfs, self.fs = self.fs, {}
62
63 fs = FS()
64
65 class CodeModel:
66   def __init__(self):
67     self.source = ''
68     self.modules = {}
69     self.decls = {}
70     self.i = 0
71
72   def make_name(self):
73     self.i += 1
74     return 'n' + str(self.i)
75
76   def fails(self):
77     fs.write('module.modulemap',
78           ''.join('module %s { header "%s.h" export * }\n' % (m, m)
79                   for m in self.modules.keys()))
80
81     for m, (s, _) in self.modules.items():
82       fs.write('%s.h' % m, s)
83
84     fs.write('main.cc', self.source)
85     fs.done()
86
87     return subprocess.call([clang, '-std=c++11', '-c', '-fmodules', 'main.cc', '-o', '/dev/null']) != 0
88
89 def generate():
90   model = CodeModel()
91   m = []
92
93   try:
94     for d in mutations(model):
95       d(model)
96       m.append(d)
97     if not model.fails():
98       return
99   except KeyboardInterrupt:
100     print
101     return True
102
103   sys.stdout.write('\nReducing:\n')
104   sys.stdout.flush()
105
106   try:
107     while True:
108       assert m, 'got a failure with no steps; broken clang binary?'
109       i = random.choice(range(len(m)))
110       x = m[0:i] + m[i+1:]
111       m2 = CodeModel()
112       for d in x:
113         d(m2)
114       if m2.fails():
115         m = x
116         model = m2
117       else:
118         sys.stdout.write('.')
119         sys.stdout.flush()
120   except KeyboardInterrupt:
121     # FIXME: Clean out output directory first.
122     model.fails()
123     return model
124
125 def choose(options):
126   while True:
127     i = int(random.uniform(0, len(options) + none_opts))
128     if i >= len(options):
129       break
130     yield options[i]
131
132 def mutations(model):
133   options = [create_module, add_top_level_decl]
134   for opt in choose(options):
135     yield opt(model, options)
136
137 def create_module(model, options):
138   n = model.make_name()
139   def go(model):
140     model.modules[n] = (model.source, model.decls)
141     (model.source, model.decls) = ('', {})
142   options += [lambda model, options: add_import(model, options, n)]
143   return go
144
145 def add_top_level_decl(model, options):
146   n = model.make_name()
147   d = random.choice([decl for decl in decls if decl.valid(model)])
148   def go(model):
149     if not d.valid(model):
150       return
151     d.apply(model, n)
152   return go
153
154 def add_import(model, options, module_name):
155   def go(model):
156     if module_name in model.modules:
157       model.source += '#include "%s.h"\n' % module_name
158       model.decls.update(model.modules[module_name][1])
159   return go
160
161 sys.stdout.write('Finding bug: ')
162 while True:
163   if generate():
164     break
165   sys.stdout.write('.')
166   sys.stdout.flush()