[Reland][Runtimes] Merge 'compile_commands.json' files from runtimes build (#116303)
[llvm-project.git] / clang / utils / modfuzz.py
blob49ac522cd17e1bdc3a9df4d1bfa3ca22ee6a5472
1 #!/usr/bin/env python
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.
7 from __future__ import absolute_import, division, print_function
8 import random
9 import subprocess
10 import sys
11 import os
13 clang = sys.argv[1]
14 none_opts = 0.3
17 class Decl(object):
18 def __init__(self, text, depends=[], provides=[], conflicts=[]):
19 self.text = text
20 self.depends = depends
21 self.provides = provides
22 self.conflicts = conflicts
24 def valid(self, model):
25 for i in self.depends:
26 if i not in model.decls:
27 return False
28 for i in self.conflicts:
29 if i in model.decls:
30 return False
31 return True
33 def apply(self, model, name):
34 for i in self.provides:
35 model.decls[i] = True
36 model.source += self.text % {"name": name}
39 decls = [
40 Decl("struct X { int n; };\n", provides=["X"], conflicts=["X"]),
41 Decl('static_assert(X{.n=1}.n == 1, "");\n', depends=["X"]),
42 Decl("X %(name)s;\n", depends=["X"]),
46 class FS(object):
47 def __init__(self):
48 self.fs = {}
49 self.prevfs = {}
51 def write(self, path, contents):
52 self.fs[path] = contents
54 def done(self):
55 for f, s in self.fs.items():
56 if self.prevfs.get(f) != s:
57 f = file(f, "w")
58 f.write(s)
59 f.close()
61 for f in self.prevfs:
62 if f not in self.fs:
63 os.remove(f)
65 self.prevfs, self.fs = self.fs, {}
68 fs = FS()
71 class CodeModel(object):
72 def __init__(self):
73 self.source = ""
74 self.modules = {}
75 self.decls = {}
76 self.i = 0
78 def make_name(self):
79 self.i += 1
80 return "n" + str(self.i)
82 def fails(self):
83 fs.write(
84 "module.modulemap",
85 "".join(
86 'module %s { header "%s.h" export * }\n' % (m, m)
87 for m in self.modules.keys()
91 for m, (s, _) in self.modules.items():
92 fs.write("%s.h" % m, s)
94 fs.write("main.cc", self.source)
95 fs.done()
97 return (
98 subprocess.call(
99 [clang, "-std=c++11", "-c", "-fmodules", "main.cc", "-o", "/dev/null"]
101 != 0
105 def generate():
106 model = CodeModel()
107 m = []
109 try:
110 for d in mutations(model):
111 d(model)
112 m.append(d)
113 if not model.fails():
114 return
115 except KeyboardInterrupt:
116 print()
117 return True
119 sys.stdout.write("\nReducing:\n")
120 sys.stdout.flush()
122 try:
123 while True:
124 assert m, "got a failure with no steps; broken clang binary?"
125 i = random.choice(list(range(len(m))))
126 x = m[0:i] + m[i + 1 :]
127 m2 = CodeModel()
128 for d in x:
129 d(m2)
130 if m2.fails():
131 m = x
132 model = m2
133 else:
134 sys.stdout.write(".")
135 sys.stdout.flush()
136 except KeyboardInterrupt:
137 # FIXME: Clean out output directory first.
138 model.fails()
139 return model
142 def choose(options):
143 while True:
144 i = int(random.uniform(0, len(options) + none_opts))
145 if i >= len(options):
146 break
147 yield options[i]
150 def mutations(model):
151 options = [create_module, add_top_level_decl]
152 for opt in choose(options):
153 yield opt(model, options)
156 def create_module(model, options):
157 n = model.make_name()
159 def go(model):
160 model.modules[n] = (model.source, model.decls)
161 (model.source, model.decls) = ("", {})
163 options += [lambda model, options: add_import(model, options, n)]
164 return go
167 def add_top_level_decl(model, options):
168 n = model.make_name()
169 d = random.choice([decl for decl in decls if decl.valid(model)])
171 def go(model):
172 if not d.valid(model):
173 return
174 d.apply(model, n)
176 return go
179 def add_import(model, options, module_name):
180 def go(model):
181 if module_name in model.modules:
182 model.source += '#include "%s.h"\n' % module_name
183 model.decls.update(model.modules[module_name][1])
185 return go
188 sys.stdout.write("Finding bug: ")
189 while True:
190 if generate():
191 break
192 sys.stdout.write(".")
193 sys.stdout.flush()