in src/jvm/main/org/jetbrains/kotlinx/lincheck/strategy/managed/ManagedStrategy.kt [330:387]
override fun tryCollectTrace(result: InvocationResult): Pair<Trace?, InvocationResult> {
val detectedByStrategy = (suddenInvocationResult != null)
val canCollectTrace = when {
result is CompletedInvocationResult -> true
result is ValidationFailureInvocationResult -> true
// Timeout poisons the runner, making it impossible to
// re-run invocation and collect the trace.
result is RunnerTimeoutInvocationResult -> false
detectedByStrategy -> true
else -> false
}
if (!canCollectTrace) {
// Interleaving events can be collected almost always,
// except for the strange cases such as runner's timeout or exceptions in Lincheck.
return null to result
}
collectTrace = true
loopDetector.enableReplayMode(
failDueToDeadlockInTheEnd =
result is ManagedDeadlockInvocationResult ||
result is ObstructionFreedomViolationInvocationResult
)
resetTraceDebuggerTrackerIds()
val loggedResults = runInvocation()
// In case the runner detects a deadlock, some threads can still be in an active state,
// simultaneously adding events to the TraceCollector, which leads to an inconsistent trace.
// Therefore, if the runner detects deadlock, we don't even try to collect trace.
if (loggedResults is RunnerTimeoutInvocationResult) return null to result
val threadNames = MutableList(threadScheduler.nThreads) { "" }
getRegisteredThreads().forEach { (threadId, thread) ->
when (val threadNumber = objectTracker.getObjectDisplayNumber(thread)) {
0 -> threadNames[threadId] = "Main Thread"
else -> threadNames[threadId] = "Thread $threadNumber"
}
}
val trace = Trace(traceCollector!!.trace, threadNames)
val sameResultTypes = loggedResults.javaClass == result.javaClass
val sameResults = (
loggedResults !is CompletedInvocationResult ||
result !is CompletedInvocationResult ||
loggedResults.results == result.results
)
check(sameResultTypes && sameResults) {
StringBuilder().apply {
appendLine("Non-determinism found. Probably caused by non-deterministic code (WeakHashMap, Object.hashCode, etc).")
appendLine("== Reporting the first execution without execution trace ==")
appendLine(result.toLincheckFailure(scenario ?: emptyScenario(), null, analysisProfile))
appendLine("== Reporting the second execution ==")
appendLine(loggedResults.toLincheckFailure(scenario ?: emptyScenario(), trace, analysisProfile).toString())
}.toString()
}
return trace to loggedResults
}