12
12
// ===----------------------------------------------------------------------===//
13
13
14
14
#include " clang/StaticAnalyzer/Core/BugReporter/Z3CrosscheckVisitor.h"
15
+ #include " clang/StaticAnalyzer/Core/AnalyzerOptions.h"
15
16
#include " clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
16
17
#include " clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
17
18
#include " llvm/ADT/Statistic.h"
18
19
#include " llvm/Support/SMTAPI.h"
20
+ #include " llvm/Support/Timer.h"
19
21
20
22
#define DEBUG_TYPE " Z3CrosscheckOracle"
21
23
22
24
STATISTIC (NumZ3QueriesDone, " Number of Z3 queries done" );
23
25
STATISTIC (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" );
24
31
25
32
STATISTIC (NumTimesZ3QueryAcceptsReport,
26
33
" Number of Z3 queries accepting a report" );
27
34
STATISTIC (NumTimesZ3QueryRejectReport,
28
35
" Number of Z3 queries rejecting a report" );
36
+ STATISTIC (NumTimesZ3QueryRejectEQClass,
37
+ " Number of times rejecting an report equivalenece class" );
29
38
30
39
using namespace clang ;
31
40
using namespace ento ;
32
41
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) {}
35
46
36
47
void Z3CrosscheckVisitor::finalizeVisitor (BugReporterContext &BRC,
37
48
const ExplodedNode *EndPathNode,
@@ -41,8 +52,12 @@ void Z3CrosscheckVisitor::finalizeVisitor(BugReporterContext &BRC,
41
52
42
53
// Create a refutation manager
43
54
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
46
61
47
62
ASTContext &Ctx = BRC.getASTContext ();
48
63
@@ -63,8 +78,15 @@ void Z3CrosscheckVisitor::finalizeVisitor(BugReporterContext &BRC,
63
78
}
64
79
65
80
// And check for satisfiability
81
+ llvm::TimeRecord Start = llvm::TimeRecord::getCurrentTime (/* Start=*/ true );
66
82
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
+ };
68
90
}
69
91
70
92
void Z3CrosscheckVisitor::addConstraints (
@@ -101,18 +123,38 @@ void Z3CrosscheckVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
101
123
Z3CrosscheckOracle::Z3Decision Z3CrosscheckOracle::interpretQueryResult (
102
124
const Z3CrosscheckVisitor::Z3Result &Query) {
103
125
++NumZ3QueriesDone;
126
+ AccumulatedZ3QueryTimeInEqClass += Query.Z3QueryTimeMilliseconds ;
104
127
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;
108
130
return AcceptReport;
109
131
}
110
132
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;
114
154
}
115
155
156
+ // If no cutoff heuristics trigger, and the report is "unsat" or "undef",
157
+ // then reject the report.
116
158
++NumTimesZ3QueryRejectReport;
117
- return RejectReport; // unsat
159
+ return RejectReport;
118
160
}
0 commit comments