Reland "[analyzer] Harden safeguards for Z3 query times" by steakhal · Pull Request #97298 · llvm/llvm-project (original) (raw)

@llvm/pr-subscribers-clang-static-analyzer-1

@llvm/pr-subscribers-clang

Author: Balazs Benics (steakhal)

Changes

This is exactly as originally landed in #95129,
but now the minimal Z3 version was increased to meet this change in #96682.

https://discourse.llvm.org/t/bump-minimal-z3-requirements-from-4-7-1-to-4-8-9/79664/4


This patch is a functional change.
https://discourse.llvm.org/t/analyzer-rfc-taming-z3-query-times/79520

As a result of this patch, individual Z3 queries in refutation will be bound by 300ms. Every report equivalence class will be processed in at most 1 second.

The heuristic should have only really marginal observable impact - except for the cases when we had big report eqclasses with long-running (15s) Z3 queries, where previously CSA effectively halted. After this patch, CSA will tackle such extreme cases as well.

(cherry picked from commit eacc3b3)


Full diff: https://github.com/llvm/llvm-project/pull/97298.diff

6 Files Affected:

diff --git a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def index f008c9c581d95..29aa6a3b8a16e 100644 --- a/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def +++ b/clang/include/clang/StaticAnalyzer/Core/AnalyzerOptions.def @@ -184,6 +184,26 @@ ANALYZER_OPTION(bool, ShouldCrosscheckWithZ3, "crosscheck-with-z3", "constraint manager backend.", false)

+ANALYZER_OPTION(

diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/Z3CrosscheckVisitor.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/Z3CrosscheckVisitor.h index 9413fd739f607..439f37fa8604f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/Z3CrosscheckVisitor.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/Z3CrosscheckVisitor.h @@ -25,8 +25,11 @@ class Z3CrosscheckVisitor final : public BugReporterVisitor { public: struct Z3Result { std::optional IsSAT = std::nullopt;

};

@@ -44,21 +47,44 @@ class Z3CrosscheckVisitor final : public BugReporterVisitor { /// Holds the constraints in a given path. ConstraintMap Constraints; Z3Result &Result;

/// The oracle will decide if a report should be accepted or rejected based on -/// the results of the Z3 solver. +/// the results of the Z3 solver and the statistics of the queries of a report +/// equivalenece class. class Z3CrosscheckOracle { public:

};

} // namespace clang::ento diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp index c9a7fd0e035c2..e2002bfbe594a 100644 --- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -89,6 +89,9 @@ STATISTIC(MaxValidBugClassSize,

STATISTIC(NumTimesReportPassesZ3, "Number of reports passed Z3"); STATISTIC(NumTimesReportRefuted, "Number of reports refuted by Z3"); +STATISTIC(NumTimesReportEQClassAborted,

STATISTIC(NumTimesReportEQClassWasExhausted, "Number of times all reports of an equivalence class was refuted");

@@ -2840,6 +2843,7 @@ generateVisitorsDiagnostics(PathSensitiveBugReport *R, std::optional PathDiagnosticBuilder::findValidReport( ArrayRef<PathSensitiveBugReport *> &bugReports, PathSensitiveBugReporter &Reporter) {

@@ -2871,16 +2875,20 @@ std::optional PathDiagnosticBuilder::findValidReport( // visitor and check again R->clearVisitors(); Z3CrosscheckVisitor::Z3Result CrosscheckResult;

diff --git a/clang/lib/StaticAnalyzer/Core/Z3CrosscheckVisitor.cpp b/clang/lib/StaticAnalyzer/Core/Z3CrosscheckVisitor.cpp index a7db44ef8ea30..739db951b3e18 100644 --- a/clang/lib/StaticAnalyzer/Core/Z3CrosscheckVisitor.cpp +++ b/clang/lib/StaticAnalyzer/Core/Z3CrosscheckVisitor.cpp @@ -12,26 +12,37 @@ //===----------------------------------------------------------------------===//

#include "clang/StaticAnalyzer/Core/BugReporter/Z3CrosscheckVisitor.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h" #include "llvm/ADT/Statistic.h" #include "llvm/Support/SMTAPI.h" +#include "llvm/Support/Timer.h"

#define DEBUG_TYPE "Z3CrosscheckOracle"

STATISTIC(NumZ3QueriesDone, "Number of Z3 queries done"); STATISTIC(NumTimesZ3TimedOut, "Number of times Z3 query timed out"); +STATISTIC(NumTimesZ3ExhaustedRLimit,

+STATISTIC(NumTimesZ3SpendsTooMuchTimeOnASingleEQClass,

STATISTIC(NumTimesZ3QueryAcceptsReport, "Number of Z3 queries accepting a report"); STATISTIC(NumTimesZ3QueryRejectReport, "Number of Z3 queries rejecting a report"); +STATISTIC(NumTimesZ3QueryRejectEQClass,

using namespace clang; using namespace ento;

-Z3CrosscheckVisitor::Z3CrosscheckVisitor(Z3CrosscheckVisitor::Z3Result &Result)

+Z3CrosscheckVisitor::Z3CrosscheckVisitor(Z3CrosscheckVisitor::Z3Result &Result,

void Z3CrosscheckVisitor::finalizeVisitor(BugReporterContext &BRC, const ExplodedNode *EndPathNode, @@ -41,8 +52,12 @@ void Z3CrosscheckVisitor::finalizeVisitor(BugReporterContext &BRC,

// Create a refutation manager llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver();

@@ -63,8 +78,15 @@ void Z3CrosscheckVisitor::finalizeVisitor(BugReporterContext &BRC, }

// And check for satisfiability

void Z3CrosscheckVisitor::addConstraints( @@ -101,18 +123,38 @@ void Z3CrosscheckVisitor::Profile(llvm::FoldingSetNodeID &ID) const { Z3CrosscheckOracle::Z3Decision Z3CrosscheckOracle::interpretQueryResult( const Z3CrosscheckVisitor::Z3Result &Query) { ++NumZ3QueriesDone;

}

+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/Z3CrosscheckVisitor.h" #include "gtest/gtest.h"

@@ -17,43 +18,126 @@ using Z3Decision = Z3CrosscheckOracle::Z3Decision;

static constexpr Z3Decision AcceptReport = Z3Decision::AcceptReport; static constexpr Z3Decision RejectReport = Z3Decision::RejectReport; +static constexpr Z3Decision RejectEQClass = Z3Decision::RejectEQClass;

static constexpr std::optional SAT = true; static constexpr std::optional UNSAT = false; static constexpr std::optional UNDEF = std::nullopt;

+static unsigned operator""_ms(unsigned long long ms) { return ms; } +static unsigned operator""_step(unsigned long long rlimit) { return rlimit; } + +static const AnalyzerOptions DefaultOpts = [] {

-struct Z3CrosscheckOracleTest : public testing::Test {

+class Z3CrosscheckOracleTest : public testing::Test { +public:

} + +private:

TEST_F(Z3CrosscheckOracleTest, AcceptsFirstSAT) {

TEST_F(Z3CrosscheckOracleTest, AcceptsSAT) {

-TEST_F(Z3CrosscheckOracleTest, AcceptsFirstTimeout) {

-TEST_F(Z3CrosscheckOracleTest, AcceptsTimeout) {

TEST_F(Z3CrosscheckOracleTest, RejectsUNSATs) {

} // namespace