hu.bme.mit.theta.xcfa.cli.checkers.InProcessChecker.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of theta-xcfa-cli Show documentation
Show all versions of theta-xcfa-cli Show documentation
Xcfa Cli subproject in the Theta model checking framework
The newest version!
/*
* Copyright 2024 Budapest University of Technology and Economics
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package hu.bme.mit.theta.xcfa.cli.checkers
import com.zaxxer.nuprocess.NuAbstractProcessHandler
import com.zaxxer.nuprocess.NuProcess
import com.zaxxer.nuprocess.NuProcessBuilder
import hu.bme.mit.theta.analysis.EmptyCex
import hu.bme.mit.theta.analysis.algorithm.EmptyProof
import hu.bme.mit.theta.analysis.algorithm.SafetyChecker
import hu.bme.mit.theta.analysis.algorithm.SafetyResult
import hu.bme.mit.theta.common.logging.Logger
import hu.bme.mit.theta.frontend.ParseContext
import hu.bme.mit.theta.xcfa.analysis.XcfaPrec
import hu.bme.mit.theta.xcfa.cli.XcfaCli
import hu.bme.mit.theta.xcfa.cli.params.*
import hu.bme.mit.theta.xcfa.cli.utils.CachingFileSerializer
import hu.bme.mit.theta.xcfa.cli.utils.getGson
import hu.bme.mit.theta.xcfa.model.XCFA
import java.io.File
import java.lang.System.err
import java.nio.ByteBuffer
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.io.path.createTempDirectory
class InProcessChecker(
val xcfa: XCFA,
val config: XcfaConfig,
val parseContext: ParseContext,
val logger: Logger,
) : SafetyChecker> {
override fun check(prec: XcfaPrec<*>?): SafetyResult {
return check()
}
override fun check(): SafetyResult {
val tempDir = createTempDirectory(config.outputConfig.resultFolder.toPath())
val xcfaJson = CachingFileSerializer.serialize("xcfa.json", xcfa) { getGson(xcfa).toJson(xcfa) }
val parseContextJson =
CachingFileSerializer.serialize("parseContext.json", parseContext) {
getGson(xcfa).toJson(parseContext)
}
val processConfig =
config.copy(
inputConfig = config.inputConfig.copy(input = xcfaJson, parseCtx = parseContextJson),
frontendConfig = config.frontendConfig.copy(inputType = InputType.JSON),
backendConfig = config.backendConfig.copy(inProcess = false, timeoutMs = 0),
outputConfig =
config.outputConfig.copy(
resultFolder = tempDir.toFile(),
cOutputConfig = COutputConfig(disable = true),
xcfaOutputConfig = XcfaOutputConfig(disable = true),
argConfig =
config.outputConfig.argConfig.copy(disable = false), // we need the arg to be produced
),
)
val configJson =
CachingFileSerializer.serialize("config.json", processConfig) {
getGson(xcfa).toJson(processConfig)
}
val pb =
NuProcessBuilder(
listOf(
ProcessHandle.current().info().command().orElse("java"),
"-Xss120m",
"-Xmx14210m",
"-cp",
File(XcfaCli::class.java.protectionDomain.codeSource.location.toURI()).absolutePath,
XcfaCli::class.qualifiedName,
"-c",
configJson.absolutePath,
)
.filterNotNull()
)
val processHandler = ProcessHandler()
pb.setProcessListener(processHandler)
val process: NuProcess = pb.start()
pb.environment().putAll(System.getenv())
val retCode = process.waitFor(config.backendConfig.timeoutMs, TimeUnit.MILLISECONDS)
val booleanSafetyResult =
if (retCode == Int.MIN_VALUE) {
if (processHandler.safetyResult == null) {
process.destroy(true)
throw ErrorCodeException(ExitCodes.TIMEOUT.code)
} else {
logger.write(
Logger.Level.RESULT,
"Config timed out but started writing result, trying to wait an additional 10%...",
)
val retCode = process.waitFor(config.backendConfig.timeoutMs / 10, TimeUnit.MILLISECONDS)
if (retCode != 0) {
throw ErrorCodeException(retCode)
} else {
processHandler.safetyResult
}
}
} else if (retCode != 0) {
throw ErrorCodeException(retCode)
} else {
processHandler.safetyResult
}
tempDir.toFile().listFiles()?.forEach {
it.copyTo(config.outputConfig.resultFolder.resolve(it.name), overwrite = true)
}
tempDir.toFile().deleteRecursively()
return booleanSafetyResult as SafetyResult
}
private class ProcessHandler : NuAbstractProcessHandler() {
private val stdout = LinkedList()
private var stdoutRemainder = ""
private val stderr = LinkedList()
private var stderrRemainder = ""
var safetyResult: SafetyResult<*, *>? = null
private set
override fun onStdout(buffer: ByteBuffer, closed: Boolean) {
if (!closed) {
val bytes = ByteArray(buffer.remaining())
buffer[bytes]
val str = bytes.decodeToString()
stdoutRemainder += str
if (stdoutRemainder.contains("SafetyResult Safe")) {
safetyResult = SafetyResult.safe(EmptyProof.getInstance())
}
if (stdoutRemainder.contains("SafetyResult Unsafe")) {
safetyResult = SafetyResult.unsafe(EmptyCex.getInstance(), EmptyProof.getInstance())
}
if (stdoutRemainder.contains("SafetyResult Unknown")) {
safetyResult = SafetyResult.unknown()
}
val newLines = stdoutRemainder.split("\n") // if ends with \n, last element will be ""
newLines.subList(0, newLines.size - 1).forEach {
stdout.add(it)
println("server: $it")
}
stdoutRemainder = newLines[newLines.size - 1]
}
}
override fun onStderr(buffer: ByteBuffer, closed: Boolean) {
if (!closed) {
val bytes = ByteArray(buffer.remaining())
buffer[bytes]
val str = bytes.decodeToString()
stderrRemainder += str
val newLines = stderrRemainder.split("\n") // if ends with \n, last element will be ""
newLines.subList(0, newLines.size - 1).forEach {
stderr.add(it)
err.println("server: $it")
}
stderrRemainder = newLines[newLines.size - 1]
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy