Skip to content
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

Periodically print Z3 statistics #2992

Merged
merged 13 commits into from
Sep 23, 2024
1 change: 1 addition & 0 deletions .unreleased/features/z3-stats.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Periodically print Z3 statistics when `--debug` is on (#2992)
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ class BoundedCheckerPassImpl @Inject() (

val smtProfile = options.common.smtprof
val smtRandomSeed = tuning.getOrElse("smt.randomSeed", "0").toInt
val solverConfig = SolverConfig(debug, smtProfile, smtRandomSeed, smtEncoding)
val smtStatsSec =
tuning.getOrElse("smt.statsSec", SolverConfig.default.z3StatsSec.toString).toInt
val solverConfig = SolverConfig(debug, smtProfile, smtRandomSeed, smtStatsSec, smtEncoding)

val result = options.checker.algo match {
case Algorithm.Incremental => runIncrementalChecker(params, input, tuning, solverConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sealed case class SolverConfig(
debug: Boolean,
profile: Boolean,
randomSeed: Int,
z3StatsSec: Int,
smtEncoding: SMTEncoding) {}

object SolverConfig {
Expand All @@ -30,5 +31,6 @@ object SolverConfig {
* Get the default configuration.
*/
val default: SolverConfig =
new SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = SMTEncoding.OOPSLA19)
new SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = SMTEncoding.OOPSLA19,
z3StatsSec = 60)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.typesafe.scalalogging.LazyLogging

import java.io.PrintWriter
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.locks.ReentrantLock
import scala.collection.mutable
import scala.collection.mutable.ListBuffer

Expand Down Expand Up @@ -120,17 +121,61 @@ class Z3SolverContext(val config: SolverConfig) extends SolverContext with LazyL
*/
private var maxCellIdPerContext = List(-1)

private trait ContextState
private case class Running() extends ContextState
private case class Disposed() extends ContextState

// the state of the context: Running or Disposed
private var state: ContextState = Running()

// the lock shared between the context and the statistics thread
private val statisticsLock: ReentrantLock = new ReentrantLock()
// start a new thread to collect statistics
private val statisticsThread = new Thread(() => {
while (state == Running()) {
// Sleep for a while.
// If we call printStatistics right away, we can easily run into a race condition with Z3 initializing.
// This produces a core dump.
Thread.sleep(config.z3StatsSec * 1000)
// make sure that the context is not being disposed right now. Otherwise, we can get a nice core dump.
statisticsLock.lock()
try {
if (state == Running()) {
printStatistics()
}
} finally {
statisticsLock.unlock()
}
}
})

if (config.debug && config.z3StatsSec > 0) {
statisticsThread.start()
}

/**
* Dispose whatever has to be disposed in the end.
*/
override def dispose(): Unit = {
logger.debug(s"Disposing Z3 solver context ${id}")
z3context.close()
closeLogs()
cellCache.clear()
inCache.clear()
funDecls.clear()
cellSorts.clear()
state = Disposed()
// Try to obtain the lock, to let the statistics thread finish its work.
// If it is stuck for some reason, continue after the timeout in any case.
statisticsLock.tryLock(2 * config.z3StatsSec, java.util.concurrent.TimeUnit.SECONDS)
try {
if (config.debug) {
printStatistics()
}
z3context.close()
closeLogs()
cellCache.clear()
inCache.clear()
funDecls.clear()
cellSorts.clear()
} finally {
// it's not that important at this point, but let's unlock it for the piece of mind
statisticsLock.unlock()
}
}

/**
Expand Down Expand Up @@ -1014,6 +1059,14 @@ class Z3SolverContext(val config: SolverConfig) extends SolverContext with LazyL
flushLogs()
throw e
}

private def printStatistics(): Unit = {
logger.info(s"Z3 statistics for context $id...")
val entries = z3solver.getStatistics.getEntries.map(stat => {
s"${stat.Key}=${stat.getValueString}"
})
logger.info(entries.mkString(",") + "\n")
}
}

object Z3SolverContext {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ trait CrossTestEncodings extends AnyFunSuite with Checkers {
val checkerParams = new ModelCheckerParams(checkerInput, 0)

val solverContext =
new Z3SolverContext(new SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = smtEncoding))
new Z3SolverContext(new SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = smtEncoding,
z3StatsSec = 0))
val rewriter: SymbStateRewriterImpl = smtEncoding match {
case SMTEncoding.OOPSLA19 => new SymbStateRewriterImpl(solverContext, renaming)
case SMTEncoding.Arrays => new SymbStateRewriterImplWithArrays(solverContext, renaming)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import org.scalatestplus.junit.JUnitRunner
class TestSeqModelCheckerWithArrays extends TestSeqModelCheckerTrait {
override protected def withFixture(test: OneArgTest): Outcome = {
val solver = RecordingSolverContext
.createZ3(None, SolverConfig(debug = false, profile = false, 0, smtEncoding = SMTEncoding.Arrays))
.createZ3(None, SolverConfig(debug = false, profile = false, 0, z3StatsSec = 0, smtEncoding = SMTEncoding.Arrays))
val rewriter = new SymbStateRewriterImpl(solver, new IncrementalRenaming(new IdleTracker), new ExprGradeStoreImpl)
test(rewriter)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import org.scalatestplus.junit.JUnitRunner
class TestSeqModelCheckerWithFunArrays extends TestSeqModelCheckerTrait {
override protected def withFixture(test: OneArgTest): Outcome = {
val solver = RecordingSolverContext
.createZ3(None, SolverConfig(debug = false, profile = false, 0, smtEncoding = SMTEncoding.FunArrays))
.createZ3(None,
SolverConfig(debug = false, profile = false, 0, z3StatsSec = 0, smtEncoding = SMTEncoding.FunArrays))
val rewriter = new SymbStateRewriterImpl(solver, new IncrementalRenaming(new IdleTracker), new ExprGradeStoreImpl)
test(rewriter)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import org.scalatestplus.junit.JUnitRunner
class TestSeqModelCheckerWithOOPSLA19 extends TestSeqModelCheckerTrait {
override protected def withFixture(test: OneArgTest): Outcome = {
val solver = RecordingSolverContext
.createZ3(None, SolverConfig(debug = false, profile = false, 0, smtEncoding = SMTEncoding.OOPSLA19))
.createZ3(None,
SolverConfig(debug = false, profile = false, 0, z3StatsSec = 0, smtEncoding = SMTEncoding.OOPSLA19))
val rewriter = new SymbStateRewriterImpl(solver, new IncrementalRenaming(new IdleTracker), new ExprGradeStoreImpl)
test(rewriter)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import org.scalatestplus.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class TestRecordingSolverContextWithArrays extends TestRecordingSolverContext {
override protected def withFixture(test: NoArgTest): Outcome = {
solverConfig = SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = SMTEncoding.Arrays)
solverConfig = SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.Arrays)
test()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import org.scalatestplus.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class TestRecordingSolverContextWithFunArrays extends TestRecordingSolverContext {
override protected def withFixture(test: NoArgTest): Outcome = {
solverConfig = SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = SMTEncoding.FunArrays)
solverConfig = SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.FunArrays)
test()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import org.scalatestplus.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class TestRecordingSolverContextWithOOPSLA19 extends TestRecordingSolverContext {
override protected def withFixture(test: NoArgTest): Outcome = {
solverConfig = SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = SMTEncoding.OOPSLA19)
solverConfig = SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.OOPSLA19)
test()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class TestTransitionExecutorWithIncrementalAndArrays
with TestFilteredTransitionExecutor[IncrementalExecutionContextSnapshot] {
override protected def withFixture(test: OneArgTest): Outcome = {
val solver =
new Z3SolverContext(SolverConfig(debug = false, profile = false, randomSeed = 0,
new Z3SolverContext(SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.Arrays))
withFixtureInContext(solver, new IncrementalExecutionContext(_), test)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class TestTransitionExecutorWithIncrementalAndFunArrays
extends TestTransitionExecutorImpl[IncrementalExecutionContextSnapshot]
with TestFilteredTransitionExecutor[IncrementalExecutionContextSnapshot] {
override protected def withFixture(test: OneArgTest): Outcome = {
val solver = new Z3SolverContext(SolverConfig(debug = false, profile = false, randomSeed = 0,
val solver = new Z3SolverContext(SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.FunArrays))
withFixtureInContext(solver, new IncrementalExecutionContext(_), test)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class TestTransitionExecutorWithIncrementalAndOOPSLA19
with TestFilteredTransitionExecutor[IncrementalExecutionContextSnapshot] {
override protected def withFixture(test: OneArgTest): Outcome = {
val solver =
new Z3SolverContext(SolverConfig(debug = false, profile = false, randomSeed = 0,
new Z3SolverContext(SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.OOPSLA19))
withFixtureInContext(solver, new IncrementalExecutionContext(_), test)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import org.scalatestplus.junit.JUnitRunner
class TestTransitionExecutorWithOfflineAndArrays extends TestTransitionExecutorImpl[OfflineExecutionContextSnapshot] {
override protected def withFixture(test: OneArgTest): Outcome = {
val solver = RecordingSolverContext
.createZ3(None, SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = SMTEncoding.Arrays))
.createZ3(None,
SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.Arrays))
withFixtureInContext(solver, new OfflineExecutionContext(_, new IncrementalRenaming(new IdleTracker)), test)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ class TestTransitionExecutorWithOfflineAndFunArrays
extends TestTransitionExecutorImpl[OfflineExecutionContextSnapshot] {
override protected def withFixture(test: OneArgTest): Outcome = {
val solver = RecordingSolverContext
.createZ3(None, SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = SMTEncoding.FunArrays))
.createZ3(None,
SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.FunArrays))
withFixtureInContext(solver, new OfflineExecutionContext(_, new IncrementalRenaming(new IdleTracker)), test)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import org.scalatestplus.junit.JUnitRunner
class TestTransitionExecutorWithOfflineAndOOPSLA19 extends TestTransitionExecutorImpl[OfflineExecutionContextSnapshot] {
override def withFixture(test: OneArgTest): Outcome = {
val solver = RecordingSolverContext
.createZ3(None, SolverConfig(debug = false, profile = false, randomSeed = 0, smtEncoding = SMTEncoding.OOPSLA19))
.createZ3(None,
SolverConfig(debug = false, profile = false, randomSeed = 0, z3StatsSec = 0,
smtEncoding = SMTEncoding.OOPSLA19))
withFixtureInContext(solver, new OfflineExecutionContext(_, new IncrementalRenaming(new IdleTracker)), test)
}
}
Loading