1212// ===----------------------------------------------------------------------===//
1313
1414#include " clang/StaticAnalyzer/Core/BugReporter/Z3CrosscheckVisitor.h"
15+ #include " clang/StaticAnalyzer/Core/AnalyzerOptions.h"
1516#include " clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
1617#include " clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
1718#include " llvm/ADT/Statistic.h"
1819#include " llvm/Support/SMTAPI.h"
20+ #include " llvm/Support/Timer.h"
1921
2022#define DEBUG_TYPE " Z3CrosscheckOracle"
2123
2224STATISTIC (NumZ3QueriesDone, " Number of Z3 queries done" );
2325STATISTIC (NumTimesZ3TimedOut, " Number of times Z3 query timed out" );
26+ STATISTIC (NumTimesZ3ExhaustedRLimit,
27+ " Number of times Z3 query exhausted the rlimit" );
28+ STATISTIC (NumTimesZ3SpendsTooMuchTimeOnASingleEQClass,
29+ " Number of times report equivalenece class was cut because it spent "
30+ " too much time in Z3" );
2431
2532STATISTIC (NumTimesZ3QueryAcceptsReport,
2633 " Number of Z3 queries accepting a report" );
2734STATISTIC (NumTimesZ3QueryRejectReport,
2835 " Number of Z3 queries rejecting a report" );
36+ STATISTIC (NumTimesZ3QueryRejectEQClass,
37+ " Number of times rejecting an report equivalenece class" );
2938
3039using namespace clang ;
3140using namespace ento ;
3241
33- Z3CrosscheckVisitor::Z3CrosscheckVisitor (Z3CrosscheckVisitor::Z3Result &Result)
34- : Constraints(ConstraintMap::Factory().getEmptyMap()), Result(Result) {}
42+ Z3CrosscheckVisitor::Z3CrosscheckVisitor (Z3CrosscheckVisitor::Z3Result &Result,
43+ const AnalyzerOptions &Opts)
44+ : Constraints(ConstraintMap::Factory().getEmptyMap()), Result(Result),
45+ Opts(Opts) {}
3546
3647void Z3CrosscheckVisitor::finalizeVisitor (BugReporterContext &BRC,
3748 const ExplodedNode *EndPathNode,
@@ -41,8 +52,12 @@ void Z3CrosscheckVisitor::finalizeVisitor(BugReporterContext &BRC,
4152
4253 // Create a refutation manager
4354 llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver ();
44- RefutationSolver->setBoolParam (" model" , true ); // Enable model finding
45- RefutationSolver->setUnsignedParam (" timeout" , 15000 ); // ms
55+ if (Opts.Z3CrosscheckRLimitThreshold )
56+ RefutationSolver->setUnsignedParam (" rlimit" ,
57+ Opts.Z3CrosscheckRLimitThreshold );
58+ if (Opts.Z3CrosscheckTimeoutThreshold )
59+ RefutationSolver->setUnsignedParam (" timeout" ,
60+ Opts.Z3CrosscheckTimeoutThreshold ); // ms
4661
4762 ASTContext &Ctx = BRC.getASTContext ();
4863
@@ -63,8 +78,15 @@ void Z3CrosscheckVisitor::finalizeVisitor(BugReporterContext &BRC,
6378 }
6479
6580 // And check for satisfiability
81+ llvm::TimeRecord Start = llvm::TimeRecord::getCurrentTime (/* Start=*/ true );
6682 std::optional<bool > IsSAT = RefutationSolver->check ();
67- Result = Z3Result{IsSAT};
83+ llvm::TimeRecord Diff = llvm::TimeRecord::getCurrentTime (/* Start=*/ false );
84+ Diff -= Start;
85+ Result = Z3Result{
86+ IsSAT,
87+ static_cast <unsigned >(Diff.getWallTime () * 1000 ),
88+ RefutationSolver->getStatistics ()->getUnsigned (" rlimit count" ),
89+ };
6890}
6991
7092void Z3CrosscheckVisitor::addConstraints (
@@ -101,18 +123,38 @@ void Z3CrosscheckVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
101123Z3CrosscheckOracle::Z3Decision Z3CrosscheckOracle::interpretQueryResult (
102124 const Z3CrosscheckVisitor::Z3Result &Query) {
103125 ++NumZ3QueriesDone;
126+ AccumulatedZ3QueryTimeInEqClass += Query.Z3QueryTimeMilliseconds ;
104127
105- if (!Query.IsSAT .has_value ()) {
106- // For backward compatibility, let's accept the first timeout.
107- ++NumTimesZ3TimedOut;
128+ if (Query.IsSAT && Query.IsSAT .value ()) {
129+ ++NumTimesZ3QueryAcceptsReport;
108130 return AcceptReport;
109131 }
110132
111- if (Query.IsSAT .value ()) {
112- ++NumTimesZ3QueryAcceptsReport;
113- return AcceptReport; // sat
133+ // Suggest cutting the EQClass if certain heuristics trigger.
134+ if (Opts.Z3CrosscheckTimeoutThreshold &&
135+ Query.Z3QueryTimeMilliseconds >= Opts.Z3CrosscheckTimeoutThreshold ) {
136+ ++NumTimesZ3TimedOut;
137+ ++NumTimesZ3QueryRejectEQClass;
138+ return RejectEQClass;
139+ }
140+
141+ if (Opts.Z3CrosscheckRLimitThreshold &&
142+ Query.UsedRLimit >= Opts.Z3CrosscheckRLimitThreshold ) {
143+ ++NumTimesZ3ExhaustedRLimit;
144+ ++NumTimesZ3QueryRejectEQClass;
145+ return RejectEQClass;
146+ }
147+
148+ if (Opts.Z3CrosscheckEQClassTimeoutThreshold &&
149+ AccumulatedZ3QueryTimeInEqClass >
150+ Opts.Z3CrosscheckEQClassTimeoutThreshold ) {
151+ ++NumTimesZ3SpendsTooMuchTimeOnASingleEQClass;
152+ ++NumTimesZ3QueryRejectEQClass;
153+ return RejectEQClass;
114154 }
115155
156+ // If no cutoff heuristics trigger, and the report is "unsat" or "undef",
157+ // then reject the report.
116158 ++NumTimesZ3QueryRejectReport;
117- return RejectReport; // unsat
159+ return RejectReport;
118160}
0 commit comments