All Downloads are FREE. Search and download functionalities are using the official Maven repository.

hu.bme.mit.theta.xcfa.cli.ExecuteConfig.kt Maven / Gradle / Ivy

There is a newer version: 6.8.5
Show 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

import com.google.common.base.Stopwatch
import com.google.gson.GsonBuilder
import com.google.gson.JsonParser
import hu.bme.mit.theta.analysis.Action
import hu.bme.mit.theta.analysis.State
import hu.bme.mit.theta.analysis.Trace
import hu.bme.mit.theta.analysis.algorithm.SafetyResult
import hu.bme.mit.theta.analysis.algorithm.debug.ARGWebDebugger
import hu.bme.mit.theta.analysis.expl.ExplState
import hu.bme.mit.theta.analysis.utils.ArgVisualizer
import hu.bme.mit.theta.analysis.utils.TraceVisualizer
import hu.bme.mit.theta.cat.dsl.CatDslManager
import hu.bme.mit.theta.common.logging.Logger
import hu.bme.mit.theta.common.visualization.Graph
import hu.bme.mit.theta.common.visualization.writer.GraphvizWriter
import hu.bme.mit.theta.common.visualization.writer.WebDebuggerLogger
import hu.bme.mit.theta.frontend.ParseContext
import hu.bme.mit.theta.graphsolver.patterns.constraints.MCM
import hu.bme.mit.theta.xcfa.analysis.ErrorDetection
import hu.bme.mit.theta.xcfa.analysis.XcfaAction
import hu.bme.mit.theta.xcfa.analysis.XcfaState
import hu.bme.mit.theta.xcfa.analysis.coi.ConeOfInfluence
import hu.bme.mit.theta.xcfa.analysis.coi.XcfaCoiMultiThread
import hu.bme.mit.theta.xcfa.analysis.coi.XcfaCoiSingleThread
import hu.bme.mit.theta.xcfa.analysis.por.XcfaDporLts
import hu.bme.mit.theta.xcfa.analysis.por.XcfaSporLts
import hu.bme.mit.theta.xcfa.cli.checkers.getChecker
import hu.bme.mit.theta.xcfa.cli.params.*
import hu.bme.mit.theta.xcfa.cli.utils.*
import hu.bme.mit.theta.xcfa.cli.witnesses.XcfaTraceConcretizer
import hu.bme.mit.theta.xcfa.model.XCFA
import hu.bme.mit.theta.xcfa.model.toDot
import hu.bme.mit.theta.xcfa.passes.LbePass
import hu.bme.mit.theta.xcfa.passes.LoopUnrollPass
import hu.bme.mit.theta.xcfa.toC
import java.io.File
import java.util.concurrent.TimeUnit
import kotlin.random.Random

fun runConfig(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): SafetyResult<*, *> {
    propagateInputOptions(config, logger, uniqueLogger)

    registerAllSolverManagers(config.backendConfig.solverHome, logger)

    validateInputOptions(config, logger, uniqueLogger)

    val (xcfa, mcm, parseContext) = frontend(config, logger, uniqueLogger)

    preVerificationLogging(xcfa, mcm, parseContext, config, logger, uniqueLogger)

    val result = backend(xcfa, mcm, parseContext, config, logger, uniqueLogger)

    postVerificationLogging(result, mcm, parseContext, config, logger, uniqueLogger)

    return result
}


private fun propagateInputOptions(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger) {
    config.inputConfig.property = determineProperty(config, logger)
    LbePass.level = config.frontendConfig.lbeLevel
    if (config.backendConfig.backend == Backend.CEGAR) {
        val cegarConfig = config.backendConfig.specConfig
        cegarConfig as CegarConfig
        val random = Random(cegarConfig.porRandomSeed)
        XcfaSporLts.random = random
        XcfaDporLts.random = random
    }
    if (config.debugConfig.argToFile) {
        WebDebuggerLogger.enableWebDebuggerLogger()
        WebDebuggerLogger.getInstance().setTitle(config.inputConfig.input?.name)
    }

    LoopUnrollPass.UNROLL_LIMIT = config.frontendConfig.loopUnroll
    ARGWebDebugger.on = config.debugConfig.argdebug
}

