Skip to content

Process some concrete failures gracefully #2456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,12 @@ data class UtConcreteExecutionFailure(override val exception: Throwable) : UtExe

/**
* Represents a failure in instrumented process
* that is not actually caused by concrete execution.
* that is not actually caused by concrete method under test call.
*
* For example, failure may occured during data preparation for a concrete call.
* For example, failure may have occurred during method arguments preparation
* or statics initializers processing during object instance creation.
*/
data class UtConcreteExecutionProcessedFailure(override val exception: Throwable): UtExecutionFailure()
data class UtConcreteExecutionProcessedFailure(override val exception: Throwable) : UtExecutionFailure()

val UtExecutionResult.isSuccess: Boolean
get() = this is UtExecutionSuccess
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,12 @@ import org.utbot.instrumentation.ConcreteExecutor
import org.utbot.instrumentation.instrumentation.Instrumentation
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionData
import org.utbot.instrumentation.instrumentation.execution.UtConcreteExecutionResult
import org.utbot.instrumentation.instrumentation.execution.UtExecutionInstrumentation
import org.utbot.taint.*
import org.utbot.taint.model.TaintConfiguration
import soot.jimple.Stmt
import soot.tagkit.ParamNamesTag
import java.lang.reflect.Method
import java.util.function.Consumer
import java.util.function.Predicate
import kotlin.math.min
import kotlin.system.measureTimeMillis

Expand Down Expand Up @@ -317,10 +315,7 @@ class UtBotSymbolicEngine(
val concreteExecutionResult =
concreteExecutor.executeConcretely(methodUnderTest, stateBefore, instrumentation, UtSettings.concreteExecutionDefaultTimeoutInInstrumentedProcessMillis)

concreteExecutionResult.processedFailure()?.let { failure ->
emitFailedConcreteExecutionResult(stateBefore, failure.exception)

logger.debug { "Instrumented process failed with exception ${failure.exception} before concrete execution started" }
if (failureCanBeProcessedGracefully(concreteExecutionResult, executionToRollbackOn = null)) {
return@measureTime
}

Expand Down Expand Up @@ -668,10 +663,7 @@ class UtBotSymbolicEngine(
UtSettings.concreteExecutionDefaultTimeoutInInstrumentedProcessMillis
)

concreteExecutionResult.processedFailure()?.let { failure ->
emitFailedConcreteExecutionResult(stateBefore, failure.exception)

logger.debug { "Instrumented process failed with exception ${failure.exception} before concrete execution started" }
if (failureCanBeProcessedGracefully(concreteExecutionResult, symbolicUtExecution)) {
return
}

Expand Down Expand Up @@ -699,6 +691,29 @@ class UtBotSymbolicEngine(
}
}

private suspend fun FlowCollector<UtResult>.failureCanBeProcessedGracefully(
concreteExecutionResult: UtConcreteExecutionResult,
executionToRollbackOn: UtExecution?,
): Boolean {
concreteExecutionResult.processedFailure()?.let { failure ->
// If concrete execution failed to some reasons that are not process death or cancellation
// when we call something that is processed successfully by symbolic engine,
// we should:
// - roll back to symbolic execution data ignoring failing concrete (is symbolic execution exists);
// - do not emit an execution if there is nothing to roll back on.

// Note that this situation is suspicious anyway, so we log a WARN message about the failure.
executionToRollbackOn?.let {
emit(it)
}

logger.warn { "Instrumented process failed with exception ${failure.exception} before concrete execution started" }
return true
}

return false
}

/**
* Collects entry method statement path for ML. Eliminates duplicated statements, e.g. assignment with invocation
* in right part.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,13 @@ enum class ExecutionGroup {
EXPLICITLY_THROWN_UNCHECKED_EXCEPTIONS,
OVERFLOWS,
TIMEOUTS,

/**
* Executions that caused by `InstrumentedProcessDeath` exception.
* Generated tests will be disabled du to possible JVM crash.
*/
CRASH_SUITE,

TAINT_ANALYSIS,
SECURITY;

Expand All @@ -221,10 +227,11 @@ private fun UtExecutionResult.clusterKind() = when (this) {
is UtStreamConsumingFailure -> ExecutionGroup.ERROR_SUITE
is UtOverflowFailure -> ExecutionGroup.OVERFLOWS
is UtTimeoutException -> ExecutionGroup.TIMEOUTS
is UtConcreteExecutionProcessedFailure -> ExecutionGroup.CRASH_SUITE
is UtConcreteExecutionFailure -> ExecutionGroup.CRASH_SUITE
is UtSandboxFailure -> ExecutionGroup.SECURITY
is UtTaintAnalysisFailure -> ExecutionGroup.TAINT_ANALYSIS
is UtConcreteExecutionProcessedFailure ->
error("Processed failure must not be found in generated tests, it can just happen on intermediate phases of tests generation")
}

/**
Expand Down