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