private fun validateInputOptions(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger) {
    rule("NoCoiWhenDataRace") {
        config.backendConfig.backend == Backend.CEGAR &&
            (config.backendConfig.specConfig as? CegarConfig)?.coi != ConeOfInfluenceMode.NO_COI &&
            config.inputConfig.property == ErrorDetection.DATA_RACE
    }
    rule("NoAaporWhenDataRace") {
        (config.backendConfig.specConfig as? CegarConfig)?.porLevel?.isAbstractionAware == true &&
            config.inputConfig.property == ErrorDetection.DATA_RACE
    }
    rule("DPORWithoutDFS") {
        (config.backendConfig.specConfig as? CegarConfig)?.porLevel?.isDynamic == true &&
            (config.backendConfig.specConfig as? CegarConfig)?.abstractorConfig?.search != Search.DFS
    }
    // TODO add more validation options
}

fun frontend(config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger): Triple {
    if (config.inputConfig.xcfaWCtx != null) {
        val xcfa = config.inputConfig.xcfaWCtx!!.first
        ConeOfInfluence = if (config.inputConfig.xcfaWCtx!!.third.multiThreading) {
            XcfaCoiMultiThread(xcfa)
        } else {
            XcfaCoiSingleThread(xcfa)
        }
        return config.inputConfig.xcfaWCtx!!
    }

    val stopwatch = Stopwatch.createStarted()

    val input = config.inputConfig.input!!
    logger.write(Logger.Level.INFO, "Parsing the input $input as ${config.frontendConfig.inputType}\n")

    val parseContext = ParseContext()

    if (config.frontendConfig.inputType == InputType.C) {
        val cConfig = config.frontendConfig.specConfig
        cConfig as CFrontendConfig
        parseContext.arithmetic = cConfig.arithmetic
    }

    val xcfa = getXcfa(config, parseContext, logger, uniqueLogger)
    val mcm = if (config.inputConfig.catFile != null) {
        CatDslManager.createMCM(config.inputConfig.catFile!!)
    } else {
        emptySet()
    }

    ConeOfInfluence = if (parseContext.multiThreading) XcfaCoiMultiThread(xcfa) else XcfaCoiSingleThread(xcfa)

    logger.write(Logger.Level.INFO, "Frontend finished: ${xcfa.name}  (in ${
        stopwatch.elapsed(TimeUnit.MILLISECONDS)
    } ms)\n")

    logger.write(Logger.Level.RESULT, "ParsingResult Success\n")

    return Triple(xcfa, mcm, parseContext)
}

private fun backend(xcfa: XCFA, mcm: MCM, parseContext: ParseContext, config: XcfaConfig<*, *>,
    logger: Logger,
    uniqueLogger: Logger): SafetyResult<*, *> =
    if (config.backendConfig.backend == Backend.NONE) {
        SafetyResult.unknown()
    } else {
        if (xcfa.procedures.all { it.errorLoc.isEmpty && config.inputConfig.property == ErrorDetection.ERROR_LOCATION }) {
            SafetyResult.safe()
        } else {
            val stopwatch = Stopwatch.createStarted()

            logger.write(Logger.Level.INFO,
                "Starting verification of ${if (xcfa.name == "") "UnnamedXcfa" else xcfa.name} using ${config.backendConfig.backend}\n")

            val checker = getChecker(xcfa, mcm, config, parseContext, logger, uniqueLogger)
            val result = exitOnError(config.debugConfig.stacktrace, config.debugConfig.debug) {
                checker.check()
            }

            logger.write(Logger.Level.INFO, "Backend finished (in ${
                stopwatch.elapsed(TimeUnit.MILLISECONDS)
            } ms)\n")

            logger.write(Logger.Level.RESULT, result.toString() + "\n")
            result
        }
    }

