Skip to content

Commit 4d7d78a

Browse files
committed
Fix fuzz harness
1 parent 49c89a4 commit 4d7d78a

File tree

4 files changed

+259
-113
lines changed

4 files changed

+259
-113
lines changed

src/main/scala/fred/Translator.scala

-15
Original file line numberDiff line numberDiff line change
@@ -455,10 +455,6 @@ object Translator {
455455
s"Function: ${fn.name.value}, unused vars: $unusedParams"
456456
)
457457

458-
// println(lastUsagesPre.view.mapValues(_.map(_.name)).toMap)
459-
println(fn.name.value)
460-
println(lastUsagesPost.view.mapValues(_.map(_.name)).toMap)
461-
462458
// param names don't need to be mangled because they're the first occurrence
463459
val params = fn.params
464460
.map(param => s"${typeRefToC(param.typ.name)} ${param.name.value}")
@@ -789,17 +785,6 @@ object Translator {
789785
decrRc(mangledVars.getOrElse(varDef, varDef.name), varDef.typ)
790786
}.mkString("\n", "\n", "")
791787

792-
if (lastUsagesPre.getOrElse(expr, Set.empty).nonEmpty) {
793-
println(
794-
s"last usage pre: ${lastUsagesPre(expr).map(_.name)}, expr: $expr"
795-
)
796-
}
797-
if (lastUsagesPost.getOrElse(expr, Set.empty).nonEmpty) {
798-
println(
799-
s"last usage post: ${lastUsagesPost(expr).map(_.name)}, expr: $expr"
800-
)
801-
}
802-
803788
(setupDecrs + setup, toC, teardown + teardownDecrs)
804789
}
805790

src/test/scala/fred/ExecTests.scala

+5-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package fred
33
import scala.sys.process.*
44
import java.nio.file.Files
55
import java.nio.file.Path
6+
import scala.util.Random
67

78
import org.scalatest.funsuite.AnyFunSuite
89
import snapshot4s.scalatest.SnapshotAssertions
@@ -221,17 +222,18 @@ object ExecTests {
221222
case None =>
222223
}
223224

224-
Compiler.invokeGCC(generatedC, "a.out", settings)
225+
val exeName = Random.alphanumeric.take(10).mkString + ".bin"
226+
Compiler.invokeGCC(generatedC, exeName, settings)
225227

226228
val stderrBuf = StringBuilder()
227229
val stdout =
228230
try {
229-
"valgrind --leak-check=full --show-leak-kinds=all -s ./a.out" !!
231+
s"valgrind --leak-check=full --show-leak-kinds=all -s ./$exeName" !!
230232
ProcessLogger(_ => {}, err => stderrBuf.append('\n').append(err))
231233
} catch {
232234
case e: RuntimeException =>
233235
throw RuntimeException(stderrBuf.toString, e)
234-
}
236+
} finally { Files.deleteIfExists(Path.of(exeName)) }
235237
val valgrindOut = stderrBuf.toString
236238
expected match {
237239
case Some(expected) => assert(

src/test/scala/fred/FuzzTests.scala

+244-49
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package fred
22

33
import scala.util.Random
4+
import java.nio.file.Files
5+
import java.nio.file.Path
46

57
import org.scalatest.matchers.should
68
import org.scalatest.propspec.AnyPropSpec
@@ -14,6 +16,234 @@ import fred.GenUtil.{GenStmt, GeneratedProgram}
1416

1517
class FuzzTests
1618
extends AnyPropSpec with ScalaCheckPropertyChecks with should.Matchers {
19+
20+
val bad = GeneratedProgram(
21+
List(
22+
TypeDef(
23+
Spanned("OptT0", Span(-1, -1)),
24+
List(
25+
EnumCase(
26+
Spanned("SomeT0", Span(-1, -1)),
27+
List(FieldDef(
28+
false,
29+
Spanned("value", Span(-1, -1)),
30+
TypeRef("T0", Span(-1, -1)),
31+
Span(-1, -1)
32+
)),
33+
Span(-1, -1)
34+
),
35+
EnumCase(Spanned("NoneT0", Span(-1, -1)), List(), Span(-1, -1))
36+
),
37+
Span(-1, -1)
38+
),
39+
TypeDef(
40+
Spanned("OptT1", Span(-1, -1)),
41+
List(
42+
EnumCase(
43+
Spanned("SomeT1", Span(-1, -1)),
44+
List(FieldDef(
45+
false,
46+
Spanned("value", Span(-1, -1)),
47+
TypeRef("T1", Span(-1, -1)),
48+
Span(-1, -1)
49+
)),
50+
Span(-1, -1)
51+
),
52+
EnumCase(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
53+
),
54+
Span(-1, -1)
55+
),
56+
TypeDef(
57+
Spanned("T1", Span(-1, -1)),
58+
List(EnumCase(
59+
Spanned("T1", Span(-1, -1)),
60+
List(
61+
FieldDef(
62+
true,
63+
Spanned("f0", Span(-1, -1)),
64+
TypeRef("OptT1", Span(-1, -1)),
65+
Span(-1, -1)
66+
),
67+
FieldDef(
68+
true,
69+
Spanned("f1", Span(-1, -1)),
70+
TypeRef("OptT0", Span(-1, -1)),
71+
Span(-1, -1)
72+
),
73+
FieldDef(
74+
true,
75+
Spanned("f2", Span(-1, -1)),
76+
TypeRef("OptT1", Span(-1, -1)),
77+
Span(-1, -1)
78+
),
79+
FieldDef(
80+
true,
81+
Spanned("f3", Span(-1, -1)),
82+
TypeRef("OptT1", Span(-1, -1)),
83+
Span(-1, -1)
84+
)
85+
),
86+
Span(-1, -1)
87+
)),
88+
Span(-1, -1)
89+
),
90+
TypeDef(
91+
Spanned("T0", Span(-1, -1)),
92+
List(EnumCase(
93+
Spanned("T0", Span(-1, -1)),
94+
List(
95+
FieldDef(
96+
true,
97+
Spanned("f0", Span(-1, -1)),
98+
TypeRef("OptT0", Span(-1, -1)),
99+
Span(-1, -1)
100+
),
101+
FieldDef(
102+
true,
103+
Spanned("f1", Span(-1, -1)),
104+
TypeRef("OptT1", Span(-1, -1)),
105+
Span(-1, -1)
106+
),
107+
FieldDef(
108+
true,
109+
Spanned("f2", Span(-1, -1)),
110+
TypeRef("OptT0", Span(-1, -1)),
111+
Span(-1, -1)
112+
),
113+
FieldDef(
114+
true,
115+
Spanned("f3", Span(-1, -1)),
116+
TypeRef("OptT1", Span(-1, -1)),
117+
Span(-1, -1)
118+
)
119+
),
120+
Span(-1, -1)
121+
)),
122+
Span(-1, -1)
123+
)
124+
),
125+
Map("vT1_0" -> "T1", "vT1_1" -> "T1", "vT0_0" -> "T0", "vT0_1" -> "T0"),
126+
List(
127+
(
128+
"vT1_0",
129+
CtorCall(
130+
Spanned("T1", Span(-1, -1)),
131+
List(
132+
(
133+
Spanned("f0", Span(-1, -1)),
134+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
135+
),
136+
(
137+
Spanned("f1", Span(-1, -1)),
138+
CtorCall(Spanned("NoneT0", Span(-1, -1)), List(), Span(-1, -1))
139+
),
140+
(
141+
Spanned("f2", Span(-1, -1)),
142+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
143+
),
144+
(
145+
Spanned("f3", Span(-1, -1)),
146+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
147+
)
148+
),
149+
Span(-1, -1)
150+
)
151+
),
152+
(
153+
"vT1_1",
154+
CtorCall(
155+
Spanned("T1", Span(-1, -1)),
156+
List(
157+
(
158+
Spanned("f0", Span(-1, -1)),
159+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
160+
),
161+
(
162+
Spanned("f1", Span(-1, -1)),
163+
CtorCall(Spanned("NoneT0", Span(-1, -1)), List(), Span(-1, -1))
164+
),
165+
(
166+
Spanned("f2", Span(-1, -1)),
167+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
168+
),
169+
(
170+
Spanned("f3", Span(-1, -1)),
171+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
172+
)
173+
),
174+
Span(-1, -1)
175+
)
176+
),
177+
(
178+
"vT0_0",
179+
CtorCall(
180+
Spanned("T0", Span(-1, -1)),
181+
List(
182+
(
183+
Spanned("f0", Span(-1, -1)),
184+
CtorCall(Spanned("NoneT0", Span(-1, -1)), List(), Span(-1, -1))
185+
),
186+
(
187+
Spanned("f1", Span(-1, -1)),
188+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
189+
),
190+
(
191+
Spanned("f2", Span(-1, -1)),
192+
CtorCall(Spanned("NoneT0", Span(-1, -1)), List(), Span(-1, -1))
193+
),
194+
(
195+
Spanned("f3", Span(-1, -1)),
196+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
197+
)
198+
),
199+
Span(-1, -1)
200+
)
201+
),
202+
(
203+
"vT0_1",
204+
CtorCall(
205+
Spanned("T0", Span(-1, -1)),
206+
List(
207+
(
208+
Spanned("f0", Span(-1, -1)),
209+
CtorCall(Spanned("NoneT0", Span(-1, -1)), List(), Span(-1, -1))
210+
),
211+
(
212+
Spanned("f1", Span(-1, -1)),
213+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
214+
),
215+
(
216+
Spanned("f2", Span(-1, -1)),
217+
CtorCall(Spanned("NoneT0", Span(-1, -1)), List(), Span(-1, -1))
218+
),
219+
(
220+
Spanned("f3", Span(-1, -1)),
221+
CtorCall(Spanned("NoneT1", Span(-1, -1)), List(), Span(-1, -1))
222+
)
223+
),
224+
Span(-1, -1)
225+
)
226+
)
227+
),
228+
List(
229+
GenStmt.Assign("vT0_0", "f1", "T1", "vT1_0"),
230+
GenStmt.Assign("vT0_1", "f1", "T1", "vT1_0")
231+
)
232+
)
233+
234+
def fuzz(prog: GeneratedProgram, out: String): Unit = {
235+
val ast = prog.asAst
236+
val settings = Settings(includeMemcheck = true)
237+
try { ExecTests.valgrindCheck(ast, None, None, settings = settings) }
238+
catch {
239+
case e =>
240+
given typer: Typer = Typer.resolveAllTypes(ast)
241+
val c = Translator.toC(ast, settings)
242+
Files.write(Path.of("fuzz-out", out), c.getBytes())
243+
throw e
244+
}
245+
}
246+
17247
property("No intermediate checks", Slow) {
18248
given PropertyCheckConfiguration =
19249
PropertyCheckConfiguration(minSize = 5, sizeRange = 30)
@@ -28,47 +258,31 @@ class FuzzTests
28258
assignExprs <- GenUtil
29259
.genAssignments(allTypes, allVars.view.mapValues(_.keySet).toMap)
30260
} yield {
31-
val finalRes = IntLiteral(0, Span.synth)
32-
val withAssignments = assignExprs.foldRight(finalRes: Expr) {
33-
(stmt, acc) => BinExpr(stmt.asAst, Spanned(BinOp.Seq, Span.synth), acc)
34-
}
35-
val withVarDefs = sccs.flatMap(
36-
_.filterNot(_.name.startsWith("Opt")).flatMap(typ => allVars(typ.name))
37-
).foldLeft(withAssignments) { case (body, (varName, value)) =>
38-
LetExpr(Spanned(varName, Span.synth), value, body, Span.synth)
39-
}
40-
ParsedFile(
261+
GeneratedProgram(
41262
allTypes,
42-
List(FnDef(
43-
Spanned("main", Span.synth),
44-
Nil,
45-
TypeRef("int", Span.synth),
46-
withVarDefs,
47-
Span.synth
48-
))
263+
allVars.flatMap((typ, vars) => vars.keySet.map(_ -> typ)).toMap,
264+
sccs.flatMap(
265+
_.filterNot(_.name.startsWith("Opt"))
266+
.flatMap(typ => allVars(typ.name))
267+
),
268+
assignExprs
49269
)
50270
}
51271

52-
forAll(genFull) { parsedFile =>
53-
ExecTests.valgrindCheck(
54-
parsedFile,
55-
None,
56-
None,
57-
save = Some("blech-oiwjsd.c"),
58-
settings = Settings(includeMemcheck = true)
59-
)
60-
}
272+
forAll(genFull) { generated => fuzz(generated, "no-intermediate.c") }
61273
}
62274

63275
property("With intermediate checks", Slow) {
64276
given PropertyCheckConfiguration =
65-
PropertyCheckConfiguration(minSize = 5, sizeRange = 60)
277+
PropertyCheckConfiguration(minSize = 10, sizeRange = 60)
66278
given Shrink[GeneratedProgram] = GenUtil.shrinkGenerated
67279

280+
val checkRate = 0.1
281+
68282
// TODO maybe randomness isn't needed here, just insert one check every few statements or something
69283
def insertValgrindChecks(stmts: List[GenStmt]): Gen[List[GenStmt]] = {
70284
GenUtil.sequence(stmts.map { stmt =>
71-
Gen.prob(0.1).map { insert =>
285+
Gen.prob(checkRate).map { insert =>
72286
if (insert) List(stmt, GenStmt.ValgrindCheck) else List(stmt)
73287
}
74288
}).map(_.flatten)
@@ -84,8 +298,7 @@ class FuzzTests
84298
assignExprsOrdered <- GenUtil
85299
.genAssignments(allTypes, allVars.view.mapValues(_.keySet).toMap)
86300
seed <- Gen.long
87-
rand = Random(seed)
88-
assignExprs = Random.shuffle(assignExprsOrdered)
301+
assignExprs = Random(seed).shuffle(assignExprsOrdered)
89302
withChecks <- insertValgrindChecks(assignExprs)
90303
} yield {
91304
GeneratedProgram(
@@ -99,24 +312,6 @@ class FuzzTests
99312
)
100313
}
101314

102-
forAll(genFull) {
103-
generated =>
104-
ExecTests.valgrindCheck(
105-
generated.asAst,
106-
None,
107-
None,
108-
save = Some("blech-1234sd.c"),
109-
settings = Settings(includeMemcheck = true),
110-
processC = c => {
111-
// TODO make lexical lifetimes or whatever they're called a setting in
112-
// the compiler instead of this buffoonery
113-
// Remove all $decr_TX calls for the variables at the end (but not $decr_OptTX calls)
114-
// because we're calling $decr_TX ourselves above
115-
val (beforeMain, afterMain) = c.splitAt(c.indexOf("int main"))
116-
beforeMain +
117-
afterMain.replaceAll(""" \$decr_T\d+\(vT\d+_\d+\);\n""", "")
118-
}
119-
)
120-
}
315+
forAll(genFull) { generated => fuzz(generated, "with-intermediate.c") }
121316
}
122317
}

0 commit comments

Comments
 (0)