From a890ebb913134bc3d7705ca8f4821ff0c0fc1c73 Mon Sep 17 00:00:00 2001 From: Alistair Michael Date: Tue, 10 Dec 2024 14:21:16 +1000 Subject: [PATCH] disable mra analyses when regions disabled, add timing to dsa --- .../scala/analysis/GlobalRegionAnalysis.scala | 4 +- src/main/scala/util/RunUtils.scala | 78 +++++++---- src/test/scala/PointsToTest.scala | 2 +- src/test/scala/TimeStaticAnalysis.scala | 125 ++++++++++-------- 4 files changed, 121 insertions(+), 88 deletions(-) diff --git a/src/main/scala/analysis/GlobalRegionAnalysis.scala b/src/main/scala/analysis/GlobalRegionAnalysis.scala index 2157b4d80..0e8ddfa53 100644 --- a/src/main/scala/analysis/GlobalRegionAnalysis.scala +++ b/src/main/scala/analysis/GlobalRegionAnalysis.scala @@ -72,7 +72,7 @@ trait GlobalRegionAnalysis(val program: Program, Set(dataPoolMaster(eval.get.value, subAccess)) } else { exp match { - case literal: BitVecLiteral => tryCoerceIntoData(literal, n, subAccess) + // case literal: BitVecLiteral => tryCoerceIntoData(literal, n, subAccess) case Extract(_, _, body) => tryCoerceIntoData(body, n, subAccess) case Repeat(_, body) => tryCoerceIntoData(body, n, subAccess) case ZeroExtend(_, body) => tryCoerceIntoData(body, n, subAccess) @@ -193,4 +193,4 @@ class GlobalRegionAnalysisSolver( ) extends GlobalRegionAnalysis(program, domain, constantProp, reachingDefs, mmm, vsaResult) with IRIntraproceduralForwardDependencies with Analysis[Map[CFGPosition, Set[DataRegion]]] - with SimpleWorklistFixpointSolver[CFGPosition, Set[DataRegion], PowersetLattice[DataRegion]] \ No newline at end of file + with SimpleWorklistFixpointSolver[CFGPosition, Set[DataRegion], PowersetLattice[DataRegion]] diff --git a/src/main/scala/util/RunUtils.scala b/src/main/scala/util/RunUtils.scala index 0e7f3b093..2ed939af5 100644 --- a/src/main/scala/util/RunUtils.scala +++ b/src/main/scala/util/RunUtils.scala @@ -291,7 +291,8 @@ object StaticAnalysis { ctx: IRContext, config: StaticAnalysisConfig, iteration: Int, - previousResults: Option[StaticAnalysisContext] = None + previousResults: Option[StaticAnalysisContext] = None, + timer: PerformanceTimer = PerformanceTimer("analyse") ): StaticAnalysisContext = { val IRProgram: Program = ctx.program val externalFunctions: Set[ExternalFunction] = ctx.externalFunctions @@ -316,17 +317,17 @@ object StaticAnalysis { Logger.debug("Subroutine Addresses:") Logger.debug(subroutines) - val timer = PerformanceTimer("Static Analysis") + timer.reset() // reducible loops val detector = LoopDetector(IRProgram) val foundLoops = detector.identify_loops() foundLoops.foreach(l => Logger.debug(s"Loop found: ${l.name}")) - timer.checkPoint("Loop Identification") + timer.checkPoint("Loop Identification " + iteration) val transformer = LoopTransform(foundLoops) val newLoops = transformer.llvm_transform() newLoops.foreach(l => Logger.debug(s"Loop found: ${l.name}")) - timer.checkPoint("Loop Transform") + timer.checkPoint("Loop Transform " + iteration) config.analysisDotPath.foreach { s => writeToFile(dotBlockGraph(IRProgram, IRProgram.map(b => b -> b.toString).toMap), s"${s}_graph-after-reduce-$iteration.dot") @@ -335,6 +336,7 @@ object StaticAnalysis { val mergedSubroutines = subroutines ++ externalAddresses + timer.reset() val domain = computeDomain(IntraProcIRCursor, IRProgram.procedures) val interDomain = computeDomain(InterProcIRCursor, IRProgram.procedures) @@ -374,7 +376,7 @@ object StaticAnalysis { val reachingDefinitionsAnalysisSolver = InterprocReachingDefinitionsAnalysisSolver(IRProgram) val reachingDefinitionsAnalysisResults = reachingDefinitionsAnalysisSolver.analyze() - timer.checkPoint("reaching definitions ReachingDefinitionsAnalysisSolver") + timer.checkPoint("reaching definitions ReachingDefinitionsAnalysisSolver " + iteration) config.analysisDotPath.foreach { s => writeToFile( @@ -392,13 +394,39 @@ object StaticAnalysis { Map[CFGPosition, LiftedElement[Map[Variable | MemoryRegion, Set[Value]]]]() } - Logger.debug("[!] Running GRA") - val graSolver = GlobalRegionAnalysisSolver(IRProgram, domain.toSet, interProcConstPropResult, reachingDefinitionsAnalysisResults, mmm, previousVSAResults) - val graResult = graSolver.analyze() + val mraResult = if (config.memoryRegions == MemoryRegionsMode.MRA) { + Logger.debug("[!] Running GRA") + val graSolver = GlobalRegionAnalysisSolver(IRProgram, domain.toSet, interProcConstPropResult, reachingDefinitionsAnalysisResults, mmm, previousVSAResults) + val graResult = graSolver.analyze() + + Logger.debug("[!] Running MRA") + val mraSolver = MemoryRegionAnalysisSolver(IRProgram, domain.toSet, globalAddresses, globalOffsets, mergedSubroutines, interProcConstPropResult, ANRResult, RNAResult, reachingDefinitionsAnalysisResults, graResult, mmm) + val mraResult = mraSolver.timeAnalyze(timer) + + + config.analysisDotPath.foreach { s => + writeToFile( + toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> mraResult(b).toString).toMap), + s"${s}_MRA$iteration.dot" + ) - Logger.debug("[!] Running MRA") - val mraSolver = MemoryRegionAnalysisSolver(IRProgram, domain.toSet, globalAddresses, globalOffsets, mergedSubroutines, interProcConstPropResult, ANRResult, RNAResult, reachingDefinitionsAnalysisResults, graResult, mmm) - val mraResult = mraSolver.timeAnalyze(timer) + writeToFile( + toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> graResult(b).toString).toMap), + s"${s}_GRA$iteration.dot" + ) + } + + + timer.reset() + Logger.debug("[!] Running MMM") + mmm.convertMemoryRegions(mraSolver.procedureToStackRegions, mraSolver.procedureToHeapRegions, mraResult, mraSolver.procedureToSharedRegions, graSolver.getDataMap, graResult) + mmm.logRegions() + timer.checkPoint("MMM " + iteration) + + mraResult + } else { + Map[CFGPosition, Set[StackRegion]]() + } config.analysisDotPath.foreach { s => writeToFile(dotCallGraph(IRProgram), s"${s}_callgraph$iteration.dot") @@ -412,22 +440,8 @@ object StaticAnalysis { s"${s}_new_ir_constprop$iteration.dot" ) - writeToFile( - toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> mraResult(b).toString).toMap), - s"${s}_MRA$iteration.dot" - ) - - writeToFile( - toDot(IRProgram, IRProgram.filter(_.isInstanceOf[Command]).map(b => b -> graResult(b).toString).toMap), - s"${s}_GRA$iteration.dot" - ) } - timer.reset() - Logger.debug("[!] Running MMM") - mmm.convertMemoryRegions(mraSolver.procedureToStackRegions, mraSolver.procedureToHeapRegions, mraResult, mraSolver.procedureToSharedRegions, graSolver.getDataMap, graResult) - mmm.logRegions() - timer.checkPoint("MMM") Logger.debug("[!] Running Steensgaard") val steensgaardSolver = InterprocSteensgaardAnalysis(interDomain.toSet, mmm, reachingDefinitionsAnalysisResults, previousVSAResults) @@ -585,9 +599,12 @@ object RunUtils { var iteration = 1 var modified: Boolean = true val analysisResult = mutable.ArrayBuffer[StaticAnalysisContext]() + + val timer = PerformanceTimer("Static Analysis") + while (modified || (analysisResult.size < 2 && config.memoryRegions == MemoryRegionsMode.MRA)) { Logger.debug("[!] Running Static Analysis") - val result = StaticAnalysis.analyse(ctx, config, iteration, analysisResult.lastOption) + val result = StaticAnalysis.analyse(ctx, config, iteration, analysisResult.lastOption, timer) analysisResult.append(result) Logger.debug("[!] Replacing Indirect Calls") @@ -607,7 +624,9 @@ object RunUtils { Logger.debug("[!] Generating Procedure Summaries") if (config.summariseProcedures) { + timer.reset() IRTransform.generateProcedureSummaries(ctx, ctx.program, result.intraProcConstProp, result.varDepsSummaries) + timer.checkPoint("Procedure Summaries") } if (modified) { @@ -623,24 +642,27 @@ object RunUtils { } Logger.debug("[!] Running Writes To") + timer.reset() val writesTo = WriteToAnalysis(ctx.program).analyze() - val reachingDefs = ReachingDefsAnalysis(ctx.program, writesTo).analyze() + val reachingDefs = ReachingDefsAnalysis(ctx.program, writesTo).timeAnalyze(timer) config.analysisDotPath.foreach { s => writeToFile(toDot(ctx.program), s"${s}_ct.dot") } Logger.debug("[!] Running Symbolic Access Analysis") val symResults: Map[CFGPosition, Map[SymbolicAddress, TwoElement]] = - SymbolicAddressAnalysis(ctx.program, analysisResult.last.interProcConstProp).analyze() + SymbolicAddressAnalysis(ctx.program, analysisResult.last.interProcConstProp).timeAnalyze(timer) config.analysisDotPath.foreach { s => val labels = symResults.map { (k, v) => k -> v.toString } writeToFile(toDot(ctx.program, labels), s"${s}_saa.dot") } + timer.reset() Logger.debug("[!] Running DSA Analysis") val symbolTableEntries: Set[SymbolTableEntry] = ctx.globals ++ ctx.funcEntries val dsa = DataStructureAnalysis(ctx.program, symResults, analysisResult.last.interProcConstProp, symbolTableEntries, ctx.globalOffsets, ctx.externalFunctions, reachingDefs, writesTo, analysisResult.last.paramResults) dsa.analyze() + timer.checkPoint("DataStructureAnalysis (DSA)") config.analysisDotPath.foreach { s => dsa.topDown(ctx.program.mainProcedure).toDot diff --git a/src/test/scala/PointsToTest.scala b/src/test/scala/PointsToTest.scala index 4b267d7e8..045a2fea2 100644 --- a/src/test/scala/PointsToTest.scala +++ b/src/test/scala/PointsToTest.scala @@ -275,4 +275,4 @@ class PointsToTest extends AnyFunSuite with OneInstancePerTest { // // runSteensgaardAnalysis(program, globals = globals, globalOffsets = globalOffsets) // } -} \ No newline at end of file +} diff --git a/src/test/scala/TimeStaticAnalysis.scala b/src/test/scala/TimeStaticAnalysis.scala index a4800cbb8..faf0f8242 100644 --- a/src/test/scala/TimeStaticAnalysis.scala +++ b/src/test/scala/TimeStaticAnalysis.scala @@ -13,7 +13,7 @@ import ExecutionContext.Implicits.global import ir.cilvisitor.* import scala.sys.process.* -val ANALYSE_TIMEOUT : Duration = 240000.millis +val ANALYSE_TIMEOUT: Duration = 60000.millis class TimeStaticAnalysis extends AnyFunSuite { @@ -41,12 +41,11 @@ class TimeStaticAnalysis extends AnyFunSuite { override def vjump(j: Jump): VisitAction[Jump] = { j match { case GoTo(targets) => goto_targets += targets.size - case _ => () + case _ => () } DoChildren() } - override def vblock(e: Block): VisitAction[Block] = { blocks += 1 DoChildren() @@ -59,37 +58,34 @@ class TimeStaticAnalysis extends AnyFunSuite { } object ProgStats { - def get(p:Program) : ProgStats = { + def get(p: Program): ProgStats = { val s = ProgStats() visit_prog(s, p) s } } - def examples() = { def getFiles(directoryName: String): Array[String] = { Option(File(directoryName).listFiles(_.isFile)) match { - case None => throw java.io.IOException(s"failed to read file '$directoryName'") + case None => throw java.io.IOException(s"failed to read file '$directoryName'") case Some(subdirs) => subdirs.map(_.getName) } } - val examples = getFiles("examples/csmith").filter(c => c.endsWith(".adt")).map(_.stripSuffix(".adt")) Logger.setLevel(LogLevel.INFO) - info("Config") def config(name: String) = ILLoadingConfig("examples/csmith/" + name + ".adt", "examples/csmith/" + name + ".relf") // use filesize to approximate size of program and sort tests by size - def map2nd[A,B,C](a: Iterable[(C, A)], f: A => B) = a.map((x: (C, A)) => (x._1, f(x._2))) + def map2nd[A, B, C](a: Iterable[(C, A)], f: A => B) = a.map((x: (C, A)) => (x._1, f(x._2))) val loads = examples.map(c => (c, config(c))).toList val loads2 = loads.map(x => { - val (c,cfg) = x + val (c, cfg) = x val chars = (File(cfg.inputFile)).length - (c,cfg,chars) + (c, cfg, chars) }) val loads3 = loads2.sortBy(_._3).map(c => (c._1, c._2)) Logger.info(loads2.map(c => s"${c._2.inputFile} ${c._3}").mkString("\n")) @@ -98,59 +94,70 @@ class TimeStaticAnalysis extends AnyFunSuite { Logger.warn(sorted.map(c => s"${c._1}, ${c._2}").mkString("\n")) - def doAnalysis(ex: String) : Future[(ProgStats, List[(String, String, Long)])] = Future { + def doAnalysis(ex: String): Future[(ProgStats, List[(String, String, Long)])] = Future { blocking { // load again and analyse val c = IRLoading.load(config(ex)) val ctx = IRTransform.doCleanup(c) val comp = ProgStats.get(c.program) - (comp, StaticAnalysis.analyse(ctx, StaticAnalysisConfig(), 0).timer.checkPoints()) + ( + comp, + RunUtils + .staticAnalysis( + StaticAnalysisConfig( + memoryRegions = MemoryRegionsMode.DSA + ), + ctx + ) + .timer + .checkPoints() + ) } } // give up after \timeout_thresh consecutive timeouts val timeout_thresh = 3 var timeouts = 0 - val result : List[(String, ProgStats, List[(String, String, Long)])] = sorted.map(v => { - val (testn,cfg) = v - try { - if (timeouts >= timeout_thresh) then { - None - } else { - Logger.warn(s"TESTING $testn") - val (comp,r) = Await.result(doAnalysis(testn), ANALYSE_TIMEOUT) - Logger.warn(comp) - Logger.warn("CHECKPOINTS:") - Logger.warn(r.map(c => s"${c._1},${c._2},${c._3}").mkString("\n")) - timeouts = 0 - Some((testn, comp, r)) - } - } catch { - case e : scala.concurrent.TimeoutException => { - timeouts += 1 - Logger.error(e) - None - } - case e => { - Logger.error(e, e.getStackTrace.take(10).mkString("\n")) - None + val result: List[(String, ProgStats, List[(String, String, Long)])] = sorted + .map(v => { + val (testn, cfg) = v + try { + if (timeouts >= timeout_thresh) then { + None + } else { + Logger.warn(s"TESTING $testn") + val (comp, r) = Await.result(doAnalysis(testn), ANALYSE_TIMEOUT) + Logger.warn(comp) + Logger.warn("CHECKPOINTS:\n" + r.map(c => s"${c._1},${c._2},${c._3}").mkString("\n")) + timeouts = 0 + Some((testn, comp, r)) + } + } catch { + case e: scala.concurrent.TimeoutException => { + timeouts += 1 + Logger.error(e) + None + } + case e => { + Logger.error(e, e.getStackTrace.take(10).mkString("\n")) + None + } } + }) + .collect { case Some(x) => + x } - }).collect { - case Some(x) => x - }.toList - // test filename , statements, blocks, procedures, checkpoint name, checkpoint loc , time delta - val times = result.flatMap(x => x._3.map((checkpoint : (String, String, Long)) => { - val comp = x._2 - (x._1, comp.statements,comp.blocks,comp.procedures, - checkpoint._1, - checkpoint._3 - ) - }) + .toList + // test filename , statements, blocks, procedures, checkpoint name, checkpoint loc , time delta + val times = result.flatMap(x => + x._3.map((checkpoint: (String, String, Long)) => { + val comp = x._2 + (x._1, comp.statements, comp.blocks, comp.procedures, checkpoint._1, checkpoint._3) + }) ) times } - + def log(path: String, text: String) = { val writer = BufferedWriter(FileWriter(path, false)) writer.write(text) @@ -165,21 +172,25 @@ class TimeStaticAnalysis extends AnyFunSuite { val grouped = r.groupBy(x => x._5).filter(i => !i._1.contains("Timeout") && !i._1.contains("Exception")) val outputPathPrefix = "src/test/analysisTiming" - var plotfile = s"set terminal 'svg' enhanced background rgb 'white'; set output '${outputPathPrefix}/analysisres.svg' ; set xlabel \"statement count (log scale)\" ; set ylabel \"analysis time (ms) (log scale)\"\nset logscale x 2\nset logscale y 2" - + val logscale = "" // \nset logscale x 2\nset logscale y 2 + var plotfile = + s"set terminal 'svg' enhanced background rgb 'white'; set output '${outputPathPrefix}/analysisres.svg' ; set xlabel \"statement count\" ; set ylabel \"analysis time (ms)\"$logscale" var plotcmds = List[String]() for ((n, vs) <- grouped) { - val table = (vs.sortBy(_._2).map(vs => { - val x = vs._2 // statements - val y = vs._6 // time - s"$x $y" - })).mkString("\n") + val table = (vs + .sortBy(_._2) + .map(vs => { + val x = vs._2 // statements + val y = vs._6 // time + s"$x $y" + })) + .mkString("\n") val pname = s"${outputPathPrefix}/${n}.dat" log(pname, table) - val plotcmd = s"'${pname}' title \"${n}\" with lines" - plotcmds = plotcmd::plotcmds + val plotcmd = s"'${pname}' title \"${n}\" with lines" + plotcmds = plotcmd :: plotcmds } val pl = s"plot ${plotcmds.mkString(", ")}" val gp = plotfile + "\n" + pl