private fun preVerificationLogging(xcfa: XCFA, mcm: MCM, parseContext: ParseContext, config: XcfaConfig<*, *>,
    logger: Logger,
    uniqueLogger: Logger) {
    try {
        val resultFolder = config.outputConfig.resultFolder
        resultFolder.mkdirs()

        logger.write(Logger.Level.INFO,
            "Writing pre-verification artifacts to directory ${resultFolder.absolutePath}\n")

        if (!config.outputConfig.xcfaOutputConfig.disable) {
            val xcfaDotFile = File(resultFolder, "xcfa.dot")
            xcfaDotFile.writeText(xcfa.toDot())

            val xcfaJsonFile = File(resultFolder, "xcfa.json")
            val uglyJson = getGson(xcfa).toJson(xcfa)
            val create = GsonBuilder().setPrettyPrinting().create()
            xcfaJsonFile.writeText(create.toJson(JsonParser.parseString(uglyJson)))
        }

        if (!config.outputConfig.cOutputConfig.disable) {
            try {
                val xcfaCFile = File(resultFolder, "xcfa.c")
                xcfaCFile.writeText(xcfa.toC(parseContext, config.outputConfig.cOutputConfig.useArr,
                    config.outputConfig.cOutputConfig.useExArr, config.outputConfig.cOutputConfig.useRange))
            } catch (e: Throwable) {
                logger.write(Logger.Level.VERBOSE, "Could not emit C file\n")
            }
        }
    } catch (e: Throwable) {
        logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n")
    }
}

private fun postVerificationLogging(safetyResult: SafetyResult<*, *>, mcm: MCM,
    parseContext: ParseContext, config: XcfaConfig<*, *>, logger: Logger, uniqueLogger: Logger) {
    try {
        // we only want to log the files if the current configuration is not --in-process or portfolio
        if (config.backendConfig.inProcess || config.backendConfig.backend == Backend.PORTFOLIO) {
            return
        }

        val resultFolder = config.outputConfig.resultFolder
        resultFolder.mkdirs()

        logger.write(Logger.Level.INFO,
            "Writing post-verification artifacts to directory ${resultFolder.absolutePath}\n")

        if (!config.outputConfig.argConfig.disable && safetyResult.hasArg()) {
            val argFile = File(resultFolder, "arg-${safetyResult.isSafe}.dot")
            val g: Graph = ArgVisualizer.getDefault().visualize(safetyResult.arg)
            argFile.writeText(GraphvizWriter.getInstance().writeString(g))
        }

        if (!config.outputConfig.witnessConfig.disable) {
            if (safetyResult.isUnsafe && safetyResult.asUnsafe().trace != null) {
                val concrTrace: Trace, XcfaAction> = XcfaTraceConcretizer.concretize(
                    safetyResult.asUnsafe().trace as Trace, XcfaAction>?,
                    getSolver(config.outputConfig.witnessConfig.concretizerSolver,
                        config.outputConfig.witnessConfig.validateConcretizerSolver))

                val traceFile = File(resultFolder, "trace.dot")
                val traceG: Graph = TraceVisualizer.getDefault().visualize(concrTrace)
                traceFile.writeText(GraphvizWriter.getInstance().writeString(traceG))
            }
            val witnessFile = File(resultFolder, "witness.graphml")
            XcfaWitnessWriter().writeWitness(safetyResult, config.inputConfig.input!!,
                getSolver(config.outputConfig.witnessConfig.concretizerSolver,
                    config.outputConfig.witnessConfig.validateConcretizerSolver), parseContext, witnessFile)
        }
    } catch (e: Throwable) {
        logger.write(Logger.Level.INFO, "Could not output files: ${e.stackTraceToString()}\n")
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy