1
1
package org.utbot.engine.greyboxfuzzer
2
2
3
+ import kotlinx.coroutines.flow.FlowCollector
4
+ import kotlinx.coroutines.flow.flow
3
5
import org.utbot.engine.*
4
6
import org.utbot.engine.greyboxfuzzer.generator.*
5
7
import org.utbot.engine.greyboxfuzzer.mutator.Mutator
@@ -12,8 +14,8 @@ import org.utbot.framework.plugin.api.util.*
12
14
import org.utbot.framework.util.sootMethod
13
15
import org.utbot.instrumentation.ConcreteExecutor
14
16
import org.utbot.engine.greyboxfuzzer.quickcheck.generator.GeneratorContext
17
+ import java.lang.reflect.Executable
15
18
import java.lang.reflect.Field
16
- import java.lang.reflect.Method
17
19
import kotlin.random.Random
18
20
19
21
class GreyBoxFuzzer (
@@ -23,24 +25,19 @@ class GreyBoxFuzzer(
23
25
private val timeBudgetInMillis : Long
24
26
) {
25
27
26
- private val methodLines =
27
- methodUnderTest.sootMethod.activeBody.units
28
- .map { it.javaSourceStartLineNumber }
29
- .filter { it != - 1 }
30
- .toSet()
31
- private val seeds = SeedCollector (methodLines = methodLines)
32
- private val succeededExecutions = mutableListOf<UtGreyBoxFuzzedExecution >()
28
+ private var methodInstructionsIds: Set <Long >? = null
29
+ private var seeds: SeedCollector ? = null
33
30
private val timeRemain
34
31
get() = timeOfStart + timeBudgetInMillis - System .currentTimeMillis()
35
32
private val timeOfStart = System .currentTimeMillis()
36
- private val percentageOfTimeBudgetToChangeMode = 10
33
+ private val percentageOfTimeBudgetToChangeMode = 25
37
34
38
- suspend fun fuzz (): Sequence < UtExecution > {
35
+ suspend fun fuzz () = flow {
39
36
logger.debug { " Started to fuzz ${methodUnderTest.name} " }
40
37
val generatorContext = GeneratorContext ()
41
38
val javaClazz = methodUnderTest.classId.jClass
42
39
val sootMethod = methodUnderTest.sootMethod
43
- val javaMethod = sootMethod.toJavaMethod()!!
40
+ val javaMethod = sootMethod.toJavaMethod() ? : return @flow
44
41
val classFieldsUsedByFunc = sootMethod.getClassFieldsUsedByFunc(javaClazz)
45
42
while (timeRemain > 0 || ! isMethodCovered()) {
46
43
explorationStage(
@@ -49,15 +46,14 @@ class GreyBoxFuzzer(
49
46
methodUnderTest,
50
47
generatorContext
51
48
)
52
- logger.debug { " SEEDS AFTER EXPLORATION STAGE = ${seeds.seedsSize()} " }
49
+ logger.debug { " SEEDS AFTER EXPLORATION STAGE = ${seeds? .seedsSize()} " }
53
50
if (timeRemain < 0 || isMethodCovered()) break
54
51
exploitationStage()
55
52
}
56
- return succeededExecutions.asSequence()
57
53
}
58
54
59
- private suspend fun explorationStage (
60
- method : Method ,
55
+ private suspend fun FlowCollector<UtExecution>. explorationStage (
56
+ method : Executable ,
61
57
classFieldsUsedByFunc : Set <Field >,
62
58
methodUnderTest : ExecutableId ,
63
59
generatorContext : GeneratorContext
@@ -112,8 +108,8 @@ class GreyBoxFuzzer(
112
108
parameter,
113
109
index,
114
110
generatorContext,
115
- GreyBoxFuzzerGenerators .sourceOfRandomness,
116
- GreyBoxFuzzerGenerators .genStatus
111
+ GreyBoxFuzzerGeneratorsAndSettings .sourceOfRandomness,
112
+ GreyBoxFuzzerGeneratorsAndSettings .genStatus
117
113
)
118
114
}
119
115
logger.debug { " Generated params = $generatedParameters " }
@@ -123,23 +119,27 @@ class GreyBoxFuzzer(
123
119
try {
124
120
logger.debug { " Execution started" }
125
121
val executionResult = execute(stateBefore, methodUnderTest)
122
+ if (methodInstructionsIds == null ) {
123
+ methodInstructionsIds = executionResult.methodInstructionsIds
124
+ seeds = SeedCollector (methodInstructionsIds = methodInstructionsIds!! )
125
+ }
126
+ seeds ? : continue
126
127
logger.debug { " Execution result: $executionResult " }
127
128
val seedCoverage = getCoverage(executionResult)
128
129
logger.debug { " Calculating seed score" }
129
- val seedScore = seeds.calcSeedScore(seedCoverage)
130
+ val seedScore = seeds!! .calcSeedScore(seedCoverage)
130
131
logger.debug { " Adding seed" }
131
132
val seed = Seed (thisInstance, generatedParameters, seedCoverage, seedScore)
132
- if (seeds.isSeedOpensNewCoverage(seed)) {
133
- succeededExecutions.add (
133
+ if (seeds!! .isSeedOpensNewCoverage(seed)) {
134
+ emit (
134
135
UtGreyBoxFuzzedExecution (
135
136
stateBefore,
136
- executionResult.result,
137
- coverage = executionResult.coverage,
138
- testMethodName = methodUnderTest.name
137
+ executionResult,
138
+ coverage = executionResult.coverage
139
139
)
140
140
)
141
141
}
142
- seeds.addSeed(seed)
142
+ seeds!! .addSeed(seed)
143
143
logger.debug { " Execution result: ${executionResult.result} " }
144
144
logger.debug { " Seed score = $seedScore " }
145
145
} catch (e: Throwable ) {
@@ -154,10 +154,10 @@ class GreyBoxFuzzer(
154
154
}
155
155
}
156
156
157
- private suspend fun exploitationStage () {
157
+ private suspend fun FlowCollector<UtExecution>. exploitationStage () {
158
158
logger.debug { " Exploitation began" }
159
- if (seeds.seedsSize() == 0 ) return
160
- if (seeds.all { it.parameters.isEmpty() }) return
159
+ if (seeds == null || seeds !! .seedsSize() == 0 ) return
160
+ if (seeds!! .all { it.parameters.isEmpty() }) return
161
161
val startTime = System .currentTimeMillis()
162
162
val endTime = startTime + timeBudgetInMillis / percentageOfTimeBudgetToChangeMode
163
163
var iterationNumber = 0
@@ -167,13 +167,13 @@ class GreyBoxFuzzer(
167
167
if (iterationNumber > 30_000 ) return
168
168
logger.debug { " Func: ${methodUnderTest.name} Mutation iteration number $iterationNumber " }
169
169
iterationNumber++
170
- val randomSeed = seeds.getRandomWeightedSeed()
170
+ val randomSeed = seeds!! .getRandomWeightedSeed()
171
171
logger.debug { " Random seed params = ${randomSeed.parameters} " }
172
172
val mutatedSeed =
173
173
Mutator .mutateSeed(
174
174
randomSeed,
175
- GreyBoxFuzzerGenerators .sourceOfRandomness,
176
- GreyBoxFuzzerGenerators .genStatus
175
+ GreyBoxFuzzerGeneratorsAndSettings .sourceOfRandomness,
176
+ GreyBoxFuzzerGeneratorsAndSettings .genStatus
177
177
)
178
178
if (mutatedSeed == randomSeed) {
179
179
logger.debug { " Cant mutate seed" }
@@ -186,17 +186,16 @@ class GreyBoxFuzzer(
186
186
logger.debug { " Execution result: $executionResult " }
187
187
val seedScore = getCoverage(executionResult)
188
188
mutatedSeed.score = 0.0
189
- if (seeds.isSeedOpensNewCoverage(mutatedSeed)) {
190
- succeededExecutions.add (
189
+ if (seeds!! .isSeedOpensNewCoverage(mutatedSeed)) {
190
+ emit (
191
191
UtGreyBoxFuzzedExecution (
192
192
stateBefore,
193
- executionResult.result,
194
- coverage = executionResult.coverage,
195
- testMethodName = methodUnderTest.name
193
+ executionResult,
194
+ coverage = executionResult.coverage
196
195
)
197
196
)
198
197
}
199
- seeds.addSeed(mutatedSeed)
198
+ seeds!! .addSeed(mutatedSeed)
200
199
logger.debug { " Execution result: ${executionResult.result} " }
201
200
logger.debug { " Seed score = $seedScore " }
202
201
} catch (e: Throwable ) {
@@ -208,23 +207,25 @@ class GreyBoxFuzzer(
208
207
209
208
private fun getCoverage (
210
209
executionResult : UtFuzzingConcreteExecutionResult
211
- ): Set <Int > {
210
+ ): Set <Long > {
212
211
val currentMethodCoverage = executionResult.coverage.coveredInstructions
213
212
.asSequence()
213
+ .filter { it.className == methodUnderTest.classId.name.replace(' .' , ' /' ) }
214
214
.filter { it.methodSignature == methodUnderTest.signature }
215
- .map { it.lineNumber }
216
- .filter { it in methodLines }
215
+ .map { it.id }
216
+ .filter { it in methodInstructionsIds !! }
217
217
.toSet()
218
- logger.debug { " Covered lines $ currentMethodCoverage from $methodLines " }
219
- executionResult.coverage.coveredInstructions.forEach { CoverageCollector .coverage.add (it) }
218
+ logger.debug { " Covered instructions ${ currentMethodCoverage.count()} from ${methodInstructionsIds !! .size} " }
219
+ executionResult.coverage.coveredInstructions.forEach { CoverageCollector .addCoverage (it) }
220
220
return currentMethodCoverage
221
221
}
222
222
223
223
private fun isMethodCovered (): Boolean {
224
- val coveredLines =
225
- CoverageCollector .coverage.filter { it.methodSignature == methodUnderTest.signature }.map { it.lineNumber }
224
+ methodInstructionsIds ? : return false
225
+ val coveredInstructions =
226
+ CoverageCollector .coverage.filter { it.methodSignature == methodUnderTest.signature }.map { it.id }
226
227
.toSet()
227
- return coveredLines .containsAll(methodLines )
228
+ return coveredInstructions .containsAll(methodInstructionsIds !! )
228
229
}
229
230
230
231
private suspend fun ConcreteExecutor <UtFuzzingConcreteExecutionResult , UtFuzzingExecutionInstrumentation >.executeConcretely (
@@ -253,12 +254,12 @@ class GreyBoxFuzzer(
253
254
254
255
255
256
private fun generateThisInstance (classId : ClassId , generatorContext : GeneratorContext ): ThisInstance =
256
- if (! methodUnderTest.isStatic) {
257
+ if (! methodUnderTest.isStatic && ! methodUnderTest.isConstructor ) {
257
258
DataGenerator .generateThis(
258
259
classId,
259
260
generatorContext,
260
- GreyBoxFuzzerGenerators .sourceOfRandomness,
261
- GreyBoxFuzzerGenerators .genStatus
261
+ GreyBoxFuzzerGeneratorsAndSettings .sourceOfRandomness,
262
+ GreyBoxFuzzerGeneratorsAndSettings .genStatus
262
263
)
263
264
} else {
264
265
StaticMethodThisInstance
0 commit comments